Skip to content

Commit

Permalink
Use GenericOAuthenticator to support Auth0
Browse files Browse the repository at this point in the history
The Auth0 authenticator made the EarthScope authentication mechanism
look more custom than it really was - it's really just standard
auth0, and if you look at the [auth0 authenticator](https://github.com/jupyterhub/oauthenticator/blob/main/oauthenticator/auth0.py)
code it's fairly minimal - just a couple of convenience functions.
This PR switches that (+ our auth0 documentation) to simply use
GenericOAuthenticator. This has the following advantages:

1. The Auth0 documentation we have can be easily ported to just
   support any Generic OAuth provider if needed in the future.
2. The GenericOAuthenticator has features the Auth0 one does not -
   particularly around groups management that we do want to use.
   While eventually I think this should be made available to all
   authenticators (and will work with upstream in doing so), moving
   to GenericOAuthenticator unblocks planning & scheduling engineering work here
   as soon as 2i2c-org#3818
   is merged.
3. It signals that the EarthScope hub is not *that* special, just the
   first as a way for us to develop and offer new features. We should
   work on structuring how we do this, and signal when features are
   available in what hubs. But in the meantime, this reduces the overall
   apparent complexity to match actual complexity
4. Removes the custom logout_url work, and instead just mentions you need
   to set the `client_id` in the logout_url, and adds that to our auth0
   documentation. This removes more custom code we have for EarthScope.
   Once jupyterhub/oauthenticator#719 is merged,
   this helps us remove all custom code here from earthscope. This
   part fixes 2i2c-org#3715

This was triggered as cleanup by https://2i2c.freshdesk.com/a/tickets/1453.
I'll create appropriate issues for next steps, and will prioritize any
future work accordingly with our engineering processes.
  • Loading branch information
yuvipanda committed Apr 30, 2024
1 parent 6617eae commit f6eb5dd
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 56 deletions.
36 changes: 7 additions & 29 deletions config/clusters/earthscope/common.values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ basehub:
hub:
extraConfig:
001-username-claim: |
from oauthenticator.auth0 import Auth0OAuthenticator
from oauthenticator.generic import GenericOAuthenticator
from traitlets import List, Unicode, default
from urllib.parse import urlencode
class CustomAuth0OAuthenticator(Auth0OAuthenticator):
class CustomGenericOAuthenticator(GenericOAuthenticator):
# required_scopes functionality comes in from https://github.com/jupyterhub/oauthenticator/pull/719
# Can be removed from here once that PR is merged
required_scopes = List(
Expand All @@ -62,28 +62,6 @@ basehub:
""",
)
# Upstreamed at https://github.com/jupyterhub/oauthenticator/pull/722
logout_redirect_to_url = Unicode(
config=True,
help="""
Redirect to this URL after the user is logged out.
Must be explicitly added to the "Allowed Logout URLs" in the configuration
for this Auth0 application. See https://auth0.com/docs/authenticate/login/logout/redirect-users-after-logout
for more information.
"""
)
@default("logout_redirect_url")
def _logout_redirect_url_default(self):
url = f"https://{self.auth0_domain}/v2/logout"
if self.logout_redirect_to_url:
# If a redirectTo is set, we must also include the `client_id`
# Auth0 expects `client_id` to be snake cased while `redirectTo` is camel cased
params = urlencode({"client_id": self.client_id, "redirectTo": self.logout_redirect_to_url})
url = f"{url}?{params}"
return url
async def check_allowed(self, username, auth_model):
if await super().check_allowed(username, auth_model):
return True
Expand Down Expand Up @@ -112,15 +90,15 @@ basehub:
c.Spawner.auth_state_hook = populate_token
c.JupyterHub.authenticator_class = CustomAuth0OAuthenticator
c.JupyterHub.authenticator_class = CustomGenericOAuthenticator
config:
JupyterHub:
authenticator_class: auth0
CustomAuth0OAuthenticator:
# JupyterHub:
# authenticator_class: auth0
CustomGenericOAuthenticator:
required_scopes:
# This allows EarthScope to control who can login to the hub
- geolab
Auth0OAuthenticator:
GenericOAuthenticator:
scope:
- openid
# This gives us refresh token
Expand Down
12 changes: 6 additions & 6 deletions config/clusters/earthscope/enc-prod.secret.values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ basehub:
jupyterhub:
hub:
config:
Auth0OAuthenticator:
client_id: ENC[AES256_GCM,data:qn8Xel6vzFKHuL7gP8aGKQr3C7AGORQ7sCyNvKulbDE=,iv:bWYt/w31HcaEDjUBW3DZv/Lb4Ny/BPEjoBTsjp0XP6g=,tag:/02E1lYfhfOMcd+P2+DV8Q==,type:str]
client_secret: ENC[AES256_GCM,data:qry2vIkYLTRd7rlg6RTO6pB+e4SP5mvzClqagyJbbzXYkdeiGQccVFsERQ15RT/BRDX/PX4Bj5ZxcuCY9wGsxw==,iv:k763ow53AuqWG7dSyqkaosa9O4NwufRnmmORRxssGQA=,tag:MX4zqeOS3vdxhhYUljipJA==,type:str]
GenericOAuthenticator:
client_id: ENC[AES256_GCM,data:+ctWM1MpyksEjMLTnVZAw+N0Wv6ZNXL+fHdeamt64Ow=,iv:1KBoaNQTaUmyAt1wAO9pmvOkoLCl+B2eCBIu3SsRKYA=,tag:sK3EmbdSJP3UldX51274xA==,type:str]
client_secret: ENC[AES256_GCM,data:UtnmnF84dQ50h741JNvLmfBkzoI6ui16YVV8tRh1GXyCIrcu5bgy5StRIIjw3uRVo7g7bFFdGN6lIeQIUVd+Pw==,iv:arvQ5RbKiHFFNdyksmIA9UVoHWRdXgeKAhDGPPE7qrU=,tag:2NdZqXXBpEIUhrA3oTnKPw==,type:str]
sops:
kms: []
gcp_kms:
Expand All @@ -14,8 +14,8 @@ sops:
azure_kv: []
hc_vault: []
age: []
lastmodified: "2024-02-01T19:48:36Z"
mac: ENC[AES256_GCM,data:Cw3rTUGqQlymWXXu/Z7qLSAIlULn5B3SAPxbzkeBDCFSO8u4fhuZXEjoEBvFFdujdEtU9Q7bASKRyl4aveZDJ+aZHboKNDV77d7atONojcEFj/DIy2ELQriMwyq1hx5hZS/onGgt8XLmcjXDJdMH6zEZOYrZl93uTuoS+Qt+4GI=,iv:oCWMr+17mgo+P1btrLglokBO3yYZ9JpZBTx36Vhtb3s=,tag:GEN4T0/9KQzz4+oTxvhbBQ==,type:str]
lastmodified: "2024-03-29T23:50:57Z"
mac: ENC[AES256_GCM,data:h9pUWffgf8vBqG4timmCMharFGj1jdP8iSaaczx1GfzouUG+hhlG82OQTFVSmLwhHkzlmxJxw+t7gi6Zwx9nNgVVfnwa4Qhw6V/XWrBRr8gre2I9+MuXXeYOcjiDqIyasF0TYxGW/kvLZ6+khGvi4iIhnk9rJOk/LpFhpj7IthQ=,iv:1yIFhnW1Mv+d5bBKFGgpMDCCt5zPGfP9YekGey4KF/g=,tag:YgXBpr03lP5q9y1sJ2CsUA==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.7.3
version: 3.8.1
12 changes: 6 additions & 6 deletions config/clusters/earthscope/enc-staging.secret.values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ basehub:
jupyterhub:
hub:
config:
Auth0OAuthenticator:
client_id: ENC[AES256_GCM,data:urLrYypX6IUSVpqFAumEAi9aGJKyQv8oQuNqw5HNhKo=,iv:sQcq2R5wbS2P00nygxPQ3p2LdAsxkRQrk4jvnMWAjQg=,tag:eosLpXx6vWQMNjIxDcsC7Q==,type:str]
client_secret: ENC[AES256_GCM,data:PrphM7gVSfUOInO008VgfhNU4r1+I4oLRT+ypJv5848Bvy1nN+ARzTrPgu7Q3KIiCaXyfNd3Xv6ieb0lsKCLZw==,iv:Vnbo4jG0sARtOL28GxgGAKKITQb5Tx6/TNscWUNgkJU=,tag:RgxhqVzdfsmwMTTEMCn2Zw==,type:str]
GenericOAuthenticator:
client_id: ENC[AES256_GCM,data:Rpa6XhJLmHBkccOZM58T0IwcviJvc2+jbLbL3LDQxgI=,iv:57//hbKbkT8PDa1kanOoS4wlWLvc1hp8fyGgMMaUKzk=,tag:zyv29aa/M7cqar2izZDRTg==,type:str]
client_secret: ENC[AES256_GCM,data:w7feSVDwFN0mbxvLH1DEpw/eanx5+vJXZ7JPSTkVxIAm0aZod4H7lhlEy/gmMgPUJfBF32tXPrrYh6Z5E83oIQ==,iv:RQt6NCiDwAwn15XGxF7T+DVdYck0kw/hKEV9ULgxY1k=,tag:BRnwMh76yaMF7RyKPhBd/g==,type:str]
sops:
kms: []
gcp_kms:
Expand All @@ -14,8 +14,8 @@ sops:
azure_kv: []
hc_vault: []
age: []
lastmodified: "2024-02-01T19:48:31Z"
mac: ENC[AES256_GCM,data:ZYVgv+u0FD+jxYtgyITNLXr5bHNEEkkXtTM0SJGv8txbAVM4yt1k9CF95iVevPsRGG2yztY5vTDQaFGeg0tLGmG55fuuliZhMrB9RsDkmM3qEibVgQQTQZI5ZUciWHSBGm/NCMKnj6ujIx0h3E3cjtZBESIpONH+66kbuGhAlMo=,iv:ORwgid7PCff05bxWN9FuWNCN+wLY+bVZi0GfGDZwQj4=,tag:O+Bkh0UbeDzmAvYWJ5PpKQ==,type:str]
lastmodified: "2024-03-29T23:43:10Z"
mac: ENC[AES256_GCM,data:OnvUNbNHox7iF98w1aJSnrFJ1C3FSD+dz/l7ZK1z5uBnJAyhX3FhVoDGmA3TWAtS5U+ebiz8RbbVjJ/ge687ke2dL/Lnd9Ueay2tsF4ac1BYF6i5LqqsHqzaPwkrRVazB1aRgKx/O37Plm8KuAg2o9dN8jGtjnnSlbIxgJuJIUQ=,iv:Eo0iQ6qrbbcUkPFHzBwuMBGi1fCYnVWLPkkn9GQfrig=,tag:iUbwcWtat7qkfNE5i5MU6A==,type:str]
pgp: []
unencrypted_suffix: _unencrypted
version: 3.7.3
version: 3.8.1
9 changes: 5 additions & 4 deletions config/clusters/earthscope/prod.values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ basehub:
name: "EarthScope"
hub:
config:
CustomAuth0OAuthenticator:
logout_redirect_to_url: https://geolab.earthscope.cloud
Auth0OAuthenticator:
auth0_domain: login.earthscope.org
GenericOAuthenticator:
token_url: https://login.earthscope.org/oauth/token
authorize_url: https://login.earthscope.org/authorize
userdata_url: https://login.earthscope.org/userinfo
logout_redirect_url: https://login.earthscope.org/v2/logout?client_id=2PbhUTbRU6e7uIaaEZIShotx15MbvsJJ
extra_authorize_params:
# This isn't an actual URL, just a string. Must not have a trailing slash
audience: https://api.earthscope.org
9 changes: 5 additions & 4 deletions config/clusters/earthscope/staging.values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ basehub:
name: "EarthScope staging"
hub:
config:
CustomAuth0OAuthenticator:
logout_redirect_to_url: https://staging.geolab.earthscope.cloud
Auth0OAuthenticator:
auth0_domain: login-dev.earthscope.org
GenericOAuthenticator:
token_url: https://login-dev.earthscope.org/oauth/token
authorize_url: https://login-dev.earthscope.org/authorize
userdata_url: https://login-dev.earthscope.org/userinfo
logout_redirect_url: https://login-dev.earthscope.org/v2/logout?client_id=Kn6kSKtw9TqgrSrEmDS0rlBM7Sc69BkL
extra_authorize_params:
# This isn't an actual URL, just a string. Must not have a trailing slash
audience: https://api.dev.earthscope.org
24 changes: 17 additions & 7 deletions docs/hub-deployment-guide/configure-auth/auth0.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,20 @@ administer. Solutions (potentially a shared account) are being explored.

## Configuring the JupyterHub to use Auth0

We will use the upstream [Auth0OAuthenticator](https://github.com/jupyterhub/oauthenticator/blob/main/oauthenticator/auth0.py)
to allow folks to login to JupyterHub.
While there is an upstream [Auth0OAuthenticator](https://github.com/jupyterhub/oauthenticator/blob/main/oauthenticator/auth0.py),
it doesn't have any specific features that aren't in the upstream [GenericOAuthenticator](https://oauthenticator.readthedocs.io/en/latest/reference/api/gen/oauthenticator.generic.html),
and is missing some features that are present in the GenericOAuthenticator. Using the GenericOAuthenticator
here also allows us to support other Generic OAuth providers in the future, and not tie ourselves down
to Auth0.

In the `common.yaml` file for the cluster hosting the hubs, we set the authenticator to be `auth0`.
In the `common.yaml` file for the cluster hosting the hubs, we set the authenticator to be `generic`.

```yaml
jupyterhub:
hub:
config:
JupyterHub:
authenticator_class: auth0
authenticator_class: generic
```
In the encrypted, per-hub config (of form `enc-<hub-name>.secret.values.yaml`), we specify the secret values
Expand All @@ -63,9 +66,10 @@ we received from the community.
jupyterhub:
hub:
config:
Auth0OAuthenticator:
GenericOAuthenticator:
client_id: <client-id>
client_secret: <client-secret>
logout_redirect_url: https://<auth0-domain>/v2/logout?client_id=<client-id>
```

And in the *unencrypted*, per-hub config (of form `<hub-name>.values.yaml`), we specify the non-secret
Expand All @@ -75,12 +79,18 @@ config values.
jupyterhub:
hub:
config:
Auth0OAuthenticator:
auth0_domain: <auth0-domain>
GenericOAuthenticator:
token_url: https://<auth0-domain>/oauth/token
authorize_url: https://<auth0-domain>/authorize
userdata_url: https://<auth0-domain>/userinfo
scope: openid
username_claim: sub
```

Auth0 has documentation for the [userinfo](https://auth0.com/docs/api/authentication#get-user-info),
[token](https://auth0.com/docs/api/authentication#authenticate-user) and [authorize](https://auth0.com/docs/api/authentication#social)
endpoints.

Once deployed, this should allow users authorized by Auth0 to login to the hub! Their usernames will
look like `<auth-provider>:<id>`, which looks a little strange but allows differentiation between
people who use multiple accounts but the same email.

0 comments on commit f6eb5dd

Please sign in to comment.