fix: specific redirect codes for mobile

This commit is contained in:
herel committed 2026-07-06 14:14:02 +02:00
1 parent 7ae137f865
commit 640bbea802
1 file changed
+25 -11
+25 -11
View File
@@ -7,7 +7,7 @@ from fastapi import FastAPI, Header, HTTPException, Request, status
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, RedirectResponse from fastapi.responses import FileResponse, RedirectResponse
from pydantic import BaseModel from pydantic import BaseModel
from typing import Annotated from typing import Annotated, Optional
from . import myice from . import myice
# OIDC Configuration # OIDC Configuration
@@ -151,11 +151,10 @@ async def favico():
@app.get("/login") @app.get("/login")
async def login(request: Request): async def login(request: Request, redirect_uri: Optional[str] = None):
"""Redirect to Infomaniak OIDC login""" """Redirect to Infomaniak OIDC login"""
import urllib.parse
import secrets import secrets
import urllib.parse
state = secrets.token_urlsafe(32) state = secrets.token_urlsafe(32)
nonce = secrets.token_urlsafe(32) nonce = secrets.token_urlsafe(32)
@@ -196,6 +195,16 @@ async def login(request: Request):
samesite="Lax", samesite="Lax",
max_age=300, max_age=300,
) )
# Store target redirect URI (e.g. myice://callback) for mobile app flow
if redirect_uri:
response.set_cookie(
"oidc_target_redirect",
redirect_uri,
httponly=True,
secure=True,
samesite="Lax",
max_age=300,
)
return response return response
@@ -204,12 +213,17 @@ async def callback(request: Request, code: str, state: str):
"""Handle OIDC callback and exchange code for token""" """Handle OIDC callback and exchange code for token"""
expected_state = request.cookies.get("oidc_state") expected_state = request.cookies.get("oidc_state")
code_verifier = request.cookies.get("oidc_verifier") code_verifier = request.cookies.get("oidc_verifier")
# Determine final redirect target (mobile app or web frontend)
target_redirect = request.cookies.get("oidc_target_redirect") or os.environ.get(
"FRONTEND_URL", "/"
)
# We should also delete state/nonce cookies once read # We should also delete state/nonce cookies once read
if not expected_state or state != expected_state: if not expected_state or state != expected_state:
response = RedirectResponse("/?error=Invalid state parameter") response = RedirectResponse(f"/{target_redirect}?error=Invalid state parameter")
response.delete_cookie("oidc_state") response.delete_cookie("oidc_state")
response.delete_cookie("oidc_nonce") response.delete_cookie("oidc_nonce")
response.delete_cookie("oidc_verifier") response.delete_cookie("oidc_verifier")
response.delete_cookie("oidc_target_redirect")
return response return response
# Exchange authorization code for access token # Exchange authorization code for access token
token_data = { token_data = {
@@ -227,14 +241,14 @@ async def callback(request: Request, code: str, state: str):
# Log the error for debugging # Log the error for debugging
logging.error("Token exchange failed: %s", token_response) logging.error("Token exchange failed: %s", token_response)
# Redirect back to frontend with error # Redirect back to frontend with error
frontend_url = os.environ.get("FRONTEND_URL", "/")
error_detail = token_response.get( error_detail = token_response.get(
"error_description", "Failed to obtain access token" "error_description", "Failed to obtain access token"
) )
error_response = RedirectResponse(f"{frontend_url}?error={error_detail}") error_response = RedirectResponse(f"{target_redirect}?error={error_detail}")
error_response.delete_cookie("oidc_state") error_response.delete_cookie("oidc_state")
error_response.delete_cookie("oidc_nonce") error_response.delete_cookie("oidc_nonce")
error_response.delete_cookie("oidc_verifier") error_response.delete_cookie("oidc_verifier")
error_response.delete_cookie("oidc_target_redirect")
return error_response return error_response
access_token = token_response["access_token"] access_token = token_response["access_token"]
@@ -250,19 +264,19 @@ async def callback(request: Request, code: str, state: str):
user_email = userinfo.get("email") user_email = userinfo.get("email")
if ALLOWED_USERS and user_email not in ALLOWED_USERS: if ALLOWED_USERS and user_email not in ALLOWED_USERS:
# Redirect back to frontend with error # Redirect back to frontend with error
frontend_url = os.environ.get("FRONTEND_URL", "/") response = RedirectResponse(f"{target_redirect}?error=User not authorized")
response = RedirectResponse(f"{frontend_url}?error=User not authorized")
response.delete_cookie("oidc_state") response.delete_cookie("oidc_state")
response.delete_cookie("oidc_nonce") response.delete_cookie("oidc_nonce")
response.delete_cookie("oidc_verifier") response.delete_cookie("oidc_verifier")
response.delete_cookie("oidc_target_redirect")
return response return response
# Redirect back to frontend with access token and user info # Redirect back to frontend with access token and user info
frontend_url = os.environ.get("FRONTEND_URL", "/") response = RedirectResponse(f"{target_redirect}#access_token={access_token}")
response = RedirectResponse(f"{frontend_url}#access_token={access_token}")
response.delete_cookie("oidc_state") response.delete_cookie("oidc_state")
response.delete_cookie("oidc_nonce") response.delete_cookie("oidc_nonce")
response.delete_cookie("oidc_verifier") response.delete_cookie("oidc_verifier")
response.delete_cookie("oidc_target_redirect")
return response return response