fix(OIDC): redirect to frontend, jwt, and callback handling

This commit is contained in:
aaldebs99 2025-10-12 02:15:47 +00:00
parent a23edca938
commit 943a3eefae
3 changed files with 35 additions and 26 deletions

View File

@ -58,7 +58,7 @@ function App(): React.ReactElement {
<CssBaseline />
<Routes>
<Route path="/login" element={!user ? <Login /> : <Navigate to="/dashboard" />} />
<Route path="/auth/oidc/callback" element={<OidcCallback />} />
<Route path="/auth/callback" element={<OidcCallback />} />
<Route
path="/*"
element={

View File

@ -14,9 +14,8 @@ const OidcCallback: React.FC = () => {
useEffect(() => {
const handleCallback = async () => {
try {
const code = searchParams.get('code');
const token = searchParams.get('token');
const error = searchParams.get('error');
const state = searchParams.get('state');
if (error) {
setError(`Authentication failed: ${error}`);
@ -24,31 +23,23 @@ const OidcCallback: React.FC = () => {
return;
}
if (!code) {
setError('No authorization code received');
if (!token) {
setError('No authentication token received from server');
setProcessing(false);
return;
}
// Call the backend OIDC callback endpoint
const response = await api.get(`/auth/oidc/callback?code=${code}&state=${state || ''}`);
if (response.data && response.data.token) {
// Store the token and user data
localStorage.setItem('token', response.data.token);
api.defaults.headers.common['Authorization'] = `Bearer ${response.data.token}`;
// Redirect to dashboard - the auth context will pick up the token on next page load
window.location.href = '/dashboard';
} else {
setError('Invalid response from authentication server');
setProcessing(false);
}
// Store the token and set up API authorization
localStorage.setItem('token', token);
api.defaults.headers.common['Authorization'] = `Bearer ${token}`;
// Redirect to dashboard - the page will reload and AuthContext will pick up the token
window.location.href = '/dashboard';
} catch (err: any) {
console.error('OIDC callback error:', err);
const errorInfo = ErrorHelper.formatErrorForDisplay(err, true);
// Handle specific OIDC callback errors
if (ErrorHelper.isErrorCode(err, ErrorCodes.USER_OIDC_AUTH_FAILED)) {
setError('OIDC authentication failed. Please try logging in again or contact your administrator.');
@ -58,7 +49,7 @@ const OidcCallback: React.FC = () => {
setError('Authentication failed. Your OIDC credentials may be invalid or expired.');
} else if (ErrorHelper.isErrorCode(err, ErrorCodes.USER_ACCOUNT_DISABLED)) {
setError('Your account has been disabled. Please contact an administrator for assistance.');
} else if (ErrorHelper.isErrorCode(err, ErrorCodes.USER_SESSION_EXPIRED) ||
} else if (ErrorHelper.isErrorCode(err, ErrorCodes.USER_SESSION_EXPIRED) ||
ErrorHelper.isErrorCode(err, ErrorCodes.USER_TOKEN_EXPIRED)) {
setError('Authentication session expired. Please try logging in again.');
} else if (errorInfo.category === 'network') {
@ -68,7 +59,7 @@ const OidcCallback: React.FC = () => {
} else {
setError(errorInfo.message || 'Failed to complete authentication. Please try again.');
}
setProcessing(false);
}
};

View File

@ -1,6 +1,6 @@
use axum::{
extract::{Query, State},
http::StatusCode,
http::{StatusCode, HeaderMap},
response::{IntoResponse, Json, Response, Redirect},
routing::{get, post},
Router,
@ -188,6 +188,7 @@ async fn oidc_login(State(state): State<Arc<AppState>>) -> Result<Redirect, Stat
)]
async fn oidc_callback(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
Query(params): Query<OidcCallbackQuery>,
) -> Result<Redirect, StatusCode> {
tracing::info!("OIDC callback called with params: code={:?}, state={:?}, error={:?}",
@ -326,8 +327,25 @@ async fn oidc_callback(
// Redirect to frontend with token in URL fragment
// The frontend should extract the token and store it
let redirect_url = format!("/#/auth/callback?token={}", urlencoding::encode(&token));
tracing::info!("OIDC authentication successful for user: {}, redirecting to: {}", user.username, redirect_url);
// Use absolute URL to ensure hash fragment is handled correctly by the browser
let host = headers
.get("host")
.and_then(|h| h.to_str().ok())
.unwrap_or("localhost:8000");
// Check if behind a proxy (X-Forwarded-Proto header)
let protocol = headers
.get("x-forwarded-proto")
.and_then(|h| h.to_str().ok())
.unwrap_or("https");
let redirect_url = format!(
"{}://{}/auth/callback?token={}",
protocol,
host,
urlencoding::encode(&token)
);
tracing::info!("OIDC authentication successful for user: {}, redirecting to callback", user.username);
Ok(Redirect::to(&redirect_url))
}