Search code examples
reactjsasp.net-coremicrosoft-graph-apimsal-react

How to use Microsoft Authentification in ASP.NET Core with React


I am currently trying to get Microsoft authentification running on ASP.NET Core. Basically I want to login on the frontend(React) and use my custom API.
A call might look like

https://example.com/API/returnHelloWorld

Now in React Im using @azure/msal-browser for the login.
By using a button I am calling instance.loginRedirect(loginRequest) which works and I am able login.

After that I call

const request = {
    scopes: ["User.Read"],
    account: accounts[0]
};

instance.acquireTokenSilent(request).then(response => {
    console.log(response.accessToken);
}

where I get the token inside the console, but when checking it on jwt.io, I get an invalid signature error.

My authConfig.js:

import { LogLevel } from "@azure/msal-browser";
export const msalConfig = {
    auth: {
        clientId: "c0000d4e-0000-0000-0000-425000032721",
        authority: "https://login.microsoftonline.com/eace0000-0000-0000-0000-6dced0000bc/oauth2/v2.0/token",
        redirectUri: "https://example.com/signin-oidc",
    },
    cache: {
        cacheLocation: "sessionStorage",
        storeAuthStateInCookie: false,
    },
    system: {
        loggerOptions: {
            loggerCallback: (level, message, containsPii) => {
                if (containsPii) {
                    return;
                }
                switch (level) {
                    case LogLevel.Error:
                        console.error(message);
                        return;
                    case LogLevel.Info:
                        console.info(message);
                        return;
                    case LogLevel.Verbose:
                        console.debug(message);
                        return;
                    case LogLevel.Warning:
                        console.warn(message);
                        return;
                    default:
                        return;
                }
            }
        }
    }
};

export const loginRequest = {
    scopes: ["api://c0000d4e-0000-0000-0000-425000032721/.default"]
};

export const graphConfig = {
    graphMeEndpoint: "https://graph.microsoft.com/v1.0/me"
};

Now I gave up on generating a valid token using react for now, mabye someone has an Idea what I have to do to fix this.

To ASP.NET Core
Inside the Program.cs I added

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddMicrosoftIdentityWebApi(options =>
            {
                builder.Configuration.Bind("AzureAd", options);
                options.Events = new JwtBearerEvents();
            }, options => { builder.Configuration.Bind("AzureAd", options); });

to use AzureAD / Microsoft for the authentication.

Inside the appsettings.json

  "AzureAd": {
    "Instance": "https://example.com/",
    "Domain": "local.example.com",
    "TenantId": "eace0000-0000-0000-0000-6dced0000bc",
    "ClientId": "c0000d4e-0000-0000-0000-425000032721",
    "Scopes": ".default", // also tried api://clientid/.default
    "CallbackPath": "/signin-oidc"
  }

Because I wanted to know if atleast ASP.NET Core does what it was supposed to do I generated a new Key using:

curl -X POST https://login.microsoftonline.com/eace0000-0000-0000-0000-6dced0000bc/oauth2/v2.0/token -H "Content-Type: application/x-www-form-urlencoded" -d "client_id=c0000d4e-0000-0000-0000-425000032721" -d scope=api://c0000d4e-0000-0000-0000-425000032721/.default" -d "client_secret=jtS0000SC000lorem0000000DjULkm00001aS9" -d "grant_type=client_credentials"

This gave me a key with a valid signature, but when calling (after adding the [Authorize] to the controller)

https://example.com/API/returnHelloWorld

with the token , I just get "Bearer error="invalid_token"".

Does anyone know how to get this running, or has an example which I may look at?


Solution

  • First of all the "invalid signature" error when validating the token on jwt.io is expected when you don't provide the public keys used by Microsoft identity platform. For validation in your server-side code, you should use libraries that can automatically retrieve Microsoft's public keys. You don't need to manually verify these tokens on jwt.io unless you also provide the public keys from Microsoft's well-known URL (https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration).

    Your authority URL in msalConfig seems incorrect. The authority URL typically ends with your tenant ID or 'organizations', not /oauth2/v2.0/token.

    authority: "https://login.microsoftonline.com/eace0000-0000-0000-0000-6dced0000bc",
    

    appsettings.json:

    "AzureAd": {
      "Instance": "https://login.microsoftonline.com/",
      "Domain": "local.example.com",
      "TenantId": "eace0000-0000-0000-0000-6dced0000bc",
      "ClientId": "c0000d4e-0000-0000-0000-425000032721",
      "Audience": "api://c0000d4e-0000-0000-0000-425000032721",
      "CallbackPath": "/signin-oidc"
    }
    

    Your loginRequest scopes are trying to use a .default scope on your client application ID (api://c0000d4e-.../.default). This scope is usually correct for acquiring tokens meant for APIs, make sure that the API is configured to this scope.

    For more detail you could refer to this document:

    https://learn.microsoft.com/en-us/entra/identity-platform/scenario-protected-web-api-app-configuration?tabs=aspnetcore

    https://learn.microsoft.com/en-us/samples/azure-samples/ms-identity-ciam-javascript-tutorial/ms-identity-ciam-javascript-tutorial-1-call-api-react/

    Azure authentication Audience validation failed