119 lines
3.3 KiB
Rust
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,
|
|
})
|
|
}
|