0

I request an openid id_token from Google or Microsoft and after going through the motions and consenting to my app's request for a scope of openid, an id_token is duly returned. I can split it on the dots and the first two parts I can decode as valid JSON so it's a JWT, no doubt about that.

But when I try to use it as a bearer token in the authorization header of a request to an aspnetcore WebApi method that's annotated with [Authorize], I get 401 Unauth, with this header:

WWW-Authenticate: Bearer error=invalid_token, error_description="The signature is invalid"

It seems unlikely to me that both Microsoft and Google are issuing dud JWTs. So now I need to know what can cause this reaction from the middleware, which is current and is set up like this:

app.UseJwtBearerAuthentication(new JwtBearerOptions { SaveToken = true });

In case it matters, here's my redacted params for the token request.

  "uri": "https://accounts.google.com/o/oauth2/auth",
  "parameters": {
    "request_type": "token",
    "response_type": "id_token",
    "client_id": "REDACTED.apps.googleusercontent.com",
    "redirect_uri": "https://REDACTED/",
    "scope": "openid",
    "state": "STATE",
    "nonce": "wibble",
    "allow_signup": "true"
  }

Obviously state and nonce are stable dummy values while I figure this out.

So, invalid? WTF? I tried using http://jwt.io to check token as captured by my code and it didn't seem unhappy. The header and the payload decoded but I wasn't really clear how to validate the signature, they wanted more values and I'm not clear on where one would obtain them. Guidance on that might be helpful.

Here's a token as I extracted it.

eyJhbGciOiJSUzI1NiIsImtpZCI6IjJiMmU0ZDZmMTgyMWFkNGQ3NmQ0NTUzYzk1MWI3NTYyMmFjYjY1MzkifQ.eyJhenAiOiI3ODg4NTA3NTgwMTItdTUwbnVkMDU0aDJsNDIxOXRlaXYxbzU4Mzg5am1iNnAuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI3ODg4NTA3NTgwMTItdTUwbnVkMDU0aDJsNDIxOXRlaXYxbzU4Mzg5am1iNnAuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTU2NTI3NjAwMDUzMDY1MDIxNTAiLCJub25jZSI6IndpYmJsZSIsImlzcyI6ImFjY291bnRzLmdvb2dsZS5jb20iLCJpYXQiOjE0OTUyNTA0MzcsImV4cCI6MTQ5NTI1NDAzN30.Mb3E32cVeJ4dvPw-Fpj5j1GwXIFzwTaa0fvSEh7YjvR3d18hC8_G0avWYGNb6CoJzasZnsSGDLDi_s0Cfpiov0ZBoJVap69udkR1RiTbOc-WxcgtimflG80LwpPpNepEjuJfF1i1OwIFSputpXKbFQOsTP5fN4Xe7cE-EjTrHVX8zzR2v31pqrjh1o5HSK2QEQXM7eEYYvJrDMpgjtL5r22DoQdPHCZgt1Wi3XnTRn_MCD8zWX99Z6n11TpD6IWxlgzBCItlrr6nXRv1Np9ti2Mvw7p0-gCmpFiJKD60J34aqB1AJUpPFXuZWpV--KTTA3T55rC2lRg6vSueeK6QoQ

And for your convenience here it is again but split on the dots.

eyJhbGciOiJSUzI1NiIsImtpZCI6IjJiMmU0ZDZmMTgyMWFkNGQ3NmQ0NTUzYzk1MWI3NTYyMmFjYjY1MzkifQ

eyJhenAiOiI3ODg4NTA3NTgwMTItdTUwbnVkMDU0aDJsNDIxOXRlaXYxbzU4Mzg5am1iNnAuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI3ODg4NTA3NTgwMTItdTUwbnVkMDU0aDJsNDIxOXRlaXYxbzU4Mzg5am1iNnAuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTU2NTI3NjAwMDUzMDY1MDIxNTAiLCJub25jZSI6IndpYmJsZSIsImlzcyI6ImFjY291bnRzLmdvb2dsZS5jb20iLCJpYXQiOjE0OTUyNTA0MzcsImV4cCI6MTQ5NTI1NDAzN30

Mb3E32cVeJ4dvPw-Fpj5j1GwXIFzwTaa0fvSEh7YjvR3d18hC8_G0avWYGNb6CoJzasZnsSGDLDi_s0Cfpiov0ZBoJVap69udkR1RiTbOc-WxcgtimflG80LwpPpNepEjuJfF1i1OwIFSputpXKbFQOsTP5fN4Xe7cE-EjTrHVX8zzR2v31pqrjh1o5HSK2QEQXM7eEYYvJrDMpgjtL5r22DoQdPHCZgt1Wi3XnTRn_MCD8zWX99Z6n11TpD6IWxlgzBCItlrr6nXRv1Np9ti2Mvw7p0-gCmpFiJKD60J34aqB1AJUpPFXuZWpV--KTTA3T55rC2lRg6vSueeK6QoQ

This one is expired now, obviously, but if you know how to validate the signature it might be handy for checking whether I damaged the token extracting it. Can anyone help me understand what's wrong and how to deal with it?

Hans's answer below seemed likely but reality didn't seem to match the documentation. Then I found this question Microsoft Account OpenID authentication token request returns id_token but not access_token with an answer from a MSFT person. It seems you only get an auth token when you have requested scopes. This makes sense; an auth token authorises access to one or more resources as specified in the token request. The only scope specified in my request is openid which is a request for an id_token as received.

But I'm not trying to get permission to work with provider scopes like the user's address-book or email. I'm trying to delegate authentication to the provider. Not authorization. I want the user's identity established - which as I understand things is the point of an id_token and I want to do my own authorisation using my own database of user roles that essentially define application specific scopes.

I think this is still an answerable question, even if the answer is a thumbnail sketch of how one goes about this using aspnetcore web api, supported by links to pertinent documentation.

OAuth 2 access_token vs OpenId Connect id_token makes it clear that this is a supported scenario. The key phrase here is SSO or single-sign-on. I'm starting to think this is more a question of how to go about SSO for a SPA that uses aspcorenet webapi.

But the original question stands: why do these tokens appear to have invalid signatures?

Community
  • 1
  • 1
Peter Wone
  • 17,965
  • 12
  • 82
  • 134

1 Answers1

0

In the OpenID Connect flow that Google and Microsoft support you'll get both an id_token and an access_token in the response to an authorization request. The id_token is used to authenticate the user to the Client (i.e. your application) and the access_token can be used to access APIs.

So you should be presenting the access_token in the Authorization: bearer <token> header on your API calls rather than the id_token.

Hans Z.
  • 50,496
  • 12
  • 102
  • 115