1

I'm attempting to secure an API with Azure AD and call it from a React SPA and I'm running into a 401 Not Authorized error. I'm not too sure what I'm doing wrong.

inside appsettings.json

"AzureAD": {
    "Instance": "https://login.microsoft.com",
    "Domain": "<MyAdDomain>",
    "TenantId": "<MyTenantId>",
    "ClientId": "<MyApiAppId>",
    "Scope": "<MyApiScope>"
  }

then StartUp.cs

            var tenantId = $"{Configuration["AzureAD:TenantId"]}";
            var authority = $"{Configuration["AzureAD:Instance"]}/{tenantId}";

            var audience = new List<string>
            {
                $"{Configuration["AzureAD:ClientId"]}",
                $"{Configuration["AzureAD:Scope"]}"
            };

            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddJwtBearer(options =>
                {
                    options.Authority = authority;
                    options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                    {
                        ValidAudiences = audience,
                        ValidIssuers = new List<string>
                        {
                            $"https://sts.windows.net/{tenantId}",
                            $"https://sts.windows.net/{tenantId}/v2.0",
                        }
                    };
                });

Meanwhile, inside my React app, I'm securing it using MsalProvider with this configuration:

authConfig.ts

export const msalConfig: Configuration = {
  auth: {
    clientId: "<MyClientApiId>",
    authority:
      "https://login.microsoftonline.com/<MyTenantId>",
    redirectUri: "/",
  },
  cache: {
    cacheLocation: "sessionStorage",
    storeAuthStateInCookie: false,
  },
// other code

App.tsx

const msalInstance = new PublicClientApplication(msalConfig);

function App() {
  return (
    <MsalProvider instance={msalInstance}>
      <BrowserRouter>
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/dealer/:accountNumber" element={<DealerContainer />} />
        </Routes>
      </BrowserRouter>
    </MsalProvider>
  );

and then inside HomePage.tsx:

  const getCompanies = useCallback(async () => {
    if (isAuthenticated) {
      const silentRequest: SsoSilentRequest = {
        scopes: ["<MyApiScope>"],
        loginHint: instance.getActiveAccount()?.username,
      };

      const token = await instance.ssoSilent(silentRequest);
      const companies = await getCompaniesAsync(token);

      setCompaniesList(companies);
      setFullCompaniesList(companies);
    }
  }, [instance, isAuthenticated]);

Any insight as to what I'm doing wrong here? Thanks!

Craig
  • 11
  • 4
  • Could you pls refer to [this answer](https://stackoverflow.com/questions/68763252/azure-authentication-audience-validation-failed/68799358#68799358)? I provide a sample there. – Tiny Wang Sep 16 '21 at 06:27
  • Thank you! That gave me some insight into what was wrong! – Craig Sep 16 '21 at 19:23

1 Answers1

0

With some insight from Tiny Wang's comment, I ended up wading through some other documentation and doing some Postman testing

inside HomePage.tsx:

 const getCompanies = useCallback(async () => {
    if (isAuthenticated) {
      const account = instance.getAllAccounts()[0];

      const silentRequest = {
        scopes: ["api://<MyApiAppId>/access_user_data"],
        account: account,
      };

      const token: AuthenticationResult = await instance.acquireTokenSilent(
        silentRequest
      );
      const companies = await getCompaniesAsync(token.accessToken);
      // rest of the code here
  "AzureAd": {
    "Instance": "https://login.microsoft.com",
    "Domain": "<MyDomain>",
    "TenantId": "<MyTenantId>",
    "ClientId": "api://<MyApiAppId>",
    "Audience": "<MyFrontEndAppId>",
    "Scope": "api://<MyApiAppId>/access_user_data"
  },

For some reason, ClientId was requesting the URI and not just the ID.

Craig
  • 11
  • 4
  • Hi @Craig I think im doing something similiar. I have a front end react js and I want to make a custom express server in the back end. Do I need to register the backend with azure, expose an api and then provide that api url in the scope of the react js ? Thanks! – aero8991 Jun 02 '23 at 21:58