1- import json
2-
31import jwt
42from fastapi import APIRouter , Body , HTTPException
5- from jwt .algorithms import RSAAlgorithm
63
74from ..config import settings
85from ..db .core import init_user_data
9- from ..deps import SessionDep , get_oidc_client
6+ from ..deps import SessionDep
107from ..models .models import AuthParams , LoginRegisterModel , Token , User
11- from ..security import (create_access_token , create_tokens , hash_password ,
12- verify_password )
13- from ..utils .utils import generate_filename , httpx_get
8+ from ..security import (create_access_token , create_tokens , get_oidc_client ,
9+ get_oidc_config , hash_password , verify_password )
10+ from ..utils .utils import generate_filename
1411
1512router = APIRouter (prefix = "/api/auth" , tags = ["auth" ])
1613
@@ -20,21 +17,26 @@ async def auth_params() -> AuthParams:
2017 data = {"oidc" : None , "register_enabled" : settings .REGISTER_ENABLE }
2118
2219 if settings .OIDC_HOST and settings .OIDC_CLIENT_ID and settings .OIDC_CLIENT_SECRET :
23- oidc_complete_url = f"{ settings .OIDC_PROTOCOL } ://{ settings .OIDC_HOST } /realms/{ settings .OIDC_REALM } /protocol/openid-connect/auth?client_id={ settings .OIDC_CLIENT_ID } &redirect_uri={ settings .OIDC_REDIRECT_URI } &response_type=code&scope=openid"
24- data ["oidc" ] = oidc_complete_url
20+ oidc_config = await get_oidc_config ()
21+ auth_endpoint = oidc_config .get ("authorization_endpoint" )
22+ data ["oidc" ] = (
23+ f"{ auth_endpoint } ?client_id={ settings .OIDC_CLIENT_ID } &redirect_uri={ settings .OIDC_REDIRECT_URI } &response_type=code&scope=openid"
24+ )
2525
2626 return data
2727
2828
2929@router .post ("/oidc/login" , response_model = Token )
3030async def oidc_login (session : SessionDep , code : str = Body (..., embed = True )) -> Token :
31- if settings .AUTH_METHOD != "oidc" :
32- raise HTTPException (status_code = 400 , detail = "Bad request " )
31+ if not ( settings .OIDC_HOST or settings . OIDC_CLIENT_ID or settings . OIDC_CLIENT_SECRET ) :
32+ raise HTTPException (status_code = 400 , detail = "Partial OIDC config " )
3333
34+ oidc_config = await get_oidc_config ()
35+ token_endpoint = oidc_config .get ("token_endpoint" )
3436 try :
3537 oidc_client = get_oidc_client ()
3638 token = oidc_client .fetch_token (
37- f" { settings . OIDC_PROTOCOL } :// { settings . OIDC_HOST } /realms/ { settings . OIDC_REALM } /protocol/openid-connect/token" ,
39+ token_endpoint ,
3840 grant_type = "authorization_code" ,
3941 code = code ,
4042 )
@@ -49,37 +51,35 @@ async def oidc_login(session: SessionDep, code: str = Body(..., embed=True)) ->
4951 decoded = jwt .decode (
5052 id_token ,
5153 settings .OIDC_CLIENT_SECRET ,
52- algorithms = alg ,
54+ algorithms = [ "HS256" ] ,
5355 audience = settings .OIDC_CLIENT_ID ,
5456 )
5557 case "RS256" :
56- config = await httpx_get (
57- f"{ settings .OIDC_PROTOCOL } ://{ settings .OIDC_HOST } /realms/{ settings .OIDC_REALM } /.well-known/openid-configuration"
58- )
59- jwks_uri = config .get ("jwks_uri" )
60- jwks = await httpx_get (jwks_uri )
61- keys = jwks .get ("keys" )
62-
63- for key in keys :
64- try :
65- pk = RSAAlgorithm .from_jwk (json .dumps (key ))
66- decoded = jwt .decode (
67- id_token ,
68- key = pk ,
69- algorithms = alg ,
70- audience = settings .OIDC_CLIENT_ID ,
71- issuer = f"{ settings .OIDC_PROTOCOL } ://{ settings .OIDC_HOST } /realms/{ settings .OIDC_REALM } " ,
72- )
73- break
74- except Exception :
75- continue
58+ jwks_uri = oidc_config .get ("jwks_uri" )
59+ issuer = oidc_config .get ("issuer" )
60+ jwks_client = jwt .PyJWKClient (jwks_uri )
61+
62+ try :
63+ signing_key = jwks_client .get_signing_key_from_jwt (id_token )
64+ decoded = jwt .decode (
65+ id_token ,
66+ key = signing_key .key ,
67+ algorithms = ["RS256" ],
68+ audience = settings .OIDC_CLIENT_ID ,
69+ issuer = issuer ,
70+ )
71+ except Exception :
72+ raise HTTPException (status_code = 401 , detail = "Invalid ID token" )
7673 case _:
7774 raise HTTPException (status_code = 500 , detail = "OIDC login failed, algorithm not handled" )
7875
7976 if not decoded :
8077 raise HTTPException (status_code = 401 , detail = "Invalid ID token" )
8178
8279 username = decoded .get ("preferred_username" )
80+ if not username :
81+ raise HTTPException (status_code = 401 , detail = "OIDC login failed, preferred_username missing" )
82+
8383 user = session .get (User , username )
8484 if not user :
8585 # TODO: password is non-null, we must init the pw with something, the model is not made for OIDC
0 commit comments