OAI Token Authorization Malfunction

I have uploaded an Angular application onto my main tenant using Cumulocity’s Web SDK, version 1019.

The web application needs to make calls to a REST services application hosted on the same tenant. After the user authenticates themselves the application loads, however, a second sign-in popup appears asking the user to re-authenticate themselves again.

I believe this is because the Angular and REST services apps are accessed using separate pathways?

Regardless, I would like the user to bypass this second round of authentication and tried using an OAI cookie token from the c8y/client package as I do not want to have to store my Cumulocity credentials in my source code to retrieve user data.

I created the following service to inject into my application’s core component.

import { Injectable } from '@angular/core';
import { CookieAuth, Client } from '@c8y/client';

@Injectable({
    providedIn: 'root'
})
export class AuthenticationService {
    private tenantUrl: string = '/api';
    private client: Client;

    public constructor() {
        console.log('AuthenticationService constructor called');
        this.client = new Client(new CookieAuth(), this.tenantUrl);
        console.log('Client initialized:', this.client);
    }

    public async getUserInfo() {
        console.log('getUserInfo called');
        try {
            const responsePromise = await this.client.user.current();
            console.log('Login with cookie successful');
            console.log(responsePromise); 
            return responsePromise;
        } catch (error) {
            console.log('Login failed: ', error)
            return null;
        }
    }
}
export class CoreComponent {

  public constructor(private authService: AuthenticationService) {
    console.log('CoreComponent initialized');
    this.authService.getUserInfo().then(userInfo => {
      console.log('User Info:', userInfo);
    }).catch(error => {
      console.error('Error fetching user info:', error);
    });
  }
}

Note: I am storing the domain URL in a proxy.conf.js file in my application’s root folder.

{
  "/api": {
    "target": "https://[domain].cumulocity.com",
    "secure": false,
    "changeOrigin": true,
    "pathRewrite": {
      "^/api": ""
    }
  }
}

When I deploy the application to my tenant, however, the authentication fails with the following information displayed in the browser console.

How can I resolve this error?

Hi @luclib,

this looks basically the same as you already had it here:

Regards,
Tristan

Hi Tristan,

I have incorporated the FetchClient into my service for making API calls to the backend as opposed the HttpClient provided by Angular.

import { FetchClient } from "@c8y/client";
import { Injectable } from "@angular/core";
import { environment } from 'src/environments/environment' 

@Injectable({
    providedIn: 'root'
})
export class DataService {

    constructor(private fetchClient: FetchClient) {}

    public async getDevices() {
        let url: string = environment.getDevicesApi.url;
        let responsePromise: any;
        try {
            responsePromise = await this.fetchClient.fetch(url);
        }
        catch (error: any) {
            console.error('Error:', error);
        }
        return responsePromise;
    }
}

While running it on my localhost:4200, the application is supposed to use the URL http://localhost:80/api/ in its calls. Which it does, expect that it is also prepending my own localhost’s URL beforehand.

:4200/http://localhost:80/api/WorkOrder/GetDevices 
        
        Failed to load resource: the server responded with a status of 404 (Not Found)

You are only supposed to provide the path to the fetch method. So e.g. /api/WorkOrder/GetDevices.
I’m still not sure what you are trying to achieve with this /api/ path. And there should also not be a need for a custom proxy configuration.

Tristian,

I have since deleted the custom proxy configuration and I have been able deploy an application that seems to work properly when I append a prefix that specifies the path of our REST services in our tenant, but there is still some debugging I need to do on my local machine.

I am using a docker container that is hosted on port 80 to run the same REST application on my local computer while my Angular/C8Y container runs on port 4200.

The problem is that when I run the fetch methods locally it is making calls to localhost:4200 and not localhost:80.

When I correct the port number:

Is there a way I can configure the fetch method to send requests to localhost:80?

Okay, it would have helped if you would have mentioned earlier that you would like to develop against a locally running service.
You should try to avoid reconfiguring the behavior in the UI just for your local development, so the behavior of the fetch method should be kept as it is.

Instead you could e.g. pick the default proxy config we are using from here and adjust it to your needs.

