Auth0 Home Blog Docs

IDToken not returned when additional scopes are defined


#1

I’m using PKCE (Auth Code Grant Flow) to obtain the access token and IDToken. The /oauth/token returns both access token and IDToken if I define scope as “openid” (in the /authorize request). But if I add additional scopes, such as “openid profile”, the /oauth/token no longer returns an IDToken. The same missing IDToken behavior applies if I add any scopes defined under APIs > Scopes as well. How can I get a token that allows me access to both the /userinfo endpoint, as well as my API endpoints (thru custom scopes)?


#3

:wave: @kelvin.lo when you say you do not get an id_token, what do you get in return? if you are using Authorization Code Grant then you get an Authorization code, which can exchange that for an access_token and id_token (steps here) and you should be able to specify any of the standard claims (openid, profile). Is it possible for you to share your code? It is odd that you would have unexpected behavior using those two claims. Do you have any Rules setup currently? Lastly, any additional scopes/custom claims can be added to the token via a rule and namespaced (example here).


#4

@kim.noel, let me list out the steps…

  1. Request for authentication using the scope “openid profile”, invoking the authorization code grant (PKCE) API. Namely, the request is https://idverifact.auth0.com/authorize?audience=https://mediation.securekey.com&scope=openid%20profile&response_type=code&client_id=D2XTLMKl50letQEGgfo0LQccizZAUCjo&state=state_value&redirect_uri=http://localhost:8086/ui&code_challenge_method=S256&code_challenge=dfShyr6JUTNpO76ErKR-vbHr75jmbtsLeMcmzDmyeeY
  2. I get the authorization code in return, which I exchange for an access token and id token making a request to the token exchange endpoint with the body:
    {
    grantType: authorization_code
    clientId: D2XTLMKl50letQEGgfo0LQccizZAUCjo
    code: 9PMfBaJWpfx85-gb
    codeVerifier: wNJy2CcukoNu_oFThm4H5RU2d1WDNEm8VAACOeRWOS8
    redirectUri: http://localhost:8086/ui
    }
  3. But the response to the above only contains the access token, namely:
    {
    accessToken: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik1EaEJRVGRET1RZNU1FWXhOREJCTlRnNU1UUkZRa05ETUVRME1qSXpRelEyTURBNFJFVTVOZyJ9.eyJpc3MiOiJodHRwczovL2lkdmVyaWZhY3QuYXV0aDAuY29tLyIsInN1YiI6Imdvb2dsZS1vYXV0aDJ8MTA1OTg4NjkyMTQ4Mzc1ODAyOTc3IiwiYXVkIjoiaHR0cHM6Ly9tZWRpYXRpb24uc2VjdXJla2V5LmNvbSIsImlhdCI6MTUyMzg5NjE3MSwiZXhwIjoxNTIzOTgyNTcxLCJhenAiOiJEMlhUTE1LbDUwbGV0UUVHZ2ZvMExRY2NpelpBVUNqbyJ9.Md-kgF_-tkZb2RKNt0zRnn80jP8KAd-nBt4jPhsQ6SRu1usbtBh9N5VMJaicmoajYgsOpq97If8ViJu5ENlPztH3pGRAUwPow961q0BI87-GDcmB6-C26js9FVaGSwHPxYKvydHihyfO2dP0ZQFVe_31d2JhPqH0zV9qtZ2T3wZyzrtH-JT4pkrdv5bE6r7WoGD5SWxqOZHXqF5yQAB9GAHmr-sznm18EP2HkB14he4WxYbFnMYhNpgfo4QgR4CnNWF_kb2rM3GAy6AK88Is-mHrNTzvzB3piMVr06BcSWy3a882QV3581JvjfFJh-OazPX_8iYvy0lggmjaIRNlkA
    refreshToken: null
    idToken: null
    tokenType: Bearer
    expiresIn: 86400
    }

If I were to use “openid” rather than “openid profile” as scope, it does return the idToken.

I have setup Rules to return customized claims in the ID Token under my own namespace. If I just have “openid” as the scope, I can see my customized claims appear as expected in the ID_Token, as well as response from /userinfo endpoint.


#5

