microsoft-authentication-library-for-js: Strategy.prototype.jwtVerify: cannot verify token. When using AzureAD v2.0

We are migrating our application from the AzureAD v1.0 endpoint to AzureAD v2.0, but after doing the proper changes I receive that response which you see in the title.

This is my configuration:

identityMetadata: OAUTH2_CONFIG.identityMetadata, clientID: OAUTH2_CONFIG.clientID, audience: OAUTH2_CONFIG.clientID, validateIssuer: OAUTH2_CONFIG.validateIssuer, //true passReqToCallback: OAUTH2_CONFIG.passReqToCallback, // true loggingLevel: OAUTH2_CONFIG.loggingLevel

Following the documentation the default value for audience is clientID, anyways I set it. But It can’t validate the Access Token.

Also I am sending the token in the Authorization header like this Bearer $thetokenIrecieve

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 1
  • Comments: 62 (3 by maintainers)

Most upvoted comments

Thanks @holwech I eventually figured it out, and literally punched the air in joy once it finally worked!

As you mention the “secret sauce” was adding a scope to my registered app in AAD and then requesting that scope. Doing so mysteriously transforms the token you get back, suddenly it will have your app client id as the aud, it will have the correct iss set (pointing at login.microsoft) and will be a v2 token. Magic! More importantly this library will successfully validate it

In summary:

  • Set an app URI and add a scope in the “Expose an API” section of the app registration portal. E.g. api://{app-guid}/myscope. The name of the scope doesn’t matter
  • In your client code add the scope api://{app-guid}/myscope to the scopes you request, i.e. alongside user.read blah blah

The access token you get back will now be for your app and not the Graph API

As you mention, it’s poorly/barely explained in the docs how to set this up or why the tokens differ so much. So many of the code examples and samples have you requesting user.read and leaving it there, so this is why I think people are tripping over it

This section does mention it, but feels quite buried https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-overview

I have the same problem, despite having a v2.0 access token. If you haven’t figured it out by now already, to get a v2.0 access token go to portal.azure.com -> Azure Active Directory -> App registrations (preview) -> Find your app registration in the list -> Manifest. In the manifest file you will find a field called accessTokenAcceptedVersion, this is by default set to null, which makes the app default to v1.0 access token, even if it is registered as a v2 app (I don’t know why it works like this…). To get v2.0 access tokens, set this value to 2. The Azure documentation is super confusing about this, but it seems to do the trick…

Did you manage to solve the issue in the end?

@timmyreilly In my case, the validation with the BearerStrategy worked as soon as I set loggingNoPII: false

@rocketraman

Thank you for the response!

Luckily i handled it by put validateIssuer=false😃 that is my config:

screenshot 2018-11-23 at 10 23 30

One last note: I think there is still a bug in the library. The validateIssuer is supposed to work automatically when using the tenant-specific endpoint. However, that endpoint returns the v1 issuer e.g.:

    "issuer": "https://sts.windows.net/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/",

For the v2 endpoint, the issuer is different:

https://login.microsoftonline.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/v2.0

It looks like this library doesn’t adjust its logic to set the issuer depending on whether the access token is a v1 or v2 token.

Actually, never mind – in my case it just looks like I hadn’t set the issuer correctly. Logging at loggingNoPII: false produces far more useful information for debugging.

Hello everyone , I’m currently getting the exactly same issue. After many time research, I found out the reason and also solved It. The reason come from the scope on SPA login. This access token is V1 and It only use for getting data on graph, I’ll wrong If we use It with passport ad… We need add a scope for our app, not scope for graph. After change the scope, we can get access_token which have decoded result like this. It in v2 version, the aud is our app id, then azure passport will work correctly. For more details, you can look at this link https://authguidance.com/2017/12/01/azure-ad-spa-code-sample/ . I’m sure It working because we are having a wrong understand about graph API.

image

Thank you to everyone that share in this issue!. I also wasted a couple of hours on this! I just want to add something I noticed, from your client you must pass the api://app-id/read scope (after you added it on azure, I put a screenshot at the end in case it helps anyone having problems setting it up), and make sure you do NOT pass the user.read from graph or any other scope from the graph because that will make the token be a v1.0 again.

Now I was wondering, is it safe to just receive the JWT token on the API, and then just parse it using JWT libs and actually query GraphAPI to get current user data and use that as the verification that the token is valid. I was able to do this by making this request:

curl --location --request GET 'https://graph.microsoft.com/v1.0/users/USER-ID-0000-aaaa-bbbbbbbb' \
--header 'Authorization: bearer YOUR_V1_GRAPH_TOKEN_HERE'

And this will return some user information like this:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
    "businessPhones": [
        "000-000-0000"
    ],
    "displayName": "User name",
    "givenName": "Given Name",
    "jobTitle": "something",
    "mail": "email@thanks.azuread.for.the.great.documentation.and.not.making.us.waste.time.com",
    "mobilePhone": null,
    "officeLocation": null,
    "preferredLanguage": null,
    "surname": "Surname",
    "id": "USER-ID-0000-aaaa-bbbbbbbb"
}

Then just to be safe you could double check the reply info, email, and user id with the data encoded on the token, and if it’s all good take as a valid token??

In case it helps anyone, here is how you expose the api and add a scope: image

To get the BerarerStratergy to work for me I followed @benc-uk’s suggestion and created an application scope api://{app-guid}/myscope. I added myscope to the scope list in config.js but I also had to make sure user.read was removed from the list. I also followed @oehm-smith’s suggestion and got my Azure AD application manifest changed to "accessTokenAcceptedVersion": 2

I’m just following the specific, recommended approach from both the javascript authentication client for the client (web browser) and this library which says to use the access token. It’s also written up to use the access token on the MS web site as a specific scenario for SPA clients and a web server backend. I’m beginning to suspect that if “ms graph” scopes are included in your access token request that it will default back to 1.0 version access tokens due to ms graph issues. The issue is that I need to access msgraph and my backend.

