TokenExchangeAuthenticator


NameTokenExchangeAuthenticator JSON
Version 0.3.6 PyPI version JSON
download
home_pagehttps://github.com/statisticsnorway/jupyterhub-extensions
SummaryJupyterhub oauth extension
upload_time2024-03-11 13:21:29
maintainer
docs_urlNone
authorStatistics Norway
requires_python
licenseMIT
keywords jupyterhub authenticator statistics norway
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # TokenExchangeAuthenticator for JupyterHub.

This Authenticator can be plugged in and used with JupyterHub. It is built on top of [OAuthenticator](https://github.com/jupyterhub/oauthenticator), and authenticates users using OIDC and retrieves external Identity Provider (IDP) tokens using [token exchange](https://www.keycloak.org/docs/latest/securing_apps/#_token-exchange). This implementation is compatible with Keycloak as an [Identity Broker](https://www.keycloak.org/docs/latest/server_admin/#_identity_broker) and Google as an external IDP (see [Internal Token to External Token Exchange](https://www.keycloak.org/docs/latest/securing_apps/#internal-token-to-external-token-exchange)). 

It also implements a refresh mechanism, ensuring that both the internal access token as well as any external IDP
tokens are updated individually. If the update is not possible, it forces a re-authentication of the user.

## Sequence diagram
The OIDC + token exchange flow may be illustrated like in the following sequence diagram:

![OIDC with token exchange](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/statisticsnorway/jupyterhub-extensions/master/TokenExchangeAuthenticator/token-exchange.puml)

## Installation

```bash
pip install tokenexchangeauthenticator
```

## Usage

In your JupyterHub config file, set the authenticator and configure it:

```python
# Enable the authenticator
c.JupyterHub.authenticator_class = 'tokenexchangeauthenticator.TokenExchangeAuthenticator'
c.TokenExchangeAuthenticator.username_key = 'preferred_username'
c.TokenExchangeAuthenticator.userdata_params = {'state': 'state', 'kc_idp_hint': 'google'}
c.TokenExchangeAuthenticator.logout_redirect_uri = 'https://my.domain.com/logout'
c.TokenExchangeAuthenticator.oauth_callback_url = 'https://my.domain.com/oauth_callback'

# Specify the issuer url, to get all the endpoints automatically from .well-known/openid-configuration
c.TokenExchangeAuthenticator.oidc_issuer = 'https://my.keycloak.com/auth/realms/myrealm'

# If you need to set a different scope, like adding the offline option for longer lived refresh token
c.TokenExchangeAuthenticator.scope = ['openid', 'email', 'offline_access']
# Request access tokens for other services by passing their id's (this uses the token exchange mechanism)
c.TokenExchangeAuthenticator.exchange_tokens = ['google']
```

#### Note on Google's authorization server
Google's authorization server only provideds the `refresh_token` in the response to the initial login request.
Hence, the Identity Broker (e.g. Keycloak) will only get the refresh token on the first login so that subsequent token 
refresh may stop working (see [issue on stack overflow](https://stackoverflow.com/questions/62700314/keycloak-only-gets-google-refresh-token-on-first-login)).
This can be remedied by prompting for re-consent at every login like this:

```python
# This will force the retrieval of a refresh_token on every login
c.TokenExchangeAuthenticator.extra_authorize_params = {'prompt': 'consent'}
```

It's also necessary to configure the client ID and secret. This may be set directly like this:
```python
# This will force the retrieval of a refresh_token on every login
c.TokenExchangeAuthenticator.client_id = 'client-id'
c.TokenExchangeAuthenticator.client_secret = 'secret'
```

Or by setting the following environment
variables:

```bash
OAUTH_CLIENT_ID=client_id
OAUTH_CLIENT_SECRET=client_secret
```

#### Expose the user's tokens

The user's tokens are stored using Jupyterhub's [authentication state](https://jupyterhub.readthedocs.io/en/stable/reference/authenticators.html#authentication-state). 
These can optionally be exposed at a custom path which will only be accessible inside the user's single-user notebook. 
The path can be customised by setting:
```python
# If set, exposes the user's access token(s) at this relative path
c.TokenExchangeAuthenticator.local_user_exposed_path = '/my-custom-path/userinfo'
```

## Running tests
To run the tests locally:

```
$ pip install --upgrade --pre -r test-requirements.txt
```

```
$ pytest -v ./tokenexchangeauthenticator/tests/
```
Or you run a specific test file with:

```
$ pytest -v ./tokenexchangeauthenticator/tests/<test-file-name>
```



            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/statisticsnorway/jupyterhub-extensions",
    "name": "TokenExchangeAuthenticator",
    "maintainer": "",
    "docs_url": null,
    "requires_python": "",
    "maintainer_email": "",
    "keywords": "JupyterHub,Authenticator,Statistics Norway",
    "author": "Statistics Norway",
    "author_email": "bjorn.skaar@ssb.no",
    "download_url": "https://files.pythonhosted.org/packages/a5/04/d7defc37974feb36fc327e33187b6df277f57419e0653838a02a3ab8c8eb/TokenExchangeAuthenticator-0.3.6.tar.gz",
    "platform": null,
    "description": "# TokenExchangeAuthenticator for JupyterHub.\n\nThis Authenticator can be plugged in and used with JupyterHub. It is built on top of [OAuthenticator](https://github.com/jupyterhub/oauthenticator), and authenticates users using OIDC and retrieves external Identity Provider (IDP) tokens using [token exchange](https://www.keycloak.org/docs/latest/securing_apps/#_token-exchange). This implementation is compatible with Keycloak as an [Identity Broker](https://www.keycloak.org/docs/latest/server_admin/#_identity_broker) and Google as an external IDP (see [Internal Token to External Token Exchange](https://www.keycloak.org/docs/latest/securing_apps/#internal-token-to-external-token-exchange)). \n\nIt also implements a refresh mechanism, ensuring that both the internal access token as well as any external IDP\ntokens are updated individually. If the update is not possible, it forces a re-authentication of the user.\n\n## Sequence diagram\nThe OIDC + token exchange flow may be illustrated like in the following sequence diagram:\n\n![OIDC with token exchange](http://www.plantuml.com/plantuml/proxy?cache=no&src=https://raw.githubusercontent.com/statisticsnorway/jupyterhub-extensions/master/TokenExchangeAuthenticator/token-exchange.puml)\n\n## Installation\n\n```bash\npip install tokenexchangeauthenticator\n```\n\n## Usage\n\nIn your JupyterHub config file, set the authenticator and configure it:\n\n```python\n# Enable the authenticator\nc.JupyterHub.authenticator_class = 'tokenexchangeauthenticator.TokenExchangeAuthenticator'\nc.TokenExchangeAuthenticator.username_key = 'preferred_username'\nc.TokenExchangeAuthenticator.userdata_params = {'state': 'state', 'kc_idp_hint': 'google'}\nc.TokenExchangeAuthenticator.logout_redirect_uri = 'https://my.domain.com/logout'\nc.TokenExchangeAuthenticator.oauth_callback_url = 'https://my.domain.com/oauth_callback'\n\n# Specify the issuer url, to get all the endpoints automatically from .well-known/openid-configuration\nc.TokenExchangeAuthenticator.oidc_issuer = 'https://my.keycloak.com/auth/realms/myrealm'\n\n# If you need to set a different scope, like adding the offline option for longer lived refresh token\nc.TokenExchangeAuthenticator.scope = ['openid', 'email', 'offline_access']\n# Request access tokens for other services by passing their id's (this uses the token exchange mechanism)\nc.TokenExchangeAuthenticator.exchange_tokens = ['google']\n```\n\n#### Note on Google's authorization server\nGoogle's authorization server only provideds the `refresh_token` in the response to the initial login request.\nHence, the Identity Broker (e.g. Keycloak) will only get the refresh token on the first login so that subsequent token \nrefresh may stop working (see [issue on stack overflow](https://stackoverflow.com/questions/62700314/keycloak-only-gets-google-refresh-token-on-first-login)).\nThis can be remedied by prompting for re-consent at every login like this:\n\n```python\n# This will force the retrieval of a refresh_token on every login\nc.TokenExchangeAuthenticator.extra_authorize_params = {'prompt': 'consent'}\n```\n\nIt's also necessary to configure the client ID and secret. This may be set directly like this:\n```python\n# This will force the retrieval of a refresh_token on every login\nc.TokenExchangeAuthenticator.client_id = 'client-id'\nc.TokenExchangeAuthenticator.client_secret = 'secret'\n```\n\nOr by setting the following environment\nvariables:\n\n```bash\nOAUTH_CLIENT_ID=client_id\nOAUTH_CLIENT_SECRET=client_secret\n```\n\n#### Expose the user's tokens\n\nThe user's tokens are stored using Jupyterhub's [authentication state](https://jupyterhub.readthedocs.io/en/stable/reference/authenticators.html#authentication-state). \nThese can optionally be exposed at a custom path which will only be accessible inside the user's single-user notebook. \nThe path can be customised by setting:\n```python\n# If set, exposes the user's access token(s) at this relative path\nc.TokenExchangeAuthenticator.local_user_exposed_path = '/my-custom-path/userinfo'\n```\n\n## Running tests\nTo run the tests locally:\n\n```\n$ pip install --upgrade --pre -r test-requirements.txt\n```\n\n```\n$ pytest -v ./tokenexchangeauthenticator/tests/\n```\nOr you run a specific test file with:\n\n```\n$ pytest -v ./tokenexchangeauthenticator/tests/<test-file-name>\n```\n\n\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Jupyterhub oauth extension",
    "version": "0.3.6",
    "project_urls": {
        "Homepage": "https://github.com/statisticsnorway/jupyterhub-extensions"
    },
    "split_keywords": [
        "jupyterhub",
        "authenticator",
        "statistics norway"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "59560c4b0075d79be133b2fb27785f6b57d7be01ac62c95b116898bbb566c2b0",
                "md5": "7206d06e5bc19a716487341d2cbd5f13",
                "sha256": "d6771a97ae85eaab65ecf82aa85f4dbd632ba5389cafd795f841fc33d8d1fe90"
            },
            "downloads": -1,
            "filename": "TokenExchangeAuthenticator-0.3.6-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "7206d06e5bc19a716487341d2cbd5f13",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 13552,
            "upload_time": "2024-03-11T13:21:26",
            "upload_time_iso_8601": "2024-03-11T13:21:26.856325Z",
            "url": "https://files.pythonhosted.org/packages/59/56/0c4b0075d79be133b2fb27785f6b57d7be01ac62c95b116898bbb566c2b0/TokenExchangeAuthenticator-0.3.6-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a504d7defc37974feb36fc327e33187b6df277f57419e0653838a02a3ab8c8eb",
                "md5": "4343d7e80b7c22931b4b00d3da29f33a",
                "sha256": "024806b2cce43683d4496464ef454c130b050522ed8d3ccc31e6e2d780c3b59e"
            },
            "downloads": -1,
            "filename": "TokenExchangeAuthenticator-0.3.6.tar.gz",
            "has_sig": false,
            "md5_digest": "4343d7e80b7c22931b4b00d3da29f33a",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 11435,
            "upload_time": "2024-03-11T13:21:29",
            "upload_time_iso_8601": "2024-03-11T13:21:29.862821Z",
            "url": "https://files.pythonhosted.org/packages/a5/04/d7defc37974feb36fc327e33187b6df277f57419e0653838a02a3ab8c8eb/TokenExchangeAuthenticator-0.3.6.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-03-11 13:21:29",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "statisticsnorway",
    "github_project": "jupyterhub-extensions",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "tokenexchangeauthenticator"
}
        
Elapsed time: 0.38194s