As you probably would want to have a similar behavior as in the cloud, you would want to forward requests going to /service/<context-path>/ to your locally running container. A proxy config (proxy.conf.mjs) could e.g. look like this (replace <context-path-of-your-microservice> with the context path your using for your microservice when hosting it in cumulocity):

// to run with proxy along with local API server, use command `ng serve -u <tenant url> --proxy-config ./proxy.conf.mjs`
import * as process from 'process';

function getTargetUrl(args) {
  const urlArgumentIndex = args.findIndex((a) => a === '-u' || a === '--url');
  if (urlArgumentIndex !== -1 && urlArgumentIndex < args.length - 1) {
    return args[urlArgumentIndex + 1];
  }
  return null;
}

const target = getTargetUrl(process.argv);

if (target) {
  console.log(`Target url for proxy: ${target}`);
} else {
  console.error('Target url not found or --url (alias -u) argument is missing');
  process.exit(1);
}

export default [
  {
    path: ['/service/<context-path-of-your-microservice>/**'],
    target: 'http://localhost:80',
    pathRewrite: { '^/service/<context-path-of-your-microservice>': '' }, // remove '/service/<context-path-of-your-microservice>' part from call to test against local server
    changeOrigin: true,
    secure: false,
    ws: true,
  },
  {
    target,
    ws: true,
    secure: false,
    changeOrigin: true,
    timeout: 120000,
    proxyTimeout: 120000,
    path: ['**'],
    /**
     * The following two lines are used to allow to use cookie-auth
     * also in un-secure environments like local-development. It removes
     * the secure flag and rewrites the domain for the cookie.
     *
     * You must never use this setting in production!
     */
    cookieDomainRewrite: 'localhost',
    /**
     * Excluding request for live reload and HMR from angular
     */
    context: function (path) {
      return !path.match(/\/ng-cli-ws/);
    },
    onProxyRes: (proxyResponse) => {
      'use strict';

      if (proxyResponse.headers['set-cookie']) {
        const cookies = proxyResponse.headers['set-cookie'].map((cookie) =>
          cookie.replace(/;\s{0,}secure/gi, ''),
        );
        proxyResponse.headers['set-cookie'] = cookies;
      }
    },
  },
];

Regards,
Tristan

Hi Tristan,

Thanks for the input.

I have tried to implement the method you provided me with, but my local application continues to make calls to port 4200…
image

Here is the default object with modifications you suggested; I have kept the rest of the file unchanged:

export default [
  {
    path: ['/service/kratos-service/**'],
    target: 'http://localhost:80/',
    pathRewrite: { '^/service/kratos-service': '' },
    changeOrigin: true,
    secure: false,
    ws: true
  }

I have tried tweaking the target property by removing the forward slash /, but that did not work.

In addition, I have modified the serve command as follows:

ng serve janus -u http://[domain].cumulocity.com --proxy-config ./proxy.conf.mjs

Similarly, tried modifying the path for my proxy-config to proxy.conf.mjs, but with no success.

Hi @luclib,

The browser is still supposed to call localhost:4200 and shouldn’t be interacting with localhost:80 directly. Your proxy configuration is then taking care of forwarding the requests going to localhost:4200/service/kratos-service/ to your service running on localhost:80.

You shouldn’t be calling a /api endpoint as these are not existing and we also did not define a proxy configuration for that. When you use the fetch client you would want to perform the request to /service/kratos-service/WorkOrder/GetWorkOrders or /service/kratos-service/api/WorkOrder/GetWorkOrders depending on how the endpoint is defined within your microservice.

Regards,
Tristan

Hi Tristan,

I have modified the URL for my development environment as you suggested.

export const environment = {
    production: false,

    getWorkOrdersApi: {
        url: '/service/kratos-service/WorkOrder/GetWorkOrders?PageNumber=1&PageSize=5'
    }
}

However, it is now returning a 504 error.

It seems you are running both servers in different containers. The container running the proxy via angular cli won’t be able to reach the other container using localhost as a host.

You will need to use a different hostname/IP to reach the other container and might need to configure your docker network accordingly. It might be sufficient to adjust the target of the proxy.conf.mjs to e.g. http://<container_name>:80 so in your case (if the name did not change) http://friendly_borg:80.