feat: add auth login options endpoint
This commit is contained in:
@@ -19,6 +19,7 @@ use crate::{
|
||||
auth_sessions::auth_sessions,
|
||||
error_middleware::normalize_error_response,
|
||||
health::health_check,
|
||||
login_options::auth_login_options,
|
||||
logout::logout,
|
||||
logout_all::logout_all,
|
||||
password_entry::password_entry,
|
||||
@@ -51,6 +52,10 @@ pub fn build_router(state: AppState) -> Router {
|
||||
attach_refresh_session_token,
|
||||
)),
|
||||
)
|
||||
.route(
|
||||
"/api/auth/login-options",
|
||||
get(auth_login_options),
|
||||
)
|
||||
.route(
|
||||
"/api/auth/me",
|
||||
get(auth_me).route_layer(middleware::from_fn_with_state(
|
||||
@@ -438,6 +443,42 @@ mod tests {
|
||||
assert!(payload["token"].as_str().is_some());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn auth_login_options_returns_enabled_methods_in_stable_order() {
|
||||
let config = AppConfig {
|
||||
sms_auth_enabled: true,
|
||||
wechat_auth_enabled: true,
|
||||
..AppConfig::default()
|
||||
};
|
||||
let app = build_router(AppState::new(config).expect("state should build"));
|
||||
|
||||
let response = app
|
||||
.oneshot(
|
||||
Request::builder()
|
||||
.uri("/api/auth/login-options")
|
||||
.body(Body::empty())
|
||||
.expect("request should build"),
|
||||
)
|
||||
.await
|
||||
.expect("request should succeed");
|
||||
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let body = response
|
||||
.into_body()
|
||||
.collect()
|
||||
.await
|
||||
.expect("response body should collect")
|
||||
.to_bytes();
|
||||
let payload: Value =
|
||||
serde_json::from_slice(&body).expect("response body should be valid json");
|
||||
|
||||
assert_eq!(
|
||||
payload["availableLoginMethods"],
|
||||
serde_json::json!(["phone", "wechat"])
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn auth_sessions_returns_multi_device_session_fields() {
|
||||
let app = build_router(AppState::new(AppConfig::default()).expect("state should build"));
|
||||
@@ -1238,4 +1279,4 @@ mod tests {
|
||||
.is_some_and(|value| value.contains("Max-Age=0"))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
server-rs/crates/api-server/src/login_options.rs
Normal file
33
server-rs/crates/api-server/src/login_options.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Extension, State},
|
||||
};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{api_response::json_success_body, request_context::RequestContext, state::AppState};
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AuthLoginOptionsResponse {
|
||||
pub available_login_methods: Vec<&'static str>,
|
||||
}
|
||||
|
||||
pub async fn auth_login_options(
|
||||
State(state): State<AppState>,
|
||||
Extension(request_context): Extension<RequestContext>,
|
||||
) -> Json<serde_json::Value> {
|
||||
let mut methods = Vec::new();
|
||||
if state.config.sms_auth_enabled {
|
||||
methods.push("phone");
|
||||
}
|
||||
if state.config.wechat_auth_enabled {
|
||||
methods.push("wechat");
|
||||
}
|
||||
|
||||
json_success_body(
|
||||
Some(&request_context),
|
||||
AuthLoginOptionsResponse {
|
||||
available_login_methods: methods,
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -9,6 +9,7 @@ mod config;
|
||||
mod error_middleware;
|
||||
mod health;
|
||||
mod http_error;
|
||||
mod login_options;
|
||||
mod logout;
|
||||
mod logout_all;
|
||||
mod password_entry;
|
||||
@@ -44,4 +45,4 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
info!(%bind_address, "api-server 已完成 tracing 初始化并开始监听");
|
||||
|
||||
axum::serve(listener, router).await
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user