1use axum::{
10 extract::{Query, State},
11 http::StatusCode,
12 response::Json,
13};
14use serde::Deserialize;
15use std::collections::HashMap;
16use std::sync::Arc;
17
18use crate::auth::token_lifecycle::{extract_token_id, TokenLifecycleManager};
19
20#[derive(Clone)]
22pub struct TokenLifecycleState {
23 pub manager: Arc<TokenLifecycleManager>,
25}
26
27#[derive(Debug, Deserialize)]
29pub struct RevokeTokenRequest {
30 pub token: Option<String>,
32 pub token_id: Option<String>,
34 pub user_id: Option<String>,
36 pub reason: String,
38}
39
40#[derive(Debug, Deserialize)]
42pub struct RevokeUserTokensRequest {
43 pub user_id: String,
45 pub reason: String,
47}
48
49#[derive(Debug, Deserialize)]
51pub struct RotateKeyRequest {
52 pub new_key_id: String,
54 pub grace_period_seconds: Option<i64>,
56}
57
58#[derive(Debug, Deserialize)]
60pub struct ClockSkewRequest {
61 pub skew_seconds: i64,
63 pub duration_seconds: Option<u64>,
65}
66
67#[derive(Debug, Deserialize)]
69pub struct ForceRefreshFailureRequest {
70 pub user_id: String,
72 pub failure_type: String,
74}
75
76#[derive(Debug, Deserialize)]
78pub struct RevokeMidSessionRequest {
79 pub user_id: String,
81 pub delay_seconds: u64,
83}
84
85pub async fn revoke_token(
87 State(state): State<TokenLifecycleState>,
88 Json(request): Json<RevokeTokenRequest>,
89) -> Result<Json<serde_json::Value>, StatusCode> {
90 let token_id = if let Some(token) = request.token {
91 extract_token_id(&token)
92 } else if let Some(tid) = request.token_id {
93 tid
94 } else {
95 return Err(StatusCode::BAD_REQUEST);
96 };
97
98 state
99 .manager
100 .revocation
101 .revoke_token(
102 token_id.clone(),
103 request.user_id,
104 request.reason,
105 None, )
107 .await;
108
109 Ok(Json(serde_json::json!({
110 "success": true,
111 "token_id": token_id,
112 "message": "Token revoked successfully"
113 })))
114}
115
116pub async fn revoke_user_tokens(
118 State(state): State<TokenLifecycleState>,
119 Json(request): Json<RevokeUserTokensRequest>,
120) -> Result<Json<serde_json::Value>, StatusCode> {
121 state
122 .manager
123 .revocation
124 .revoke_user_tokens(request.user_id.clone(), request.reason)
125 .await;
126
127 Ok(Json(serde_json::json!({
128 "success": true,
129 "user_id": request.user_id,
130 "message": "All user tokens revoked successfully"
131 })))
132}
133
134pub async fn get_token_status(
136 State(state): State<TokenLifecycleState>,
137 Query(params): Query<HashMap<String, String>>,
138) -> Result<Json<serde_json::Value>, StatusCode> {
139 let token_id = if let Some(token) = params.get("token") {
140 extract_token_id(token)
141 } else if let Some(tid) = params.get("token_id") {
142 tid.clone()
143 } else {
144 return Err(StatusCode::BAD_REQUEST);
145 };
146
147 if let Some(revoked) = state.manager.revocation.get_revocation_status(&token_id).await {
148 Ok(Json(serde_json::json!({
149 "revoked": true,
150 "revoked_at": revoked.revoked_at,
151 "reason": revoked.reason
152 })))
153 } else {
154 Ok(Json(serde_json::json!({
155 "revoked": false
156 })))
157 }
158}
159
160pub async fn rotate_keys(
162 State(state): State<TokenLifecycleState>,
163 Json(request): Json<RotateKeyRequest>,
164) -> Result<Json<serde_json::Value>, StatusCode> {
165 state
170 .manager
171 .key_rotation
172 .rotate_key(request.new_key_id.clone())
173 .await
174 .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
175
176 Ok(Json(serde_json::json!({
177 "success": true,
178 "new_key_id": request.new_key_id,
179 "message": "Key rotation initiated. Update OIDC configuration to use the new key."
180 })))
181}
182
183pub async fn get_active_keys(State(state): State<TokenLifecycleState>) -> Json<serde_json::Value> {
185 let keys = state.manager.key_rotation.get_active_keys().await;
186 Json(serde_json::json!({
187 "keys": keys.iter().map(|k| serde_json::json!({
188 "kid": k.kid,
189 "created_at": k.created_at,
190 "inactive_at": k.inactive_at,
191 "is_primary": k.is_primary
192 })).collect::<Vec<_>>()
193 }))
194}
195
196pub async fn set_clock_skew(
198 State(state): State<TokenLifecycleState>,
199 Json(request): Json<ClockSkewRequest>,
200) -> Result<Json<serde_json::Value>, StatusCode> {
201 state.manager.clock_skew.set_skew(request.skew_seconds).await;
202
203 if let Some(duration) = request.duration_seconds {
205 let state_clone = state.clone();
206 let skew_value = request.skew_seconds;
207 tokio::spawn(async move {
208 tokio::time::sleep(tokio::time::Duration::from_secs(duration)).await;
209 state_clone.manager.clock_skew.set_skew(0).await;
210 });
211 }
212
213 Ok(Json(serde_json::json!({
214 "success": true,
215 "skew_seconds": request.skew_seconds,
216 "message": "Clock skew set successfully"
217 })))
218}
219
220pub async fn get_clock_skew(State(state): State<TokenLifecycleState>) -> Json<serde_json::Value> {
222 let skew = state.manager.clock_skew.get_skew().await;
223 let adjusted_time = state.manager.clock_skew.get_adjusted_time().await;
224 let server_time = chrono::Utc::now().timestamp();
225
226 Json(serde_json::json!({
227 "skew_seconds": skew,
228 "server_time": server_time,
229 "adjusted_time": adjusted_time
230 }))
231}
232
233pub async fn force_refresh_failure(
235 State(state): State<TokenLifecycleState>,
236 Json(request): Json<ForceRefreshFailureRequest>,
237) -> Result<Json<serde_json::Value>, StatusCode> {
238 let reason = format!("test_scenario:{}", request.failure_type);
240 state
241 .manager
242 .revocation
243 .revoke_user_tokens(request.user_id.clone(), reason)
244 .await;
245
246 Ok(Json(serde_json::json!({
247 "success": true,
248 "user_id": request.user_id,
249 "failure_type": request.failure_type,
250 "message": "Refresh token failure simulated"
251 })))
252}
253
254pub async fn revoke_mid_session(
256 State(state): State<TokenLifecycleState>,
257 Json(request): Json<RevokeMidSessionRequest>,
258) -> Result<Json<serde_json::Value>, StatusCode> {
259 let state_clone = state.clone();
260 let user_id = request.user_id.clone();
261 let delay = request.delay_seconds;
262
263 tokio::spawn(async move {
264 tokio::time::sleep(tokio::time::Duration::from_secs(delay)).await;
265 state_clone
266 .manager
267 .revocation
268 .revoke_user_tokens(user_id, "mid_session_revocation".to_string())
269 .await;
270 });
271
272 Ok(Json(serde_json::json!({
273 "success": true,
274 "user_id": request.user_id,
275 "delay_seconds": request.delay_seconds,
276 "message": format!("Token will be revoked in {} seconds", request.delay_seconds)
277 })))
278}
279
280pub fn token_lifecycle_router(state: TokenLifecycleState) -> axum::Router {
282 use axum::routing::{get, post};
283
284 axum::Router::new()
285 .route("/tokens/revoke", post(revoke_token))
286 .route("/tokens/revoke/user", post(revoke_user_tokens))
287 .route("/tokens/status", get(get_token_status))
288 .route("/keys/rotate", post(rotate_keys))
289 .route("/keys/active", get(get_active_keys))
290 .route("/clock/skew", post(set_clock_skew))
291 .route("/clock/skew", get(get_clock_skew))
292 .route("/test/force-refresh-failure", post(force_refresh_failure))
293 .route("/test/revoke-mid-session", post(revoke_mid_session))
294 .with_state(state)
295}