I'm trying to setup authentication for my NodeJS/VueJS app using Azure AD B2B using the passport-azure-ad strategy. I use Authorization Code Flow to get access_token and id_token. The problem comes when I pass the access_token to the passport-azure-ad strategy. I get an error saying that the token has an invalid signature (jwt.io shows the same). If I use the id_token then everything works fine. However I don't want to use the id_token because I want to implement silent refresh and I can only do that with the access_token.
Here is my setup:
let url = 'https://login.microsoftonline.com/'+TENANT_ID+'/oauth2/v2.0/authorize?'+
'client_id='+CLIENT_ID+
'&response_type=code'+
'&scope=openid offline_access'+
'&redirect_uri='+window.location.origin+
'&state='+encodeURIComponent(generateRandomString())+
'&nonce='+nonce;
let data = {
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: 'authorization_code',
redirect_uri: window.location.origin,
code: code
}
let response = await axios.post(`/${process.env.TENANT}/oauth2/v2.0/token`, data)
Here is the configuration of my BearerStrategy:
let options = {
identityMetadata: "https://login.microsoftonline.com/"+envVars.TENANT+"/v2.0/.well-known/openid-configuration",
clientID: envVars.CLIENT_ID,
passReqToCallback: false,
audience: envVars.CLIENT_ID,
validateIssuer: true,
issuer: 'https://sts.windows.net/'+envVars.TENANT+'/',
loggingLevel: 'error',
}
var bearerStrategy = new OIDCBearerStrategy(options,
function(token, done) {
console.log(token)
if (!token.oid) {
return done(new Error('oid is not found in token'));
}
else {
return done(null, token.unique_name, token);
}
}
);
passport.use(bearerStrategy);
I think the problem is that for some reason I'm getting a v1 token back instead of v2. I tried setting "accessTokenAcceptedVersion": 2
in the manifest of the app registration but it didn't work.
What am I doing wrong?
Here is the decoded token with some information hidden:
HEADER:ALGORITHM & TOKEN TYPE
{
"typ": "JWT",
"nonce": "1BaxlG05CpKz_zLXaJjZ0-nyYqLpMkktzvL9WiOEL74",
"alg": "RS256",
"x5t": "kg2LYs2T0CTjIfj4rt6JIynen38",
"kid": "kg2LYs2T0CTjIfj4rt6JIynen38"
}
PAYLOAD:DATA
{
"aud": "00000003-0000-0000-c000-000000000000",
"iss": "https://sts.windows.net/[tenant_id]/",
"iat": 1601568985,
"nbf": 1601568985,
"exp": 1601572885,
"acct": 0,
"acr": "1",
"aio": "ASQA2/8RAAAAq7Z5bMg6AIJJ25iq7RI34DjbdRD9C6vnTlL3ZeilylQ=",
"amr": [
"pwd"
],
"app_displayname": "[app_displayname]",
"appid": "[appid]",
"appidacr": "1",
"family_name": "[family_name]",
"given_name": "[given_name]",
"idtyp": "user",
"ipaddr": "[ipaddr]",
"name": "[name]",
"oid": "[oid]",
"platf": "5",
"puid": "10033FFFAFA83EAE",
"rh": "0.ATAAdcJAG5J6AkqwgY-6rF6WssXSwci23txClbKekzYO8PwwAE8.",
"scp": "User.Read profile openid email",
"sub": "44PNYdl91SJrrrj4F25e1hXFyj3uTt-1ko6bUyT_Gq8",
"tenant_region_scope": "EU",
"tid": "[tenant_id]",
"unique_name": "[email_address]",
"upn": "[email_address]",
"uti": "DNAFrnHtrU6wprPVywDNAA",
"ver": "1.0",
"wids": [
"b79fbf4d-3ef9-4689-8143-76b194e85509"
],
"xms_st": {
"sub": "xpu87pTRk0UTZGgL9MGwrwTtZXP7qn7Aw-byR3_N_fU"
},
"xms_tcdt": 1504270371
}
UPDATE: It turned out I was getting a token for the MS Graph API which I couldn't use for my own API. So I had to expose an API for my app and add it to the permissions in AAD. This is a really good tutorial for this case -> https://authguidance.com/2017/12/01/azure-ad-spa-code-sample/
According to the information you provide, we request access token to call Microsoft Graph. If so, we do not need to validate Microsoft graph signature. Because MsGraph recognized an opportunity to improve security for users. They achieved this by putting a ‘nonce’ into the jwt header. The JWS is signed with a SHA2 of the nonce, the nonce
is replaced before the JWS is serialized. To Validate this token, the nonce
will need to be replaced with the SHA2 of the nonce
in the header. Now this can change since there is no public contract. So when calling Microsoft Graph, you should treat access tokens as opaque. For more details, please refer here and here