AzureAD / microsoft-authentication-library-for-js

Microsoft Authentication Library (MSAL) for JS
http://aka.ms/aadv2
MIT License
3.67k stars 2.65k forks source link

Does msal-node support Client credentials flow for Azure AD B2C #4939

Closed kengaderdus closed 2 years ago

kengaderdus commented 2 years ago

Core Library

MSAL Node (@azure/msal-node)

Core Library Version

1.10.0

Wrapper Library

Not Applicable

Wrapper Library Version

n/a

Description

Recently, Client credentials flow for Azure AD B2C moved to public preview. Works okay when you make an HTTP call, but it seems not to work when using msal-node.

Error Message

ClientAuthError: network_error: Network request failed. Please check network trace to determine root cause. | Fetch client threw: Error: HTTP status code 400 | Attempted to reach: https://tenant-name.b2clogin.com/tenant-name.onmicrosoft.com/policy-name/oauth2/v2.0/token

Msal Logs

[Mon, 27 Jun 2022 10:45:33 GMT] : @azure/msal-node@1.10.0 : Info - acquireTokenByClientCredential called [Mon, 27 Jun 2022 10:45:33 GMT] : @azure/msal-node@1.10.0 : Verbose - initializeRequestScopes called Client credentials app listening accessToken = 3000 [Mon, 27 Jun 2022 10:45:33 GMT] : [40fa6b2a-7e02-4aa5-a576-cc3b9306f8af] : @azure/msal-node@1.10.0 : Verbose - buildOauthClientConfiguration called [Mon, 27 Jun 2022 10:45:33 GMT] : [40fa6b2a-7e02-4aa5-a576-cc3b9306f8af] : @azure/msal-node@1.10.0 : Verbose - building oauth client configuration with the authority: https://tenant-name.b2clogin.com/tenant-name.onmicrosoft.com/policy-name [Mon, 27 Jun 2022 10:45:33 GMT] : [40fa6b2a-7e02-4aa5-a576-cc3b9306f8af] : @azure/msal-node@1.10.0 : Verbose - createAuthority called[Mon, 27 Jun 2022 10:45:35 GMT] : [40fa6b2a-7e02-4aa5-a576-cc3b9306f8af] : @azure/msal-node@1.10.0 : Verbose - Client credential client created

MSAL Configuration

const msalConfig = {
    auth: {
        clientId: APP_CLIENT_ID,
        authority: POLICY_AUTHORITY in the form of https://tenant-name.b2clogin.com/tenant-name.onmicrosoft.com/policy-name,
        clientSecret: APP_CLIENT_SECRET,
        knownAuthorities: [tenant-name.b2clogin.com]
    },
    system: {
        loggerOptions: {
            loggerCallback(loglevel, message, containsPii) {
                console.log(message);
            },
            piiLoggingEnabled: false,
            logLevel: msal.LogLevel.Verbose,
        }
    }
   };

Relevant Code Snippets

const msalConfig = {
    auth: {
        clientId: APP_CLIENT_ID,
        authority: POLICY_AUTHORITY in the form of https://tenant-name.b2clogin.com/tenant-name.onmicrosoft.com/policy-name,
        clientSecret: APP_CLIENT_SECRET,
        knownAuthorities: [tenant-name.b2clogin.com]
    },
    system: {
        loggerOptions: {
            loggerCallback(loglevel, message, containsPii) {
                console.log(message);
            },
            piiLoggingEnabled: false,
            logLevel: msal.LogLevel.Verbose,
        }
    }
   }; 

// With client credentials flows permissions need to be granted in the portal by a tenant administrator. 
// The scope is always in the format '<resource>/.default'.
const tokenRequest = {
    //Use this scope to receive an access token you use to access resources protected by Azure AD B2C itself. 
    scopes: ['https://tenant-name.onmicrosoft.com/client-credentials-api/.default']
};
//Create msal application object
const cca = new msal.ConfidentialClientApplication(msalConfig);

cca.acquireTokenByClientCredential(tokenRequest).then((authResponse)=>{
    console.log('accessToke: ' + authResponse.accessToken)

}).catch((error)=>{
    console.log("\nErrorAtLogin: \n" + error);
});

Reproduction Steps

  1. Follow the steps in Register Azure AD B2C web API to register web api
  2. Follow the steps in Register a web app to register the web app
  3. Use the code snippet in Relevant Code Snippets section above to request an access token. Make sure you replace the client ID, client secret, Azure AD B2C policy and Azure AD B2C tenant name with your own values.

Expected Behavior

I should have received an access token for my web app. The web app can then access resources (in this case the web API) as needed.

Identity Provider

Azure B2C Basic Policy

Browsers Affected (Select all that apply)

None (Server)

Regression

It didn't work

Source

Internal (Microsoft)

derisen commented 2 years ago

@kengaderdus I can reproduce this. The actual error response from the service:

'{"error":"invalid_scope","error_description":"AADB2C99068: Scope parameter (https://fabrikamb2c.onmicrosoft.com/3aab53e3-c083-49e8-a54e-2444ad952113/.default openid profile offline_access) must be formatted like https://<application-uri>/.default\r\nCorrelation ID: 07dc20f2-b4f0-421d-ad35-6a7d97526814\r\nTimestamp: 2022-06-29 02:29:07Z\r\n"}'

