1

Intro: I'm trying to configure react authentication with Azure B2C, based on https://github.com/Azure-Samples/ms-identity-javascript-react-tutorial/tree/main/3-Authorization-II/2-call-api-b2c/SPA/src

Goal: I want the users to be able to login and sign-up on the app (react) using their personal emails (as consumers) Gmail, Yahoo, etc.

Issue: However, when a user sign-up, I cannot read the user account details. The user signs up successfully following the user flows, I can even edit the user profile using the user flows. However, I cannot fetch user account info.

Image showing the react app, in the console, it outputs graph.js:18 GET https://graph.microsoft.com/beta/me 401 (Unauthorized)

authConfig.js

 /*
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License.
 */

import { LogLevel } from "@azure/msal-browser";
import { b2cPolicies } from "./policies";

/**
 * Configuration object to be passed to MSAL instance on creation.
 * For a full list of MSAL.js configuration parameters, visit:
 * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/configuration.md
 */
export const msalConfig = {
  auth: {
    clientId: "3f7f0b29-dcd8-45ab-a478-0893bfb74e8e", // This is the ONLY mandatory field; everything else is optional.
    authority: b2cPolicies.authorities.signUpSignIn.authority, // Choose sign-up/sign-in user-flow as your default.
    knownAuthorities: [b2cPolicies.authorityDomain], // You must identify your tenant's domain as a known authority.
    redirectUri: "http://localhost:3000", // You must register this URI on Azure Portal/App Registration. Defaults to "window.location.href".
  },
  cache: {
    cacheLocation: "sessionStorage", // This configures where your cache will be stored
    storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
  },
  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;
        }
      },
    },
  },
};

/**
 * Scopes you add here will be prompted for user consent during sign-in.
 * By default, MSAL.js will add OIDC scopes (openid, profile, email) to any login request.
 * For more information about OIDC scopes, visit:
 * https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
 */
export const loginRequest = {
  scopes: [
    "openid",
    "email",
    "profile",
    "https://sebcdemo.onmicrosoft.com/api/mail.send",
  ],
};

/**
 * Add here the scopes to request when obtaining an access token for MS Graph API. For more information, see:
 * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md
 */
export const graphConfig = {
  graphMeEndpoint: "https://graph.microsoft.com/beta/me",
};

policies.js

/**
 * Enter here the user flows and custom policies for your B2C application
 * To learn more about user flows, visit: https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-flow-overview
 * To learn more about custom policies, visit: https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-overview
 */
export const b2cPolicies = {
  names: {
    signUpSignIn: "B2C_1_b2c-signup-signin",
    editProfile: "B2C_1_edit_profile_v2",
  },
  authorities: {
    signUpSignIn: {
      authority:
        "https://sebcdemo.b2clogin.com/sebcdemo.onmicrosoft.com/B2C_1_b2c-signup-signin",
    },
    editProfile: {
      authority:
        "https://sebcdemo.b2clogin.com/sebcdemo.onmicrosoft.com/B2C_1_edit_profile_v2",
    },
  },
  authorityDomain: "sebcdemo.b2clogin.com",
};

graph.js

import { graphConfig } from "./authConfig";

/**
 * Attaches a given access token to a MS Graph API call. Returns information about the user
 * @param accessToken 
 */
export async function callMsGraph(accessToken) {
    const headers = new Headers();
    const bearer = `Bearer ${accessToken}`;

    headers.append("Authorization", bearer);

    const options = {
        method: "GET",
        headers: headers
    };

    return fetch(graphConfig.graphMeEndpoint, options)
        .then(response => response.json())
        .catch(error => console.log(error));
}

App.js

import React, { useState } from "react";
import { AuthenticatedTemplate, UnauthenticatedTemplate, useMsal } from "@azure/msal-react";
import { loginRequest } from "./authConfig";
import { PageLayout } from "./components/PageLayout";
import { ProfileData } from "./components/ProfileData";
import { callMsGraph } from "./graph";
import Button from "react-bootstrap/Button";
import "./styles/App.css";

/**
 * Renders information about the signed-in user or a button to retrieve data about the user
 */
const ProfileContent = () => {
    const { instance, accounts } = useMsal();
    const [graphData, setGraphData] = useState(null);

    function RequestProfileData() {
        
        // Silently acquires an access token which is then attached to a request for MS Graph data
        instance.acquireTokenSilent({
            ...loginRequest,
            account: accounts[0]
        }).then((response) => {
            callMsGraph(response.accessToken).then(response => {
                console.log(
                    "Response: ", response
                );
                console.log("Account: ",accounts[0]);
                setGraphData(response)
            });
        });
    }

    return (
        <>
            <h5 className="card-title">Welcome {accounts[0].name}</h5>
            {graphData ? 
                <ProfileData graphData={graphData} />
                :
                <Button variant="secondary" onClick={RequestProfileData}>Request Profile Information</Button>
            }
        </>
    );
};

/**
 * If a user is authenticated the ProfileContent component above is rendered. Otherwise a message indicating a user is not authenticated is rendered.
 */
const MainContent = () => {    
    return (
        <div className="App">
            <AuthenticatedTemplate>
                <ProfileContent />
            </AuthenticatedTemplate>

            <UnauthenticatedTemplate>
                <h5 className="card-title">Please sign-in to see your profile information.</h5>
            </UnauthenticatedTemplate>
        </div>
    );
};

export default function App() {
    return (
        <PageLayout>
            <MainContent />
        </PageLayout>
    );
}

The App.js outputs:

console.log("Response: ", response);

Response:
error: code: "InvalidAuthenticationToken" innerError: {date: "2021-08-04T06:19:07", request-id: "8e0ef8bf-f7db-4220-9a66-689ca3b06683", client-request-id: "8e0ef8bf-f7db-4220-9a66-689ca3b06683"} message: "CompactToken parsing failed with error code: 80049217"

console.log("Account: ",accounts[0]);

Account: environment: "seb2cdemo.b2clogin.com" homeAccountId: "5ddb5502-c1f1-44d1-a33d-4aefa924350f-b2c_1_b2c-signup-signin.e2ba8437-a274-4842-b694-4764f10077ce" idTokenClaims: {exp: 1628061537, nbf: 1628057937, ver: "1.0", iss: "https://seb2cdemo.b2clogin.com/e2ba8437-a274-4842-b694-4764f10077ce/v2.0/", sub: "5ddb5502-c1f1-44d1-a33d-4aefa924350f", …} localAccountId: "5ddb5502-c1f1-44d1-a33d-4aefa924350f" name: undefined tenantId: "" username: ""

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
  • 1
    Found the solution. The error was due to the fact that I did not select the application claims checkboxes. – D.K. Ananias Aug 11 '21 at 14:58
  • 1
    I had a similar issue using MSAL with Azure b2c with Angular. Users could sign up (using an email address) but I couldn't see that property in my application using authService.instance.getAllAccounts()[0]. It turns out (thanks to user8549687's comment above) I had to go into my Azure b2c directory, select the sign up / sign in (susi) user flow, then application claims, and enable email addresses. Now this information is returned in the authService – Rory Sep 06 '22 at 02:02

0 Answers0