I can't use Azure AD provider in my Forge app

Hi all!

I am trying to authenticate to Azure AD from my Forge application and after declaring an auth provider I try to use the fetch api but the application does not work before reaching this step and gives me the following error:

UnhandledPromiseRejectionWarning: Error: Isolate was disposed during execution
at (<isolated-vm boundary) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch().

This is my manifest with the provider:

permissions:
  external:
    fetch:
      backend:
        - https://login.microsoftonline.com
        - https://graph.microsoft.com
providers:
  auth:
    - key: azure
      name: MSAzure
      scopes:
        - '.default'
      type: oauth2
      clientId: MY CLIENT ID
      remotes:
        - azure-login
        - azure-graph
      bearerMethod: authorization-header
      actions:
        authorization:
          remote: azure-login
          path: /tenantID/oauth2/v2.0/authorize
        exchange:
          remote: azure-login
          path: /tenantID/oauth2/v2.0/token
        retrieveProfile:
          remote: azure-graph
          path: /v1.0/me
          resolvers:
            id: sub
            displayName: email
remotes:
  - key: azure-login
    baseUrl: https://login.microsoftonline.com
  - key: azure-graph
    baseUrl: https://graph.microsoft.com

And this is my code:

const azure = api.asUser().withProvider('azure', 'azure-graph');

	try {
		if (!await azure.hasCredentials()) {
			await azure.requestCredentials()
		  }
		  const response = await azure.fetch('any link');
		  if (response.ok) {
			return response.json()
		  }
	} catch (error) {
		console.error('Error:', error);
	}

The code breaks in the hasCredentials() method call.

Thank you all for your time.

Hi @LuisMiguel ,

we have a quite similar approach which is working for our app since some time:

manifest.yml:

providers:
  auth:
    - key: ms365
      name: Microsoft
      scopes:
        - 'profile'
        - 'openid'
        - 'offline_access'
        - 'email'
        - 'User.Read'
        - <more scopes>
      type: oauth2
      clientId: <My clientID>
      remotes:
        - graph-api
      bearerMethod: authorization-header
      actions:
        authorization:
          remote: msal-oauth
          path: /organizations/oauth2/v2.0/authorize
        exchange:
          remote: msal-oauth
          path: /organizations/oauth2/v2.0/token
        retrieveProfile:
          remote: graph-api
          path: /oidc/userinfo
          resolvers:
            id: sub
            displayName: email
remotes:
  - key: msal-oauth
    baseUrl: https://login.microsoftonline.com
  - key: graph-api
    baseUrl: https://graph.microsoft.com
permissions:
  external:
    fetch:
      backend:
        - 'https://login.microsoftonline.com'
        - 'https://graph.microsoft.com'

Code usage:

private GraphClient = async () => {
    const graph = api.asUser().withProvider('ms365', 'graph-api');
    if (!await graph.hasCredentials()) {
        const result = await graph.requestCredentials();
    }
    return graph;
}

// sample usage
public getMyProfile = async (): Promise<User> => {
    const graph = await this.GraphClient();
    const response = await graph.fetch('/v1.0/me');

    if (response.ok) {
        const result = await response.json() as User;
        return result;
    }

    return null;
}

I can only see the following differences:

  1. We use another endpoint to retrieve the user info (see also here: Microsoft identity platform UserInfo endpoint - Microsoft identity platform | Microsoft Learn)
  2. we are not using the “.default” scope from the EntraID application but instead defining the Graph scopes (and some OpenID scopes) directly
  3. We use the global “organizations” target instead of a single tenantId (but that should not make a difference as long as you have set the correct tenantId)

I guess one of these points will be the reason as the code I provided is currently working for us.

2 Likes

Hi Thomas,

Thanks for your response, but that still does not work for me. I’m trying to authenticate using fetch but still doesn’t working.

Hii @ThomasDedek @LuisMiguel ,
Any new update on this , I am also trying to do same things. I am also getting error

The error I am getting below and I am using though resolvers. and calling through invoke in index.jsx.
index.jsx:155
Error invoking getToken: Error: There was an error invoking the function - Bad provider or missing config for provider ms365
at invoke (https://jira-frontend-bifrost.prod-east.frontend.public.atl-paas.net/assets/project-settings-page.ef231d21.js:68:13084)
at async https://jira-frontend-bifrost.prod-east.frontend.public.atl-paas.net/assets/project-settings-page.ef231d21.js:68:86694

Error: There was an error invoking the function - Bad provider or missing config for provider ms365
at fe.error (https://forge.cdn.prod.atlassian-dev.net/global-bridge.js:2:153277)
at Object. (https://forge.cdn.prod.atlassian-dev.net/global-bridge.js:2:161050)