calendar/src/handlers/auth.rs
pavel 9ae6d88c21
All checks were successful
/ upload (release) Successful in 1m54s
init
2026-02-15 23:16:26 +01:00

119 lines
3.3 KiB
Rust

use crate::auth::AppState;
use crate::entities::user;
use axum::{
Json,
extract::{Query, State},
response::{IntoResponse, Redirect, Response},
};
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set};
use serde::Deserialize;
#[derive(Deserialize)]
pub struct AuthCallback {
pub code: String,
#[allow(dead_code)]
pub state: String,
}
pub async fn login(State(state): State<AppState>) -> Response {
let oidc = state.oidc;
let (auth_url, _csrf_token, _nonce) = oidc.auth_url();
Redirect::to(&auth_url).into_response()
}
pub async fn callback(
State(state): State<AppState>,
Query(params): Query<AuthCallback>,
) -> Response {
let oidc = state.oidc;
let db = state.db;
match oidc.exchange_code(params.code).await {
Ok(tokens) => match oidc.validate_token(&tokens.id_token).await {
Ok(claims) => {
let sub = claims.sub;
let email = claims.email.unwrap_or_default();
let name = claims.name.unwrap_or_default();
let existing_user = user::Entity::find()
.filter(user::Column::Sub.eq(&sub))
.one(&db)
.await
.unwrap_or(None);
if existing_user.is_none() {
let new_user = user::ActiveModel {
sub: Set(sub),
email: Set(email),
name: Set(name),
..Default::default()
};
new_user.insert(&db).await.unwrap();
};
let mut redirect_url = format!("/#access_token={}", tokens.id_token);
if let Some(refresh_token) = tokens.refresh_token {
redirect_url.push_str(&format!("&refresh_token={}", refresh_token));
}
Redirect::to(&redirect_url).into_response()
}
Err(e) => {
tracing::error!("Token validation error: {:?}", e);
(
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
"Invalid token",
)
.into_response()
}
},
Err(e) => {
tracing::error!("OIDC callback error: {:?}", e);
(
axum::http::StatusCode::INTERNAL_SERVER_ERROR,
"Authentication failed",
)
.into_response()
}
}
}
pub async fn refresh(
State(state): State<AppState>,
Json(payload): Json<RefreshRequest>,
) -> Response {
let oidc = state.oidc;
match oidc.refresh_token(payload.refresh_token).await {
Ok(tokens) => Json(tokens).into_response(),
Err(e) => {
tracing::error!("Token refresh error: {:?}", e);
(axum::http::StatusCode::UNAUTHORIZED, "Refresh failed").into_response()
}
}
}
#[derive(Deserialize)]
pub struct RefreshRequest {
pub refresh_token: String,
}
pub async fn logout() -> Response {
Redirect::to("/").into_response()
}
pub async fn me(
crate::auth::CurrentUser {
id,
sub,
email,
name,
}: crate::auth::CurrentUser,
) -> Json<crate::auth::CurrentUser> {
Json(crate::auth::CurrentUser {
id,
sub,
email,
name,
})
}