Reputation: 6146
We decided we want one of our endpoints to be secured with a JWT mechanism. The authentication is done via Microsoft Identity Platform with Microsoft Entra ID. (It's the first time I integrate such a system and have no prior experience regarding JWT in general and in particular none with the whole MS Azure Authentication world.)
Now I have successfully integrated MSAL in my front-end and completed the sign-in process with a test user and am being correctly redirected to where I started the sign-in journey.
But I can't find the obtained Entra ID token in localStorage
(where it should be according to my msalConfig
).
I suspect that is because the token is stored under a different domain and thus I can't read it. But I need to read it so I can send it as a bearer token to the endpoint as was intended.
Yes, I read the documentation (but not ALL of it), but am quite woozy from all the new terminology. So maybe this has to do with the audience
that keeps popping up in the docs?
Between claims
, scopes
, audience
, authority
, tenants
, clientId
, etc. I am a bit lost I have to admit.
This is my msalConfig
and my loginRequest
export const msalConfig = {
auth: {
clientId: globals.meta.config.MSAL_CLIENT_ID,
authority: 'https://galactus.some-example-authority.com/',
redirectUri: 'https://example.org/pages/galactus-login',
postLogoutRedirectUri: '/',
navigateToLoginRequestUrl: false,
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: false,
},
}
export const loginRequest = {
scopes: [],
}
Upvotes: 0
Views: 69
Reputation: 22352
Initially, I registered one application and below API permissions
in it:
In Authentication tab, I added redirect URI as http://localhost:3000 in Single-page application platform:
Enabled public-client flows option like this:
In my case, I used below code files and got tokens with signed-in user details calling MS Graph API successfully like this:
authConfig.js:
// src/authConfig.js
export const msalConfig = {
auth: {
clientId: "appId",
authority: "https://login.microsoftonline.com/tenantId",
redirectUri: "http://localhost:3000", // Redirect URI registered in Azure AD
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: false,
},
};
export const loginRequest = {
scopes: ["User.Read", "openid"], // Scopes for MS Graph API or custom API
};
App.js:
// src/App.js
import React, { useEffect, useState } from "react";
import { msalConfig, loginRequest } from "./authConfig";
import { PublicClientApplication } from "@azure/msal-browser";
import axios from "axios";
const msalInstance = new PublicClientApplication(msalConfig);
const App = () => {
const [accessToken, setAccessToken] = useState("");
const [idToken, setIdToken] = useState("");
const [userProfile, setUserProfile] = useState(null);
const [userName, setUserName] = useState("");
// Initialize MSAL on app load
useEffect(() => {
const initializeMSAL = async () => {
try {
await msalInstance.initialize();
console.log("MSAL initialized successfully");
} catch (error) {
console.error("MSAL initialization failed: ", error);
}
};
initializeMSAL();
}, []);
const loginAndGetToken = async () => {
try {
const loginResponse = await msalInstance.loginPopup(loginRequest);
console.log("Login successful!", loginResponse);
// Set the active account after successful login
msalInstance.setActiveAccount(loginResponse.account);
// Access tokens and ID token
const newAccessToken = loginResponse.accessToken;
const newIdToken = loginResponse.idToken; // Get the ID token
setAccessToken(newAccessToken);
setIdToken(newIdToken);
setUserName(loginResponse.account.name);
// Call the Graph API to fetch user profile info
await callGraphAPI(newAccessToken);
} catch (error) {
console.error("Login failed: ", error);
alert(`Login failed: ${error.message}`);
}
};
const callGraphAPI = async (token) => {
try {
const response = await axios.get("https://graph.microsoft.com/v1.0/me", {
headers: {
Authorization: `Bearer ${token}`,
},
});
console.log("Graph API Response:", response.data);
setUserProfile(response.data);
} catch (error) {
console.error("Graph API call failed:", error);
}
};
const logout = () => {
msalInstance.logoutPopup();
};
return (
<div>
<h1>MSAL Login</h1>
{!accessToken ? (
<button onClick={loginAndGetToken}>Login</button>
) : (
<div>
<h2>Logged in as: {userName}</h2> {/* Display user's name */}
<button onClick={logout}>Logout</button>
</div>
)}
{accessToken && (
<div>
<h2>Access Token:</h2>
<p>{accessToken}</p>
</div>
)}
{idToken && (
<div>
<h2>ID Token:</h2>
<p>{idToken}</p>
</div>
)}
{userProfile && (
<div>
<h2>User Profile:</h2>
<p><strong>Name:</strong> {userProfile.displayName}</p>
<p><strong>Email:</strong> {userProfile.mail || userProfile.userPrincipalName}</p>
<p><strong>Job Title:</strong> {userProfile.jobTitle}</p>
</div>
)}
</div>
);
};
export default App;
Output:
You can also decode above access token by pasting it in jwt.ms website and check claims like aud
, scp
to validate it:
If you exposed any API with custom scopes, make sure to use scopes parameter value as api://appId/scopename
while getting token.
Upvotes: 1