1414
1515from cryptography .hazmat .primitives import serialization
1616from cryptography .hazmat .primitives .asymmetric import rsa
17- from fastapi import FastAPI , Form , HTTPException
17+ from fastapi import FastAPI , Form , HTTPException , Request
1818from fastapi .middleware .cors import CORSMiddleware
1919from fastapi .responses import JSONResponse , RedirectResponse
20+ from fastapi .templating import Jinja2Templates
2021from jose import jwt
2122
2223app = FastAPI ()
2324
25+ # Configure templates
26+ templates = Jinja2Templates (directory = str (Path (__file__ ).parent / "templates" ))
27+
2428# Configure CORS
2529app .add_middleware (
2630 CORSMiddleware ,
3943 "REDIRECT_URI" , "http://localhost:8000/docs/oauth2-redirect"
4044)
4145ISSUER = os .environ .get ("ISSUER" , "http://localhost:3000" )
42- SCOPES = os .environ .get ("SCOPES" , "" )
46+ AVAILABLE_SCOPES = os .environ .get ("SCOPES" , "" )
4347KEY_ID = "1"
4448
4549
@@ -104,6 +108,7 @@ def int_to_base64url(value):
104108authorization_codes = {}
105109pkce_challenges = {}
106110access_tokens = {}
111+ auth_requests = {}
107112
108113# Mock client registry
109114CLIENT_REGISTRY = {
@@ -125,7 +130,7 @@ async def root():
125130@app .get ("/.well-known/openid-configuration" )
126131async def openid_configuration ():
127132 """Return OpenID Connect configuration."""
128- scopes_set = set (["openid" , "profile" , * SCOPES .split ("," )])
133+ scopes_set = set (["openid" , "profile" , * AVAILABLE_SCOPES .split ("," )])
129134 return {
130135 "issuer" : ISSUER ,
131136 "authorization_endpoint" : f"{ ISSUER } /authorize" ,
@@ -149,6 +154,7 @@ async def jwks():
149154
150155@app .get ("/authorize" )
151156async def authorize (
157+ request : Request ,
152158 response_type : str ,
153159 client_id : str ,
154160 redirect_uri : str ,
@@ -174,23 +180,60 @@ async def authorize(
174180 if code_challenge_method != "S256" :
175181 raise HTTPException (status_code = 400 , detail = "Only S256 PKCE is supported" )
176182
183+ # Store the auth request details
184+ request_id = os .urandom (16 ).hex ()
185+ auth_requests [request_id ] = {
186+ "client_id" : client_id ,
187+ "redirect_uri" : redirect_uri ,
188+ "state" : state ,
189+ "scope" : scope ,
190+ "code_challenge" : code_challenge ,
191+ "code_challenge_method" : code_challenge_method ,
192+ }
193+
194+ # Show login page
195+ scopes = sorted (set (("openid profile " + scope ).split ()))
196+ return templates .TemplateResponse (
197+ "login.html" ,
198+ {
199+ "request" : request ,
200+ "request_id" : request_id ,
201+ "client_id" : client_id ,
202+ "scopes" : scopes ,
203+ },
204+ )
205+
206+
207+ @app .post ("/login" )
208+ async def login (request_id : str = Form (...)):
209+ """Handle login form submission."""
210+ # Retrieve the stored auth request
211+ if request_id not in auth_requests :
212+ raise HTTPException (status_code = 400 , detail = "Invalid request" )
213+
214+ auth_request = auth_requests .pop (request_id )
215+
177216 # Generate authorization code
178217 code = os .urandom (32 ).hex ()
179218
180219 # Store authorization details
181220 authorization_codes [code ] = {
182- "client_id" : client_id ,
183- "redirect_uri" : redirect_uri ,
184- "scope" : " " .join (sorted (set (("openid profile " + scope ).split (" " )))),
221+ "client_id" : auth_request ["client_id" ],
222+ "redirect_uri" : auth_request ["redirect_uri" ],
223+ "scope" : " " .join (
224+ sorted (set (("openid profile " + auth_request ["scope" ]).split (" " )))
225+ ),
185226 }
186227
187228 # Store PKCE challenge if provided
188- if code_challenge :
189- pkce_challenges [code ] = code_challenge
229+ if auth_request [ " code_challenge" ] :
230+ pkce_challenges [code ] = auth_request [ " code_challenge" ]
190231
191232 # Redirect back to client with the code
192- params = {"code" : code , "state" : state }
193- return RedirectResponse (url = f"{ redirect_uri } ?{ urlencode (params )} " )
233+ params = {"code" : code , "state" : auth_request ["state" ]}
234+ return RedirectResponse (
235+ url = f"{ auth_request ['redirect_uri' ]} ?{ urlencode (params )} " , status_code = 303
236+ )
194237
195238
196239@app .post ("/token" )
0 commit comments