MSAL.js by default adds openid profile offline_access scopes to every token request. It seems B2C does not accept these when doing client credentials grant (which is correct protocol-wise, since these are delegated permissions), while AAD apparently ignores them, hence why this issue never came up before. @nickgmicrosoft is there a way we can confirm this? @sameerag fyi

arielrahmane commented 2 years ago

Hi, I'm having the same issue and couldn't find a solution... Maybe I'm missing something?

Error

network_error: Network request failed. Please check network trace to determine root cause. | Fetch client threw: Error: HTTP status code 400 | Attempted to reach: https://login.microsoftonline.com/common/oauth2/v2.0/token

Complete log


edubi-service_1  | [Nest] 43  - 07/06/2022, 1:40:01 PM   ERROR [ExceptionsHandler] network_error: Network request failed. Please check network trace to determine root cause. | Fetch client threw: Error: HTTP status code 400 | Attempted to reach: https://login.microsoftonline.com/common/oauth2/v2.0/token
edubi-service_1  | ClientAuthError: network_error: Network request failed. Please check network trace to determine root cause. | Fetch client threw: Error: HTTP status code 400 | Attempted to reach: https://login.microsoftonline.com/common/oauth2/v2.0/token
edubi-service_1  |     at ClientAuthError.AuthError [as constructor] (/edubi/service/node_modules/@azure/msal-common/src/error/AuthError.ts:45:9)
edubi-service_1  |     at new ClientAuthError (/edubi/service/node_modules/@azure/msal-common/src/error/ClientAuthError.ts:205:9)
edubi-service_1  |     at Function.ClientAuthError.createNetworkError (/edubi/service/node_modules/@azure/msal-common/src/error/ClientAuthError.ts:259:16)
edubi-service_1  |     at NetworkManager.<anonymous> (/edubi/service/node_modules/@azure/msal-common/src/network/NetworkManager.ts:44:39)
edubi-service_1  |     at step (/edubi/service/node_modules/@azure/msal-common/dist/index.cjs.js:79:23)
edubi-service_1  |     at Object.throw (/edubi/service/node_modules/@azure/msal-common/dist/index.cjs.js:60:53)
edubi-service_1  |     at rejected (/edubi/service/node_modules/@azure/msal-common/dist/index.cjs.js:51:65)
edubi-service_1  |     at processTicksAndRejections (node:internal/process/task_queues:96:5)

Configs


export const msalAuthOptions: NodeAuthOptions = {
  clientId: process.env.MSAL_CLIENT_ID,
  authority: process.env.MSAL_AUTHORITY, // https://login.microsoftonline.com/common
  clientSecret: process.env.MSAL_CLIENT_SECRET,
  knownAuthorities: [process.env.MSAL_KNOWN_AUTHORITY], // login.microsoftonline.com
};

export const msalSystemOptions: NodeSystemOptions = {
  loggerOptions: {
    piiLoggingEnabled: true,
    logLevel: LogLevel.Trace,
  },
};

export const clientCredentialsRequest: ClientCredentialRequest = {
  scopes: [process.env.MSAL_PBI_SCOPE], // https://analysis.windows.net/powerbi/api
  skipCache: true,
};

The function where the error happens


  async getClientCredentialsToken(): Promise<AuthenticationResult | null> {
    const confidentialClientApplication = new ConfidentialClientApplication({
      auth: msalAuthOptions,
      system: msalSystemOptions,
    });

    const authResult =
      await confidentialClientApplication.acquireTokenByClientCredential(
        clientCredentialsRequest,
      );

    this.logger.log(authResult);

    return authResult;
  }

Dependencies

"@azure/msal-node": "^1.11.0",
"@nestjs/common": "^8.0.0",
kengaderdus commented 2 years ago

@jo-arroyo

FYI: Azure AD B2C now directly supports Client credentials flow (see article here: Set up OAuth 2.0 client credentials flow in Azure Active Directory B2C), so we expect msal should support this flow for Azure AD B2C.

derisen commented 2 years ago

@EmLauber can this be tracked?

ghost commented 2 years ago

@kengaderdus This issue has been automatically marked as stale because it is marked as requiring author feedback but has not had any activity for 5 days. If your issue has been resolved please let us know by closing the issue. If your issue has not been resolved please leave a comment to keep this open. It will be closed automatically in 7 days if it remains stale.

kengaderdus commented 2 years ago

@derisen

I see you're taking care of this issue.

derisen commented 2 years ago

@kengaderdus the preference now is for the B2C service to align their behavior with AAD, so waiting to hear back from them at the moment. If not, there's a PR to fix the issue on msal-node side, but that's less desirable as it would necessitate a package update for the consumers of msal-node.

kengaderdus commented 2 years ago

@derisen Thanks for the update.

derisen commented 2 years ago

@Robbie-Microsoft transferring this to you

kengaderdus commented 2 years ago

@Robbie-Microsoft

Any updates?

Robbie-Microsoft commented 2 years ago

We believe that @derisen's fix in this PR will fix the issue.

derisen commented 2 years ago

@kengaderdus the fix for this has been merged and will be available in the next release of msal-node. Closing as no further action at the moment. cc @Robbie-Microsoft

ghost commented 2 years ago

:tada:This issue was addressed in #4999, which has now been successfully released as @azure/msal-node@v1.14.1.:tada:

Handy links: