I have an Azure app configured for my frontend. From that, I am obtaining an access token from Azure for the Microsoft Graph API. This is my frontend auth config.
import { PublicClientApplication } from '@azure/msal-browser';
const msalConfig = {
auth: {
clientId: '#####',
authority:
'https://login.microsoftonline.com/#####',
redirectUri: '#####'
},
cache: {
cacheLocation: 'localStorage',
storeAuthStateInCookie: true
}
};
export const loginRequest = {
scopes: ['User.Read', 'User.ReadBasic.All']
};
const msalInstance = new PublicClientApplication(msalConfig);
This is how I obtain access token at the frontend.
import { msalInstance, loginRequest } from './auth.config';
const accounts = msalInstance.getAllAccounts();
if (accounts.length <= 0) return;
try {
const response = await msalInstance.acquireTokenSilent({
...loginRequest,
account: accounts[0]
});
// response.accesstoken is obtained
} catch (error) {
console.error(error);
}
When I am calling the backend API, I send this token as a bearer token in the request to backend and I need to verify it at the backend. This is where I am stuck. I tried but I cannot verify this token at the backend.
Since this access token is a jwt token I try to verify it in the follwing way.
import jwt, { JwtPayload } from 'jsonwebtoken';
import { IPublicKey } from './interfaces';
import jwkToPem from 'jwk-to-pem';
async function getKeysUri(): Promise<string> {
// Get jwks_uri from following meta data url.
// https://login.microsoftonline.com/{tenant_id}/.well-known/openid-configuration?appid=###
}
async function getKeyForKid(kid: string, jwksUri: string): Promise<IPublicKey> {
// Get keys from jwksUri
return keys.find((obj: IPublicKey) => obj.kid === kid);
}
export async function verifyAccessToken(accessToken: string): Promise<JwtPayload> {
try {
const decoded = jwt.decode(accessToken, { complete: true });
const kid = decoded.header.kid;
const jwksUri = await getKeysUri();
const key = await getKeyForKid(kid, jwksUri);
const pem = jwkToPem(key);
const payload = jwt.verify(accessToken, pem) as JwtPayload;
return payload;
} catch (err) {
console.error(err)
}
}
In this way, I can obtain the public key corresponding to the private key which was used to signing the token and I should be able to verify the token. But this fails by giving me an error message saying "Invalid signature". I cannot find why it is failing. (I already checed whether the public key I am obtaining using jwkToPem and it is correct). I need help to verfiy this token or find the error in above code snippet.
You should not be verifying tokens that are not meant for your API. Graph API also uses some different technique for their tokens, which you see here. That isn't a problem since only Graph API itself needs to be able to verify the token.
Your front-end should acquire an access token for your API. That one you can verify, and then use the on-behalf-of flow to exchange that for a Graph API token, which you can then use to call Graph API.