oauthenticator icon indicating copy to clipboard operation
oauthenticator copied to clipboard

[AzureAD] 0.11.0 returns v1 token while 0.10.0 returns v2 token

Open scivm opened this issue 4 years ago • 6 comments

I tried upgrading from 0.9.0-beta.3 to 0.9.0-beta.4 but had an issue that with the AD token. In .4 I get a v1 token while in .3 was getting the expected v2 token. The v1 token is missing the AD groups which my customization needs.

The oauthenticator code is calling the older v1 endpoint in both the old .10 and new .11 azuread oauthenicator. The older endpoint is still valid though I am not sure it is intentional not to move to the v2 endpoint or make it configurable?

old v1 endpoint:

https://login.microsoftonline.com/{0}/oauth2/authorize
https://login.microsoftonline.com/{0}/oauth2/token

The new v2 urls are:

https://login.microsoftonline.com/{0}/oauth2/v2.0/authorize
https://login.microsoftonline.com/{0}/oauth2/v2.0/token
auth:
    state:
       enabled: true
       cryptoKey: "xxxxx"
    admin:
      users:
        - shamberger michael (ext)
    type: custom
    custom:
      className: "CustomAzureAdOAuthenticator"
      config:
        client_id: "xxxx"
        client_secret: "xxxxx"
        oauth_callback_url: "https://xxxxx.cloudapp.azure.com/hub/oauth_callback"
        tenant_id: "xxxxx"
    authpasstoken: |
      from oauthenticator.azuread import AzureAdOAuthenticator
      from tornado import gen
      class CustomAzureAdOAuthenticator(AzureAdOAuthenticator):
        @gen.coroutine
        def pre_spawn_start(self, user, spawner):
          auth_state = yield user.get_auth_state()
          if not auth_state:
            # user has no auth state
            return
          # define some environment variables from auth_state
          spawner.environment['AD_TOKEN'] = auth_state['access_token']
      c.JupyterHub.authenticator_class = CustomAzureAdOAuthenticator

0.9.0-beta.3

[I 2020-02-28 13:02:17.669 JupyterHub pages:347] shamberger michael (ext) is pending spawn
[I 2020-02-28 13:02:17.670 JupyterHub log:174] 200 GET /hub/spawn-pending/shamberger%20michael%20%28ext%29 (shamberger michael (ext)@10.0.1.4) 4.24ms
[I 2020-02-28 13:02:17.671 JupyterHub spawner:1769] PVC claim-shamberger-20michael-20-28ext-29 already exists, so did not create new pvc.
{'aud': '0baa8e55-94c4-4d64-a40b-7e65c931980f', 'iss': 'https://login.microsoftonline.com/<tenant_id>/v2.0', 'iat': 1582854616, 'nbf': 1585894616, 'exp': 1585898516, 'aio': 'AUVAW/8OAAAASNNPpBCjnF4kyUZ/PKeSzYiDme747DyWcN7NNbkR3UAuqKRn4OZqojTtIulE3cmOBRAhzyxkgbuDnZ2wwgyx8Q==', 'azp': '0bff8e55-94c4-4d64-a40b-7e65c931980f', 'azpacr': '1', **'groups': ['4eb05c45-1cb4-46f2-a87d-b8a4b57d1513', '0a351829-b0b3-4c40-cd58-74fa893920e1', 'b013a1e8-1ef3-45db-a700-0d8ca9b08a6c', '96432116-56fa-4747-980c-c07f3bf34412', 'cf018488-e3fa-434f-b89a-63aa280d5418', '9f7abff4-b90d-4548-943e-378458718374', '1cc41db6-b321-4d87-b85f-1f6ddc98a7c7', '2d70690b-4dbd-42d2-9b8f-cac42fe45812', 'ae90f80e-db97-49a9-9010-e862a0042c28', 'b709cddc-2e3e-4f2b-89b5-3fadf46b0dd9']**, 'idp': 'https://sts.windows.net/cbede638-a3d9-459f-8f4e-24ced73b4e5e/', 'name': 'Shamberger Michael (Ext)', 'oid': 'd274b495-429d-4b4f-9d42-870b9ad6e7a8', 'preferred_username': 'ext.not.real@foocom', 'scp': 'Directory.Read.All User.Read', 'sub': 'pTvhsfZwiosaNE2CwQuI5WUDF4u_HTipnlss5U8Ljs8', 'tid': 'xxx', 'uti': 'acawH6Qt8kigiE4jaL8jAA', 'ver': '2.0', 'wids': ['9b893d92-2cd3-44c7-9d02-a6ac2d5ea5c3']}

0.9.0-beta.4 (missing groups)

