fastapi
fastapi copied to clipboard
Add JWT token mechanism with fastapi authlib-oauth implementation for external SSO
First Check
- [X] I added a very descriptive title to this issue.
- [X] I used the GitHub search to find a similar issue and didn't find it.
- [X] I searched the FastAPI documentation, with the integrated search.
- [X] I already searched in Google "How to X in FastAPI" and didn't find any information.
- [X] I already read and followed all the tutorial in the docs and didn't find an answer.
- [X] I already checked if it is not related to FastAPI but to Pydantic.
- [X] I already checked if it is not related to FastAPI but to Swagger UI.
- [X] I already checked if it is not related to FastAPI but to ReDoc.
Commit to Help
- [X] I commit to help with one of those options 👆
Example Code
from authlib.integrations.starlette_client import OAuth
oauth = OAuth()
CONF_URL = "https://localhost:8080/.well-known/openid-configuration"
oauth.register(
name="cad",
server_metadata_url=CONF_URL,
client_id=settings.CLIENT_ID,
client_secret=settings.CLIENT_SECRET,
client_kwargs={"scope": "openid email profile authorization_group"},
)
oauth2_scheme = OAuth2PasswordBearer(tokenUrl='/auth')
def create_access_token(*, data: dict, expires_delta: datetime.timedelta = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.datetime.utcnow() + expires_delta
else:
expire = datetime.datetime.utcnow() + datetime.timedelta(minutes=15)
to_encode.update({'exp': expire})
encoded_jwt = jwt.encode(to_encode, "abcd", algorithm="HS256")
return encoded_jwt
def create_token(id):
access_token_expires = datetime.timedelta(minutes=120)
access_token = create_access_token(data={'sub': id}, expires_delta=access_token_expires)
return access_token
@app.middleware("http")
async def authorize(request: Request, call_next):
if not (request.scope["path"].startswith("/login") or request.scope["path"].startswith("/auth")):
if not is_session_okay(request.session):
return RedirectResponse(url="/login")
return await call_next(request)
@app.get("/login")
async def login(request: Request):
redirect_uri = request.url_for("auth")
return await oauth.cad.authorize_redirect(request, redirect_uri)
@app.get("/auth")
async def auth(request: Request):
try:
token = await oauth.cad.authorize_access_token(request)
except OAuthError as error:
return HTMLResponse(f"<h1>{error.error}</h1>")
user = await oauth.cad.parse_id_token(request, token)
access_token = create_token(user['sub'])
return {"access_token": access_token, "token_type": "bearer"}
async def get_current_user(token: str = Depends(oauth2_scheme)):
cred_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail='Could not validate credentials',
headers={'WWW-Authenticate': 'Bearer'},
)
try:
payload = jwt.decode(token, os.getenv("CLIENT_SECRET"), algorithms=["HS256"])
id: str = payload.get('sub')
if id is None:
raise cred_exception
except jwt.JWTError:
raise cred_exception
return id
@app.get("/", tags=["Web-UI"])
def index():
frontend_root = "./ui"
return FileResponse(str(frontend_root) + "/index.html", media_type="text/html")
Description
I have this main.py file where I am configuring authentication with an external-sso or local-oidc-server. I am creating a jwt token here at /auth endpoint. After the token is generated, now I am unable to redirect it to base path("/"). How can I achieve that. If I try to access the / path, i get redirected to auth endpoint with the bearer token displayed.
In the "auth" endpoint after the access token is created, I even tried to do RedirectResponse to "/" like this
response = RedirectResponse(url='/', status_code=status.HTTP_302_FOUND)
response.set_cookie(key='access_token', value=access_token)
return response
but i get into a infinite loop where after sso authentication is successful it again does the same thing.
Console log:
INFO: 127.0.0.1:61886 - "GET /auth?code=fkZa_Z9LrA6paUeHXS02HH_hLCU-y3TVAvQBBADJ&state=2Yi3OwaTweWmCuQtkEeuAEduvxAEET HTTP/1.1" 302 Found
INFO: 127.0.0.1:61886 - "GET / HTTP/1.1" 307 Temporary Redirect
INFO: 127.0.0.1:61886 - "GET /login HTTP/1.1" 302 Found
INFO: 127.0.0.1:61886 - "GET /auth?code=fkZa_Z9LrA6paUeHXS02HH_hLCU-y3TVAvQBBADJ&state=2Yi3OwaTweWmCuQtkEeuAEduvxAEET HTTP/1.1" 302 Found
INFO: 127.0.0.1:61886 - "GET / HTTP/1.1" 307 Temporary Redirect
INFO: 127.0.0.1:61886 - "GET /login HTTP/1.1" 302 Found
...........
Operating System
Windows
Operating System Details
No response
FastAPI Version
0.63.0
Python Version
Python 3.7.11
Additional Context
No response