For completeness, I’ve included below the steps corresponding to the above, but with only scope=“openid”

  1. https://idverifact.auth0.com/authorize?audience=https://mediation.securekey.com&scope=openid&response_type=code&client_id=D2XTLMKl50letQEGgfo0LQccizZAUCjo&state=state_value&redirect_uri=http://localhost:8086/ui&code_challenge_method=S256&code_challenge=dfShyr6JUTNpO76ErKR-vbHr75jmbtsLeMcmzDmyeeY
  2. token exchange request body:
    {
    grantType: authorization_code
    clientId: D2XTLMKl50letQEGgfo0LQccizZAUCjo
    code: rzf11xj7_MB2QAxj
    codeVerifier: wNJy2CcukoNu_oFThm4H5RU2d1WDNEm8VAACOeRWOS8
    redirectUri: http://localhost:8086/ui
    }
  3. token exchange response body:
    {
    accessToken: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik1EaEJRVGRET1RZNU1FWXhOREJCTlRnNU1UUkZRa05ETUVRME1qSXpRelEyTURBNFJFVTVOZyJ9.eyJpc3MiOiJodHRwczovL2lkdmVyaWZhY3QuYXV0aDAuY29tLyIsInN1YiI6Imdvb2dsZS1vYXV0aDJ8MTA1OTg4NjkyMTQ4Mzc1ODAyOTc3IiwiYXVkIjpbImh0dHBzOi8vbWVkaWF0aW9uLnNlY3VyZWtleS5jb20iLCJodHRwczovL2lkdmVyaWZhY3QuYXV0aDAuY29tL3VzZXJpbmZvIl0sImlhdCI6MTUyMzg5NjQ1OSwiZXhwIjoxNTIzOTgyODU5LCJhenAiOiJEMlhUTE1LbDUwbGV0UUVHZ2ZvMExRY2NpelpBVUNqbyIsInNjb3BlIjoib3BlbmlkIn0.l4O3_FNTo_kdifFD2QfOLlenScAvmEYAhWc5_DoYUttM3i60FM-Nyy-JdCb3mhSS6IfGQuBO1xYjq-5qpfX2KRx8uf9EWsHqMp_xvJ5ox3G6G9abN0B5rTtJv7zxVK643j3_-B6N-mQyACF4H73V54urrgtCFs5XfGBvFpCSruD9A8B5FlhOv17NulsRmGcpSajXEFTKqIhJP0MpdhTNR2xKG5kulS-stDZDKTs9g4te-478P7uQFv6Xj3QzQZBr8VP70Uh4PvTvGPHiSX-e7thdo8TmYc0gsBfek2_BuXEUlf9m3EXb1gIzrlhFII25GMZ0Mtw-ZwIQMT46Agt3OQ
    refreshToken: null
    idToken: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik1EaEJRVGRET1RZNU1FWXhOREJCTlRnNU1UUkZRa05ETUVRME1qSXpRelEyTURBNFJFVTVOZyJ9.eyJodHRwczovL3d3dy5zZWN1cmVrZXkuY29tL3RpdGxlIjoiTXIuIiwiaHR0cHM6Ly93d3cuc2VjdXJla2V5LmNvbS9naXZlbl9uYW1lIjoiS2VsdmluIiwiaHR0cHM6Ly93d3cuc2VjdXJla2V5LmNvbS9taWRkbGVfbmFtZSI6Ikx1Y2lmZXIiLCJodHRwczovL3d3dy5zZWN1cmVrZXkuY29tL2ZhbWlseV9uYW1lIjoiTG8iLCJodHRwczovL3d3dy5zZWN1cmVrZXkuY29tL2hvbm9yaWZpYyI6Ik1CQSIsImh0dHBzOi8vd3d3LnNlY3VyZWtleS5jb20vcGhvbmVfbnVtYmVyIjoiNDE2LTk3NC0xMTExIiwiaHR0cHM6Ly93d3cuc2VjdXJla2V5LmNvbS9hZGRyZXNzIjp7InN0cmVldF9hZGRyZXNzIjoiNDEwMSBZb25nZSBTdHJlZXQiLCJsb2NhbGl0eSI6IlRvcm9udG8iLCJyZWdpb24iOiJPTiIsInBvc3RhbF9jb2RlIjoiTTJQIDFONiIsImNvdW50cnkiOiJDQSJ9LCJodHRwczovL3d3dy5zZWN1cmVrZXkuY29tL2JpcnRoZGF0ZSI6IjE5NzQtMDItMjkiLCJodHRwczovL3d3dy5zZWN1cmVrZXkuY29tL2VtYWlsIjoia2VsdmluLmxvQHByb2RpZ3lsYWJzLm5ldCIsImh0dHBzOi8vd3d3LnNlY3VyZWtleS5jb20vY3VzdG9tZXJfcmVmX251bSI6IjdmZTFiNTA1LTQzMGMtNGY1YS04ZTMyLTEwOGVmYjA1NTNmOCIsImh0dHBzOi8vd3d3LnNlY3VyZWtleS5jb20vdmVyaWZpY2F0aW9uX2RhdGUiOiIyMDE4LTAzLTAyIiwiaHR0cHM6Ly93d3cuc2VjdXJla2V5LmNvbS9hY2NvdW50Ijp7InR5cGUiOiJkZXBvc2l0IiwibnVtYmVyIjoiYWNjb3VudE51bS0xIiwiaW5zdGl0dXRpb24iOiJTZWN1cmVCYW5rIiwic3RhdHVzIjoib3BlbiJ9LCJpc3MiOiJodHRwczovL2lkdmVyaWZhY3QuYXV0aDAuY29tLyIsInN1YiI6Imdvb2dsZS1vYXV0aDJ8MTA1OTg4NjkyMTQ4Mzc1ODAyOTc3IiwiYXVkIjoiRDJYVExNS2w1MGxldFFFR2dmbzBMUWNjaXpaQVVDam8iLCJpYXQiOjE1MjM4OTY0NTksImV4cCI6MTUyMzkzMjQ1OX0.WzamKZbxPufh1hHm37tQmWkofkFHS-oAZdMKe37usFksr36DaI7tm1ZdkHSkUgK4pJbMriGBLir8UWGQqIS9Y0_CUOeZ16Jhg3ptBdIjT-RlvU4VsBhIefPlqKrdgWCabGATeBSvwWfKXTYIhNu1jJnI4nv4kkG7srHYgDX6C-kBoL_fgWNJIJtvufBaIAGS4jiI-WNtxW8MF-Hvad1YDiXtN8j2J3-ISVts7G01b_mgPAU2Efu9H_RRK8ugqJ-AcOOU-_ZvM0p6SMBAaeYSlb7UivK25SzyO9LH3SCvzDUhEIy8AMeyLE3c-jUUtY-LCgxRfLmmJ74kADgmvioJDQ
    tokenType: Bearer
    expiresIn: 86400
    }

As you can see, this does include the id_token in the response if I only specify “openid” in the scope during the initial /authorize request.