[I 2020-02-28 12:57:45.487 JupyterHub pages:347] shamberger michael (ext) is pending spawn
[I 2020-02-28 12:57:45.491 JupyterHub log:174] 200 GET /hub/spawn-pending/shamberger%20michael%20%28ext%29 (shamberger michael (ext)@10.0.1.4) 7.51ms
[I 2020-02-28 12:57:45.491 JupyterHub spawner:1769] PVC claim-shamberger-20michael-20-28ext-29 already exists, so did not create new pvc.
{'aud': '00000002-0000-0000-c000-000000000000', 'iss': 'https://sts.windows.net/<tenant_id>/', 'iat': 1582894045, 'nbf': 1582894045, 'exp': 1582897945, 'acr': '1', 'aio': 'AUQAu/8OCABAQCyIWpRlYdeCTc3mTfx9nmd/fnVZhbJqlcae+9RGUl3bHhjzxifGLXTprHTfqsW+AHl9xOYzoGRQqlQbm11OfQ==', 'altsecid': '5::10032000824A25E9', 'amr': ['pwd'], 'appid': '0bff8e55-94c4-6564-a40b-7e65c931980f', 'appidacr': '1', 'email': '[email protected]', 'idp': 'https://sts.windows.net/cbede638-a3d9-459f-8f4e-24ced73b4e5e/', 'in_corp': 'true', 'ipaddr': 'xxx.xxx.xxx.xxx', 'name': 'Shamberger Michael (Ext)', 'oid': 'd274b495-429d-4b4f-4d42-870b9ad6e7a8', 'puid': '1003200089439CD3', 'scp': 'Directory.Read.All User.Read', 'sub': 'V7S_jAmxhB1XUwOjBjYNwfFJ5ZS74x2LaI53d3oggao', 'tenant_region_scope': 'EU', 'tid': 'xxx', 'unique_name': '[email protected]', 'uti': 'U_Y_fMFGHk6IW7a7ITMrAA', 'ver': '1.0'}	

scivm avatar Feb 28 '20 14:02 scivm

Questions in my mind

  • Should we actively change the default of oauthenticator default to use the /v2.0 endpoint for /token and /authorize?
  • Why did @scvim experience a different response of some kind between oauthenticator 0.10 and 0.11?

consideRatio avatar Feb 29 '20 10:02 consideRatio

I think I'm running into this.

In [165]: decoded.keys()
Out[165]: dict_keys(['aud', 'iss', 'iat', 'nbf', 'exp', 'amr', 'email', 'family_name', 'given_name', 'ipaddr', 'name', 'oid', 'onprem_sid', 'preferred_username', 'rh', 'sub', 'tid', 'unique_name', 'upn', 'ver'])

In [166]: decoded['ver']
Out[166]: '1.0'

In [167]: decoded['iss'].replace(tenant_id, '<tenant_id>')
Out[167]: 'https://sts.windows.net/<tenant_id>/'

I'm running oauthenticator master.

dhirschfeld avatar Apr 07 '21 11:04 dhirschfeld

Naively changing the urls over to v2.0 gives an error about scope:

image

dhirschfeld avatar Apr 07 '21 12:04 dhirschfeld

Continuing on the journey, the below was enough to get it to work:

c.AzureAdOAuthenticator.scope = ['openid']

...after which I could then get the group information:

In [176]: decoded.keys()
Out[176]: dict_keys(['aud', 'iss', 'iat', 'nbf', 'exp', 'aio', 'email', 'groups', 'rh', 'sub', 'tid', 'uti', 'ver'])

In [177]: decoded['ver']
Out[177]: '2.0'

The token is now however missing other configured optional claims such as upn amongst others. The group information is more important to me though.

dhirschfeld avatar Apr 07 '21 12:04 dhirschfeld

Reading the docs it seems this may be configurable in the application manifest:

This is controllable by applications using the accessTokenAcceptedVersion setting in the app manifest, where null and 1 result in v1.0 tokens, and 2 results in v2.0 tokens. ... The Microsoft identity platform supports issuing any token version from any version endpoint - they are not related. This is why a resource setting accessTokenAcceptedVersion to 2 means that a client calling the v1.0 endpoint to get a token for that API will receive a v2.0 access token.

https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens#v10-and-v20

dhirschfeld avatar Apr 07 '21 12:04 dhirschfeld

Just hit a similar issue. I don't think anything had changed on the JupyterHub side, so maybe something changed on the Microsoft side. Not sure.

Trying to log in to the JupyterHub system would produce an error (in the browser):

OAuth error: AADSTS90027: We are unable to issue tokens from this API version on the MSA tenant. Please contact the application vendor as they need to use version 2.0 of the protocol to support this.

Trying to adjust various things on the Microsoft 'App registration' side seemed to have no effect.

Following some of the tips in this thread, I found that pointing to the v2.0 URLs (https://login.microsoftonline.com/{0}/oauth2/v2.0), and adding the configuration setting:

c.AzureAdOAuthenticator.scope = ['openid', 'profile']

returns a token with the attributes:

['aud', 'iss', 'iat', 'nbf', 'exp', 'name', 'oid', 'preferred_username', 'rh', 'sub', 'tid', 'uti', 'ver']

where name will be the full name of the user (e.g. Jane Doe), and the preferred_username is (at least in this situation) username@domain.

The v1 request had previously returned a token with the attributes:

['aud', 'iss', 'iat', 'nbf', 'exp', 'amr', 'family_name', 'given_name', 'ipaddr', 'name', 'oid', 'onprem_sid', 'rh', 'sub', 'tid', 'unique_name', 'upn', 'ver']

where name was the full name and upn contained username@domain.

Hope that's of use to somebody.

cemacrr avatar Apr 14 '21 10:04 cemacrr