Monday 31 May 2021

Refresh token using FastAPI and Swagger

I am trying to create an API for our organization using FastAPI. It has a KeyCloak server that is used for all authentication, and OpenID Connect and JWTs is the way that is considered best practise.

In the simplest case, someone else takes care of acquiring a valid JWT token so that FastAPI then can simply decode and read the user and permissions.

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

async def get_current_user(token: str = Depends(oauth2_scheme)):

    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )

    try:
        jwt_token = jwt.decode(token, key=env.keycloak_server_public_key, audience='myorg')
        return jwt_token['preferred_username']
    except jwt.exceptions.ExpiredSignatureError:
        raise credentials_exception

Life is simple!

I do, however, want to try to let users explore the API using the Swagger page. I have created this function that lets users login using the UI:

@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    login_request = requests.post(
        "https://mygreatorg.com/auth/realms/master/protocol/openid-connect/token",
        data={
            "grant_type": "password",
            "username": form_data.username,
            "password": form_data.password,
            "client_id": "fastapi-application",
        },
    )
    raw_response = json.loads(login_request.content.decode('utf-8'))
    raw_response['acquire_time'] = time.time()

    TOKEN_CACHE[form_data.username] = raw_response

    return {"access_token": raw_response['access_token'], "token_type": "bearer"}

This works fine. The auth header in Swagger is now the token, and it validates, for about a minute. The expire time for the tokens are set to a very short time. One is then expected to refresh them using the refresh_token provided in the raw_response payload.

I can very easily make another request to get a new valid access token given the refresh_token. But I am unable to get Swagger to change the token of the request in the UI. The only way I find is to logout and login again, but users will be very annoyed if they are only allows a minute without being kicked out.

One workaround would be to simply cache the token and ignore the expire time and let the user be logged in for a while longer, but that defeats the purpose of the entire security setup and feels like a bad idea.

Any ideas on how to let the UI of FastAPI update the bearer token when its need a refresh, without letting the user login again?



from Refresh token using FastAPI and Swagger

No comments:

Post a Comment