I tried to implement normal AD (not B2C) openid and bearer strategies.

The current problem is that I get a token with openid strategy, but I cannot verify it’s signature with bearer strategy.

Here the output:

{"name":"AzureAD: Bearer Strategy","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"In BearerStrategy constructor: created strategy with options {\"identityMetadata\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/v2.0/.well-known/openid-configuration\",\"clientID\":\"2da8f1d9-ddab-4333-83df-49479403b200\",\"allowHttpForRedirectUrl\":true,\"audience\":[\"2da8f1d9-ddab-4333-83df-49479403b200\"],\"passReqToCallback\":false,\"loggingLevel\":\"info\",\"isB2C\":false,\"validateIssuer\":true,\"scope\":[\"profile\",\"offline_access\",\"User.Read\"],\"loggingNoPII\":false,\"clockSkew\":300,\"allowMultiAudiencesInToken\":false,\"_isCommonEndpoint\":false}","time":"2018-11-29T09:08:49.990Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"In Strategy.prototype.authenticate: {\"metadataURL\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/v2.0/.well-known/openid-configuration?x-client-SKU=passport-azure-ad&x-client-Ver=4.0.0\",\"cacheKey\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/v2.0/.well-known/openid-configuration?x-client-SKU=passport-azure-ad&x-client-Ver=4.0.0\"}","time":"2018-11-29T09:13:39.118Z","v":0}
{"name":"AzureAD: Metadata Parser","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"Parsing JSON retreived from the endpoint","time":"2018-11-29T09:13:39.297Z","v":0}
{"name":"AzureAD: Metadata Parser","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"Request to update the Open ID Connect Metadata","time":"2018-11-29T09:13:39.298Z","v":0}
{"name":"AzureAD: Metadata Parser","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"Algorithm retrieved was:  [ 'RS256' ]","time":"2018-11-29T09:13:39.298Z","v":0}
{"name":"AzureAD: Metadata Parser","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"Issuer we are using is:  https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/v2.0","time":"2018-11-29T09:13:39.298Z","v":0}
{"name":"AzureAD: Metadata Parser","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"Key Endpoint we will use is:  https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/discovery/v2.0/keys","time":"2018-11-29T09:13:39.298Z","v":0}
{"name":"AzureAD: Metadata Parser","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"Authentication endpoint we will use is:  https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/oauth2/v2.0/authorize","time":"2018-11-29T09:13:39.298Z","v":0}
{"name":"AzureAD: Metadata Parser","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"Token endpoint we will use is:  https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/oauth2/v2.0/token","time":"2018-11-29T09:13:39.298Z","v":0}
{"name":"AzureAD: Metadata Parser","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"User info endpoint we will use is:  https://graph.microsoft.com/oidc/userinfo","time":"2018-11-29T09:13:39.298Z","v":0}
{"name":"AzureAD: Metadata Parser","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"The logout endpoint we will use is:  https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/oauth2/v2.0/logout","time":"2018-11-29T09:13:39.298Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"In Strategy.prototype.authenticate: received metadata: {\"url\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/v2.0/.well-known/openid-configuration?x-client-SKU=passport-azure-ad&x-client-Ver=4.0.0\",\"metadata\":{\"authorization_endpoint\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/oauth2/v2.0/authorize\",\"token_endpoint\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/oauth2/v2.0/token\",\"token_endpoint_auth_methods_supported\":[\"client_secret_post\",\"private_key_jwt\",\"client_secret_basic\"],\"jwks_uri\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/discovery/v2.0/keys\",\"response_modes_supported\":[\"query\",\"fragment\",\"form_post\"],\"subject_types_supported\":[\"pairwise\"],\"id_token_signing_alg_values_supported\":[\"RS256\"],\"http_logout_supported\":true,\"frontchannel_logout_supported\":true,\"end_session_endpoint\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/oauth2/v2.0/logout\",\"response_types_supported\":[\"code\",\"id_token\",\"code id_token\",\"id_token token\"],\"scopes_supported\":[\"openid\",\"profile\",\"email\",\"offline_access\"],\"issuer\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/v2.0\",\"claims_supported\":[\"sub\",\"iss\",\"cloud_instance_name\",\"cloud_instance_host_name\",\"cloud_graph_host_name\",\"msgraph_host\",\"aud\",\"exp\",\"iat\",\"auth_time\",\"acr\",\"nonce\",\"preferred_username\",\"name\",\"tid\",\"ver\",\"at_hash\",\"c_hash\",\"email\"],\"request_uri_parameter_supported\":false,\"userinfo_endpoint\":\"https://graph.microsoft.com/oidc/userinfo\",\"tenant_region_scope\":\"EU\",\"cloud_instance_name\":\"microsoftonline.com\",\"cloud_graph_host_name\":\"graph.windows.net\",\"msgraph_host\":\"graph.microsoft.com\",\"rbac_url\":\"https://pas.windows.net\"},\"authtype\":\"oidc\",\"loggingNoPII\":false,\"oidc\":{\"algorithms\":[\"RS256\"],\"authorization_endpoint\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/oauth2/v2.0/authorize\",\"end_session_endpoint\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/oauth2/v2.0/logout\",\"issuer\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/v2.0\",\"token_endpoint\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/oauth2/v2.0/token\",\"userinfo_endpoint\":\"https://graph.microsoft.com/oidc/userinfo\",\"keys\":[{\"kty\":\"RSA\",\"use\":\"sig\",\"kid\":\"wULmYfsqdQuWtV_-hxVtDJJZM4Q\",\"x5t\":\"wULmYfsqdQuWtV_-hxVtDJJZM4Q\",\"n\":\"mtRp-flf1CRQNELJgexsre0aBxDUaIcRysmaYBxWxgH0r2cfdwoe7edWto5rMbkSkXVxxV8969Cz6WDmX3SmOSAdVMu3nVXcIOVIbnII5Rz9W6j5YrInyP_FPQ_XuZ4WMfXuItGXN8bofel1ehBbhS9YEvgFpfPBCvSD1JVCrUD_YZtXFGPS0--9ncTxNPEcso3NJrp-FDESZ8f8nfjRB1mfcePwGdKsuzndL3FGPbLn8hpO4ZXn_KysIma4XBnqvbs0X0AqWEG_g7abL63HM9Ci7QO7PY6Rm5XDci-Kh-conWcKXv5zt5NOKgjOiepr5VN3bjSl8cpT2gtZStFtSw\",\"e\":\"AQAB\",\"x5c\":[\"MIIDBTCCAe2gAwIBAgIQKGZsKfAUzaJHVantyrwVdzANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE4MTAwMTAwMDAwMFoXDTIwMTAwMTAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJrUafn5X9QkUDRCyYHsbK3tGgcQ1GiHEcrJmmAcVsYB9K9nH3cKHu3nVraOazG5EpF1ccVfPevQs+lg5l90pjkgHVTLt51V3CDlSG5yCOUc/Vuo+WKyJ8j/xT0P17meFjH17iLRlzfG6H3pdXoQW4UvWBL4BaXzwQr0g9SVQq1A/2GbVxRj0tPvvZ3E8TTxHLKNzSa6fhQxEmfH/J340QdZn3Hj8BnSrLs53S9xRj2y5/IaTuGV5/ysrCJmuFwZ6r27NF9AKlhBv4O2my+txzPQou0Duz2OkZuVw3IviofnKJ1nCl7+c7eTTioIzonqa+VTd240pfHKU9oLWUrRbUsCAwEAAaMhMB8wHQYDVR0OBBYEFFve38eLO2PUMXcqbBC/YDaayIbrMA0GCSqGSIb3DQEBCwUAA4IBAQCWhrem9A2NrOiGesdzbDy2K3k0oxjWMlM/ZGIfPMPtZl4uIzhc+vdDVVFSeV8SKTOEIMjMOJTQ3GJpZEHYlyM7UsGWiMSXqzG5HUxbkPvuEFHx7cl9Ull3AEymB2oVPC9DPtLUXPyDH898QgEEVhAEI+JZc1Yd6mAlY/5nOw5m2Yqm+84JOPWLgFDqfVmz/MH27LS1rnzzc+0hhcm/Nv/x7FmpOeRfh00BjCA4PogJlpjl/z/6+GTYcYFsvKE3jmmXka8tQbBOHgAlMnamFA8xGeDok6QaxOELu8NSWzvyZXM2lJK5WFQPHF2hjnNXs6+RxOovG55Ybpo52c2frhNZ\"],\"issuer\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/v2.0\"},{\"kty\":\"RSA\",\"use\":\"sig\",\"kid\":\"nbCwW11w3XkB-xUaXwKRSLjMHGQ\",\"x5t\":\"nbCwW11w3XkB-xUaXwKRSLjMHGQ\",\"n\":\"u98KvoUHfs2z2YJyfkJzaGFYM58eD0epHfETTwNDl6AL_cTfOklcxM4jrLWvVqqp2sHaH0gFpYPyovN-_akmE_4fkc0Vw_wGM5jDP-jnOJ1vBvbFoF7uBAy4r3ln2ey1PoGUhpkXdDawhIfdAbc7WLtyopHNWQXI336rXiwjvcjL8dHhievDOktsAsilADP5wJT0lyTifONPZOq-XWCw9FtXAQr7DniOC5uDuUaL0mM1UJChiCrDmFOAf6CNdu2SwLinXYauqM9ORElKYEChoEfi51fcsmlsn4mtNPkxstvR7OJiJBpvk7FLeiaBtMnsO5x30DPgrhAagrVn3IaKRQ\",\"e\":\"AQAB\",\"x5c\":[\"MIIDBTCCAe2gAwIBAgIQV68hSN9DrrlCaA3NJ0bnfDANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE4MTExMTAwMDAwMFoXDTIwMTExMTAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALvfCr6FB37Ns9mCcn5Cc2hhWDOfHg9HqR3xE08DQ5egC/3E3zpJXMTOI6y1r1aqqdrB2h9IBaWD8qLzfv2pJhP+H5HNFcP8BjOYwz/o5zidbwb2xaBe7gQMuK95Z9nstT6BlIaZF3Q2sISH3QG3O1i7cqKRzVkFyN9+q14sI73Iy/HR4YnrwzpLbALIpQAz+cCU9Jck4nzjT2Tqvl1gsPRbVwEK+w54jgubg7lGi9JjNVCQoYgqw5hTgH+gjXbtksC4p12GrqjPTkRJSmBAoaBH4udX3LJpbJ+JrTT5MbLb0eziYiQab5OxS3omgbTJ7Ducd9Az4K4QGoK1Z9yGikUCAwEAAaMhMB8wHQYDVR0OBBYEFGWLmYFSm5Exg9VcAGSg5sFE1mXgMA0GCSqGSIb3DQEBCwUAA4IBAQB0yTGzyhx+Hz2vwBSo5xCkiIom6h7b946KKiWvgBLeOvAuxOsB15N+bbf51sUfUJ6jBaa1uJjJf27dxwH0oUe2fcmEN76QSrhULYe+k5yyJ7vtCnd6sHEfn9W6iRozv0cb48tESOTlFuwbYDVW+YZxzM9EQHC32CjugURzuN9/rf6nJ9etSeckRMO8QPqyIi4e5sGSDYExxNs7J4prhIbtYT4NRiqc4nWzA/p5wSYOUgAZMSTLD/beSI81UN1Ao9VBBJu3v83d62WL3zHSbpUwtG/utNhSi/n/7Q94claEWJVhBx6LiA1hrU6YZkjRGqBOrWIZkSkh75xW6Xujocy4\"],\"issuer\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/v2.0\"},{\"kty\":\"RSA\",\"use\":\"sig\",\"kid\":\"M6pX7RHoraLsprfJeRCjSxuURhc\",\"x5t\":\"M6pX7RHoraLsprfJeRCjSxuURhc\",\"n\":\"xHScZMPo8FifoDcrgncWQ7mGJtiKhrsho0-uFPXg-OdnRKYudTD7-Bq1MDjcqWRf3IfDVjFJixQS61M7wm9wALDj--lLuJJ9jDUAWTA3xWvQLbiBM-gqU0sj4mc2lWm6nPfqlyYeWtQcSC0sYkLlayNgX4noKDaXivhVOp7bwGXq77MRzeL4-9qrRYKjuzHfZL7kNBCsqO185P0NI2Jtmw-EsqYsrCaHsfNRGRrTvUHUq3hWa859kK_5uNd7TeY2ZEwKVD8ezCmSfR59ZzyxTtuPpkCSHS9OtUvS3mqTYit73qcvprjl3R8hpjXLb8oftfpWr3hFRdpxrwuoQEO4QQ\",\"e\":\"AQAB\",\"x5c\":[\"MIIC8TCCAdmgAwIBAgIQfEWlTVc1uINEc9RBi6qHMjANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDExhsb2dpbi5taWNyb3NvZnRvbmxpbmUudXMwHhcNMTgxMDE0MDAwMDAwWhcNMjAxMDE0MDAwMDAwWjAjMSEwHwYDVQQDExhsb2dpbi5taWNyb3NvZnRvbmxpbmUudXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEdJxkw+jwWJ+gNyuCdxZDuYYm2IqGuyGjT64U9eD452dEpi51MPv4GrUwONypZF/ch8NWMUmLFBLrUzvCb3AAsOP76Uu4kn2MNQBZMDfFa9AtuIEz6CpTSyPiZzaVabqc9+qXJh5a1BxILSxiQuVrI2BfiegoNpeK+FU6ntvAZervsxHN4vj72qtFgqO7Md9kvuQ0EKyo7Xzk/Q0jYm2bD4SypiysJoex81EZGtO9QdSreFZrzn2Qr/m413tN5jZkTApUPx7MKZJ9Hn1nPLFO24+mQJIdL061S9LeapNiK3vepy+muOXdHyGmNctvyh+1+laveEVF2nGvC6hAQ7hBAgMBAAGjITAfMB0GA1UdDgQWBBQ5TKadw06O0cvXrQbXW0Nb3M3h/DANBgkqhkiG9w0BAQsFAAOCAQEAI48JaFtwOFcYS/3pfS5+7cINrafXAKTL+/+he4q+RMx4TCu/L1dl9zS5W1BeJNO2GUznfI+b5KndrxdlB6qJIDf6TRHh6EqfA18oJP5NOiKhU4pgkF2UMUw4kjxaZ5fQrSoD9omjfHAFNjradnHA7GOAoF4iotvXDWDBWx9K4XNZHWvD11Td66zTg5IaEQDIZ+f8WS6nn/98nAVMDtR9zW7Te5h9kGJGfe6WiHVaGRPpBvqC4iypGHjbRwANwofZvmp5wP08hY1CsnKY5tfP+E2k/iAQgKKa6QoxXToYvP7rsSkglak8N5g/+FJGnq4wP6cOzgZpjdPMwaVt5432GA==\"],\"issuer\":\"https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/v2.0\"}]}}","time":"2018-11-29T09:13:39.478Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"In Strategy.prototype.authenticate: we will validate the following options: [object Object]","time":"2018-11-29T09:13:39.513Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"In Strategy.prototype.authenticate: received access_token from request header: eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFDNXVuYTBFVUZnVElGOEVsYXh0V2pUTlg4NWEwaGJIUUtCeXVnbFdtVGhIOWRUQ0NWSlQ2eEgwcmtVY2l3aTlEdms2YWM3Y1FyaUZvYnRqQmxqenNzd3BSY1FYdElVLVk5VlFCcGpja1JvZXlBQSIsImFsZyI6IlJTMjU2IiwieDV0Ijoid1VMbVlmc3FkUXVXdFZfLWh4VnRESkpaTTRRIiwia2lkIjoid1VMbVlmc3FkUXVXdFZfLWh4VnRESkpaTTRRIn0.eyJhdWQiOiJodHRwczovL2dyYXBoLm1pY3Jvc29mdC5jb20iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9lNWNhM2U1OC1mYjYzLTQ3MzYtYmY5NS01NmQyYjA0ZGY3NDUvIiwiaWF0IjoxNTQzNDgxOTM4LCJuYmYiOjE1NDM0ODE5MzgsImV4cCI6MTU0MzQ4NTgzOCwiYWNjdCI6MCwiYWNyIjoiMSIsImFpbyI6IkFTUUEyLzhKQUFBQTl5cXA2dDhpc2tDdFNIZnNiOVJIWVBvaTJqZXJpeHlKa2VUVEpoOVovQXM9IiwiYWx0c2VjaWQiOiIxOmxpdmUuY29tOjAwMDZCRkZERUE2NUI0OTciLCJhbXIiOlsicHdkIl0sImFwcF9kaXNwbGF5bmFtZSI6IkF6dXJlQWRBcHAiLCJhcHBpZCI6IjJkYThmMWQ5LWRkYWItNDMzMy04M2RmLTQ5NDc5NDAzYjIwMCIsImFwcGlkYWNyIjoiMSIsImVtYWlsIjoidmljaHVndW5vdkBob3RtYWlsLmNvbSIsImZhbWlseV9uYW1lIjoiQ2h1Z3Vub3YiLCJnaXZlbl9uYW1lIjoiVmljdG9yIiwiaWRwIjoibGl2ZS5jb20iLCJpcGFkZHIiOiIyMTIuMTE0LjE1OS4yMzQiLCJuYW1lIjoiVmljdG9yIENodWd1bm92Iiwib2lkIjoiNjcwYmFhYjktMjNmYi00NzE5LTgyMDctYjlhYmFlN2IxZDdhIiwicGxhdGYiOiI1IiwicHVpZCI6IjEwMDMwMDAwQUU3OTBDNjAiLCJzY3AiOiJvcGVuaWQgcHJvZmlsZSBVc2VyLlJlYWQgZW1haWwiLCJzdWIiOiJOT3BnRzJXcVFlRDFaMnpvX29UenFTeHg2cDVLR2NmUDA0RHZIRGtHMTNNIiwidGlkIjoiZTVjYTNlNTgtZmI2My00NzM2LWJmOTUtNTZkMmIwNGRmNzQ1IiwidW5pcXVlX25hbWUiOiJsaXZlLmNvbSN2aWNodWd1bm92QGhvdG1haWwuY29tIiwidXRpIjoicDJpaEhqcmpxMFNfMWZLVVhGdDBBUSIsInZlciI6IjEuMCIsIndpZHMiOlsiNjJlOTAzOTQtNjlmNS00MjM3LTkxOTAtMDEyMTc3MTQ1ZTEwIl0sInhtc19zdCI6eyJzdWIiOiJJMDMtbzVzOW9uQ3RtWVZTRWNiLVRnSHdDbVFFVl9WYlRab3ZBM0pTdnFZIn0sInhtc190Y2R0IjoxNTM5MDc1NjM2fQ.QcFiPmNLN6H0zpDoywdAPAzwZxiOM4qZGYr6KTl3lMk874sJd8ijX_uffOmQU2uyFvIW27eaQyrY_3FVg2nL_ic_Qn01-_twpheSyBAO8TMboUjQfplnLiSpS9ymRtuzBe6Mv2CQYHH9geTah3S-i9t9NysoaUGefCKpcHDNANdljs2EBAjPaUSrE8uJmLcOLrGfW_Z3eDp3w1-6ULFyXB1eKKluRDSFR1Qr3g-MbFg44F_zd-3fLk_gFSLiBA3Srl235uV4YDz3WhkI_FirBLJSI2QX_XZIlzoBWHPOkFGR-eP-RsaI4RU_t46i8_IWfBgAsUYmxK4WjXqDe68I7Q","time":"2018-11-29T09:13:39.513Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"In Strategy.prototype.jwtVerify: token decoded:   { header:\n   { typ: 'JWT',\n     nonce:\n      'AQABAAAAAAC5una0EUFgTIF8ElaxtWjTNX85a0hbHQKByuglWmThH9dTCCVJT6xH0rkUciwi9Dvk6ac7cQriFobtjBljzsswpRcQXtIU-Y9VQBpjckRoeyAA',\n     alg: 'RS256',\n     x5t: 'wULmYfsqdQuWtV_-hxVtDJJZM4Q',\n     kid: 'wULmYfsqdQuWtV_-hxVtDJJZM4Q' },\n  payload:\n   { aud: 'https://graph.microsoft.com',\n     iss:\n      'https://sts.windows.net/e5ca3e58-fb63-4736-bf95-56d2b04df745/',\n     iat: 1543481938,\n     nbf: 1543481938,\n     exp: 1543485838,\n     acct: 0,\n     acr: '1',\n     aio: 'ASQA2/8JAAAA9yqp6t8iskCtSHfsb9RHYPoi2jerixyJkeTTJh9Z/As=',\n     altsecid: '1:live.com:0006BFFDEA65B497',\n     amr: [ 'pwd' ],\n     app_displayname: 'AzureAdApp',\n     appid: '2da8f1d9-ddab-4333-83df-49479403b200',\n     appidacr: '1',\n     email: 'vichugunov@hotmail.com',\n     family_name: 'Chugunov',\n     given_name: 'Victor',\n     idp: 'live.com',\n     ipaddr: '212.114.159.234',\n     name: 'Victor Chugunov',\n     oid: '670baab9-23fb-4719-8207-b9abae7b1d7a',\n     platf: '5',\n     puid: '10030000AE790C60',\n     scp: 'openid profile User.Read email',\n     sub: 'NOpgG2WqQeD1Z2zo_oTzqSxx6p5KGcfP04DvHDkG13M',\n     tid: 'e5ca3e58-fb63-4736-bf95-56d2b04df745',\n     unique_name: 'live.com#vichugunov@hotmail.com',\n     uti: 'p2ihHjrjq0S_1fKUXFt0AQ',\n     ver: '1.0',\n     wids: [ '62e90394-69f5-4237-9190-012177145e10' ],\n     xms_st: { sub: 'I03-o5s9onCtmYVSEcb-TgHwCmQEV_VbTZovA3JSvqY' },\n     xms_tcdt: 1539075636 },\n  signature:\n   'QcFiPmNLN6H0zpDoywdAPAzwZxiOM4qZGYr6KTl3lMk874sJd8ijX_uffOmQU2uyFvIW27eaQyrY_3FVg2nL_ic_Qn01-_twpheSyBAO8TMboUjQfplnLiSpS9ymRtuzBe6Mv2CQYHH9geTah3S-i9t9NysoaUGefCKpcHDNANdljs2EBAjPaUSrE8uJmLcOLrGfW_Z3eDp3w1-6ULFyXB1eKKluRDSFR1Qr3g-MbFg44F_zd-3fLk_gFSLiBA3Srl235uV4YDz3WhkI_FirBLJSI2QX_XZIlzoBWHPOkFGR-eP-RsaI4RU_t46i8_IWfBgAsUYmxK4WjXqDe68I7Q' }","time":"2018-11-29T09:13:39.516Z","v":0}
{"name":"AzureAD: Metadata Parser","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"working on key: { kty: 'RSA',\n  use: 'sig',\n  kid: 'wULmYfsqdQuWtV_-hxVtDJJZM4Q',\n  x5t: 'wULmYfsqdQuWtV_-hxVtDJJZM4Q',\n  n:\n   'mtRp-flf1CRQNELJgexsre0aBxDUaIcRysmaYBxWxgH0r2cfdwoe7edWto5rMbkSkXVxxV8969Cz6WDmX3SmOSAdVMu3nVXcIOVIbnII5Rz9W6j5YrInyP_FPQ_XuZ4WMfXuItGXN8bofel1ehBbhS9YEvgFpfPBCvSD1JVCrUD_YZtXFGPS0--9ncTxNPEcso3NJrp-FDESZ8f8nfjRB1mfcePwGdKsuzndL3FGPbLn8hpO4ZXn_KysIma4XBnqvbs0X0AqWEG_g7abL63HM9Ci7QO7PY6Rm5XDci-Kh-conWcKXv5zt5NOKgjOiepr5VN3bjSl8cpT2gtZStFtSw',\n  e: 'AQAB',\n  x5c:\n   [ 'MIIDBTCCAe2gAwIBAgIQKGZsKfAUzaJHVantyrwVdzANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE4MTAwMTAwMDAwMFoXDTIwMTAwMTAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJrUafn5X9QkUDRCyYHsbK3tGgcQ1GiHEcrJmmAcVsYB9K9nH3cKHu3nVraOazG5EpF1ccVfPevQs+lg5l90pjkgHVTLt51V3CDlSG5yCOUc/Vuo+WKyJ8j/xT0P17meFjH17iLRlzfG6H3pdXoQW4UvWBL4BaXzwQr0g9SVQq1A/2GbVxRj0tPvvZ3E8TTxHLKNzSa6fhQxEmfH/J340QdZn3Hj8BnSrLs53S9xRj2y5/IaTuGV5/ysrCJmuFwZ6r27NF9AKlhBv4O2my+txzPQou0Duz2OkZuVw3IviofnKJ1nCl7+c7eTTioIzonqa+VTd240pfHKU9oLWUrRbUsCAwEAAaMhMB8wHQYDVR0OBBYEFFve38eLO2PUMXcqbBC/YDaayIbrMA0GCSqGSIb3DQEBCwUAA4IBAQCWhrem9A2NrOiGesdzbDy2K3k0oxjWMlM/ZGIfPMPtZl4uIzhc+vdDVVFSeV8SKTOEIMjMOJTQ3GJpZEHYlyM7UsGWiMSXqzG5HUxbkPvuEFHx7cl9Ull3AEymB2oVPC9DPtLUXPyDH898QgEEVhAEI+JZc1Yd6mAlY/5nOw5m2Yqm+84JOPWLgFDqfVmz/MH27LS1rnzzc+0hhcm/Nv/x7FmpOeRfh00BjCA4PogJlpjl/z/6+GTYcYFsvKE3jmmXka8tQbBOHgAlMnamFA8xGeDok6QaxOELu8NSWzvyZXM2lJK5WFQPHF2hjnNXs6+RxOovG55Ybpo52c2frhNZ' ],\n  issuer:\n   'https://login.microsoftonline.com/e5ca3e58-fb63-4736-bf95-56d2b04df745/v2.0' }","time":"2018-11-29T09:13:39.516Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"PEMkey generated: -----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAmtRp+flf1CRQNELJgexsre0aBxDUaIcRysmaYBxWxgH0r2cfdwoe\n7edWto5rMbkSkXVxxV8969Cz6WDmX3SmOSAdVMu3nVXcIOVIbnII5Rz9W6j5YrIn\nyP/FPQ/XuZ4WMfXuItGXN8bofel1ehBbhS9YEvgFpfPBCvSD1JVCrUD/YZtXFGPS\n0++9ncTxNPEcso3NJrp+FDESZ8f8nfjRB1mfcePwGdKsuzndL3FGPbLn8hpO4ZXn\n/KysIma4XBnqvbs0X0AqWEG/g7abL63HM9Ci7QO7PY6Rm5XDci+Kh+conWcKXv5z\nt5NOKgjOiepr5VN3bjSl8cpT2gtZStFtSwIDAQAB\n-----END RSA PUBLIC KEY-----\n","time":"2018-11-29T09:13:39.517Z","v":0}
{"name":"AzureAD: Bearer Strategy","hostname":"FVFXG1GSHV2H.local","pid":35339,"level":30,"msg":"authentication failed due to: invalid signature","time":"2018-11-29T09:13:39.520Z","v":0}

My config for openid auth strategy:

{ identityMetadata:
   'https://login.microsoftonline.com/<azure_tenant_id>/v2.0/.well-known/openid-configuration',
  clientID: '<azure_application_id>',
  allowHttpForRedirectUrl: true,
  audience: '<azure_application_id>',
  passReqToCallback: false,
  loggingLevel: 'info',
  isB2C: false,
  prompt: 'login',
  responseType: 'id_token code',
  validateIssuer: false,
  allowMultiAudiencesInToken: true,
  clientSecret: '<azure_app_client_secret>',
  responseMode: 'form_post',
  redirectUrl: '<return url>',
  scope: [ 'openid', 'profile', 'offline_access', 'User.Read' ] }

My config for bearer strategy:

{ identityMetadata:
   'https://login.microsoftonline.com/<azure_tenant_id>/v2.0/.well-known/openid-configuration',
  clientID: '<azure_application_id>',
  allowHttpForRedirectUrl: true,
  audience: '<azure_application_id>',
  passReqToCallback: false,
  loggingLevel: 'info',
  isB2C: false,
  validateIssuer: true,
  scope: [ 'profile', 'offline_access', 'User.Read' ],
  loggingNoPII: false }

What am I doing wrong?

Coming back to this after a long time… after having just updated to MSAL 2.x, I can offer some additional guidance which I’m not sure anyone has mentioned above.

The main thing to note is that with MSAL you can pass one set of scopes (graph scopes like openid, profile, and user.read) to the login call e.g. loginRedirect, and a different set of scopes when retrieving an id token e.g. acquireTokenSilent which can be verified by a backend like passport-azure-ad.

The first set of scopes are Graph scopes, and as many people have noted, the token generated with those scopes is not meant to be validated by any system other than Graph. So, when generating the token that is needed for your backend API, pass only a custom scope (see https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-protected-web-api-app-registration#expose-an-api). This custom scope can be pre-approved by the user at login time by adding it to extraScopesToConsent in the loginRedirect/loginPopup call options.

NOTE: The custom scope recommended in those docs didn’t actually work for me – what did work is to pass an empty scope list to the acquireTokenSilent call, which works fine when all you need is an id token to pass to your backend, and not an access token that your frontend code needs to access some resource directly.

See this comment for more info: https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/1040#issuecomment-540810355.

I don’t know why this is so poorly documented…

Things I have tried.

  • Using every combination of endpoint in both MSAL client side and server side
  • Tried registering my app via Azure portal, and apps.dev.microsoft.com
  • Tried every combination of scopes and audiences (both sides)
  • Tried putting my app id in the scopes
  • Registering new apps in AAD
  • Creating an completely new AAD tenant
  • Trying with work & school and MSA accounts
  • Checking accessTokenAcceptedVersion is set to 2 (setting does nothing, it makes no difference)

@aappddeevv - Think you are right. Nothing I try gives me a v2.0 token, soon as you request “user.read” you get a Graph API scope and you always get a v1.0 token. And you need “user.read” for EVERYTHING! So I don’t see how you can ever validate any access token using this library

I’ve traced the source to be the step where the signature is validated with jws here If you remove that part, then the token validation passes and works

The problem might be in the AAD and the MS Graph platform itself, I’m not sure. But following the standard guidelines it’s not possible to get a client + backend working together

I’ve given up, I’m validating my id-token instead, that works, but it’s not the right thing to be doing

That’s too bad! I see now that MSAL.js returns a v1.0 access token when calling the Microsoft graph API. I get tokens for my own API and for that service I do get a v2.0 token. It might be that the graph API endpoint URL for v2.0 is different than for v1.0, and that MSAL defaults to the v1.0 endpoint. Either way, you shouldn’t really use an access token issued for the graph API to authenticate/authorize someone to access your API service. For that you should either use the id token, or better, issue your own access token for you API service (which is what I have done). I’m not sure this covers your case exactly, but hopefully it is of some help.

@pstepnowski I think I was wrong about the issuer url being incorrect, perhaps my endpoint was wrong at the time? Beats me – I changed so many things back then to get this working, who knows what happened.

In any case, my current options are just:

// options for passport-azure-ad
// https://github.com/AzureAD/passport-azure-ad
// issuer is automatic based on the result of `identityMetadata`
export const azureAdOptions = {
  identityMetadata: `https://login.microsoftonline.com/${TENANT_NAME}.onmicrosoft.com/v2.0/.well-known/openid-configuration`,
  clientID: AAD_CLIENT_ID,
  passReqToCallback: true /*,
  loggingLevel: 'info',
  loggingNoPII: false */
}

and this seems to work…

@aappddeevv this should be put everywhere! Finally fixed my issue after reading this. Why is this happening

I also got this working after several hours of fiddling around with the settings and reading documentation. Who said that authentication should be easy 😩

The solution that worked for me was also the one others wrote about regarding exposing an API. I only had to use this scope on the frontend application (React) tough when authenticating, no scopes had to be added to the bearer strategy options.

This is the final configuration that worked for me using the @azure/msal-browser package for the frontend with the custom scope and passport-azure-ad with the bearer strategy is the following:

{
  identityMetadata: "https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration",
  clientID: "<client-id>",
  validateIssuer: true,
  passReqToCallback: false,
  loggingLevel: "info",
  loggingNoPII: false,
}

For me, it is still generating a V1 token as long as user.read exists. This seems to be an issue with the MS graph API. If you remove user.read then it “works”, however, then you cannot use the graph API or get any user details so it defeats the purpose of the lib.

This ticket has made clear that the real problem we faced was a version 1 token was being returned instead of a V2. Once I realised this the problem became easier!

MS / Azure need to update their doco.

Thanks everyone for this walk through of solving this problem. Other tips I have (possibly discussed above):

  • Azure > App > App Active Directory > App Registrations > Manifest - set accessTokenAcceptedVersion=2
  • Passport-Azure-Ad config (jwtOptions)
    • clientId and audience the same
    • Set the scope to the scope name as seen in Azure > … > API Permissions

Once @benc-uk’s steps and part 1 of mine was done, the errors became obvious and easily solved.

I’m also hitting this.

App registered in AAD as v2, all v2 endpoints used. Token obtained via MSAL library (the new v1.0.0 version) - The returned token is valid when checked at jwt.ms

But this library never validates the tokens no mater what I try! I’ve put every combination of settings in BearerStrategy but nothing works. I’m pulling my hair out

The application is registered on azure under v2. MSAL in the web client obtains a v2 id_toKen as expected. It then gets a v1 access_token. The v1 access_token cannot be verified by passport_azure_ad. The v1 issuance seems to be a AAD problem not just a MSAL javascript problem. Unless passport_azure_ad can verify the v1 access_token using the bearer strategy, which it currently cannot, it appears, the bearer strategy implementation is inconsistent with AAD.

Here it is, retyped. Client then server (passport side)

Client side

// via msal
 const app = new UserAgentApplication(
    BuildConstants.clientId,
    BuildConstants.authority,
     {
      // redirect to root of URL
      redirectUri = dom.document.location.origin
      postLogoutRedirectUri = dom.document.location.origin + "/login"
    }
  )

with

BuildConstants.clientId = <my app guid>
BuildConstants.authority = https://login.microsoftonline.com/<tenant id>

Server side

const bs = new OIDCBearerStrategy(options, (req, token, done) => {
    log("Was the token retreived", token, req)
    if (!token.oid)
        done(new Error("Expected oid in token but no token/id found."))
    else {
        // Given access_token.oid, look up user info and attach
        // to req.user.
        // check roles in the access_token for admin role
        /// isAdmin = ...
        done(null, token, {
            isAdmin,
            id: token.oid,
        })
    }
})
// ....
export const options: IBearerStrategyOptionWithRequest =
    {
      // none of these worked
      identityMetadata: 
      //"https://login.microsoftonline.com/<tenant id>/v2.0/.well-known/openid-configuration"
      //"https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"
      // v1 - jwt.ms suggested I had a v1 of token!
      //"https://login.microsoftonline.com/<tenant id>/.well-known/openid-configuration"
      ,
      clientID: <app id>,
      // Required.
      // Set to true if you use `function(req, token, done)` as the verify callback.
      passReqToCallback: true,
      //issuer: undefined,
      allowMultiAudiencesInToken: false,
      // not sure why this is being set in the access_token for v2, but tried appid, matching it, etc.
      audience: "https://graph.microsoft.com",
      //audience: <appid>,
      // Optional. 'error', 'warn' or 'info'
      loggingLevel: "error",
     // tried leaving out, true and false, per note in another issues
      loggingNoPII: false,
      // @ts-ignore
      validateIssuer: false,
      // changing this did not help
      //issuer: "https://sts.windows.net/<tenant id>",
      // leave blank to not check scope, but matching scopes in actual jwt did not work
      //scope: ["Mail.Read", "Mail.Send", "openid", "profile", "User.Read", "email"]
    }
//...
log("Passport initialization")
app.use(passport.initialize())
passport.use(bs)

@Afdz1020 No, you should send the accessToken.

My accessToken contains nonce in it’s header, so I can’t decode it with passport-azure-ad (reference) - but validating idToken works perfectly.

This blog post written by @tonesandtones may be helpful.

We are working on an update for the passport library that will encompass this ask, you can track the progress here.

The key insight for me was that after getting your identity token, if any scope for msgraph is included in your access token request, its a v1 token no matter what. Hence, I request a separate access token for msgraph and a separate access token for my API. I needed to use 2 tokens and token requests to keep the returned token version clean.

I got this to work, by stepping through these two relevant files.

bearerstrategy.js

  // turn issuer into an array
  if (options.issuer === '')
    options.issuer = null;
  if (options.issuer && Array.isArray(options.issuer) && options.issuer.length === 0)
    options.issuer = null; 
  if (options.issuer && !Array.isArray(options.issuer))
    options.issuer = [options.issuer];

jsonWebToken.js:

[// (1) issuer
  //   - check the existence and the format of payload.iss
  //   - validate if options.issuer is set
  if (typeof payload.iss !== 'string' || payload.iss === '')
    return done(new Error('invalid iss value in payload'));
  if (options.validateIssuer !== false) {
    if (!options.issuer || options.issuer === '' || (Array.isArray(options.issuer) && options.issuer.length === 0))
      return done(new Error('options.issuer is missing'));
    var valid = false;
    if (Array.isArray(options.issuer))
      valid = (options.issuer.indexOf(payload.iss) !== -1);
    else
      valid = (options.issuer === payload.iss);
    if (!valid)
      return done(new Error('jwt issuer is invalid'));
  }
](url)

So fully working code is here:

var options = {
    identityMetadata: "https://" + domain + "/" + tenantID + "/v2.0/.well-known/openid-configuration/",
    
    clientID: clientID,
    policyName: policyName,
    isB2C: true,
    validateIssuer: true,
    loggingLevel: 'error',
    passReqToCallback: false,
    loggingNoPII: true,
    audience: "GUID_HERE",
    issuer: "https://login.microsoftonline.com/MY_GUID_HERE/v2.0/"
};

The issuer comes straight from the B2C AAD (or just AAD) token issuer

I just looked at the manifest and the accessTokenAcceptedVersion was already set to 2. Hmm…

Jummmm, are you working with the v1 or v2 endpoint? Because if you are sendind v1 access_tokens to your server that works with v2 enpoint then It won’t work.

It looks likes the openid configuration at https://login.microsoftonline.com/common/.well-known/openid-configuration (my tenant value has something similar) has an property “jwks_uri”:“https://login.microsoftonline.com/common/discovery/keys” which when accessed has a document with a key entry that matches the kid value found in my access_token.

So it appears that the public key is available for validation.

It also looks like msal is issuing v1 access_token and that won’t change anytime soon.

It feels like this is a passport-azure-ad bug but it is still not clear. The wiki clearly points out supporting v1 metadata but it does not explicitly say that it supports v1 access_tokens.

I can’t get validation to work either. I’ve looked at AzureAD/passport-azure-ad#373 and https://stackoverflow.com/questions/52582547/passport-azure-ad-strategy-prototype-jwtverify-cannot-verify-token. I’m trying to do the v2 endpoint thing. My SPA passes the access token in the header back to a nodejs server where I am using passport BearerStrategy.

I believe that the access token does not give a signature error when decoding with jwt.ms but I’m not 100% sure jwt.ms is trying to verify.

I did notice in jwt.ms that I have a v1 of the token and my audience is set to https://graph.microsoft.com. I’m not sure if this has an effect. I have tried the v1 identityMetadata endpoint and setting audience in my config but that did not work either. Not sure why its v1 with this audience as that’s not in my SPA’s config anywhere and I signed up the app using the “v2” registration portal. My ID token is listed as v2, but the SPA MSAL generates a v1 access token. I’m using msal 0.2.4 in my SPA that sends the access token to my SPA data server.

Essentially what I am seeing is that based on AzureAD/passport-azure-ad#373, AzureAD/microsoft-authentication-library-for-js#3259, a major use case for this library does not work and it appears that the v1 access token issued by azure ad is part of the problem.

Another reference: https://stackoverflow.com/questions/54916165/passport-azure-ad-veriy-msal-js-token-with-bearer-strategy