Skip to content

Commit

Permalink
An attempt to wrap the API with JWT
Browse files Browse the repository at this point in the history
Lots of hardcoded stuff and assumptions, but works as expected. It can
be run as a service with a config like this
```
c.JupyterHub.load_roles = [
    {
        "name": "jwt",
        "scopes": [
            "read:users:activity",  # read user last_activity
            "servers",  # start and stop servers
            "admin:users",  # needed if culling idle users as well
        ],
    },
    {
        "name": "user",
        "scopes": ["access:services", "self"],
    },
]

c.JupyterHub.services = [
    {
        "name": "jwt",
        "url": "http://localhost:1984/",
        # any secret >8 characters, you'll use api_token to
        # authenticate api requests to the hub from your service
        "api_token": "super-secret",
    }
]
```

and then start the service:
```
fastapi dev --port 1984 api_wrapper.py
```
  • Loading branch information
enolfc committed Jun 10, 2024
1 parent c3dfca2 commit 6c7716b
Showing 1 changed file with 43 additions and 0 deletions.
43 changes: 43 additions & 0 deletions egi_notebooks_hub/services/api_wrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from typing import Union

from fastapi import FastAPI, Request
import httpx

app = FastAPI()

AUTH_HEADER = "Authorization"
TOKEN_TYPE = "bearer"
URL = "http://localhost:8000/hub/jwt_login"
API_URL = "http://localhost:8000/hub/api"
PREFIX = "services/jwt"


async def request(client):
response = await client.get(URL)
return response.text


# this could be way more generic and moved to a base function and then
# a thin wrapper for get/put/post/etc...
@app.get("/{svc_path:path}")
async def api_wrapper(request: Request, svc_path: str):
user_token = {}
if AUTH_HEADER in request.headers:
f = request.headers[AUTH_HEADER].split()
if len(f) == 2 and f[0].lower() == TOKEN_TYPE:
async with httpx.AsyncClient() as client:
r = await client.get(
URL, headers={AUTH_HEADER: request.headers[AUTH_HEADER]}
)
# errors should kick me out
user_token = r.json()
# assume we have the user_token here
api_path = svc_path.removeprefix(PREFIX)
print(svc_path)
print(api_path)
async with httpx.AsyncClient() as client:
# which headers do we need to preserve?
headers = {"Authorization": f"token {user_token['token']}"}
r = await client.get(API_URL + api_path, headers=headers)
# is this a correct assumption?
return r.json()

0 comments on commit 6c7716b

Please sign in to comment.