auth_framework/api/
oauth_simple.rs1use crate::api::{ApiResponse, ApiState};
8use axum::{
9 extract::State,
10 Form,
11};
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Deserialize)]
17pub struct TokenIntrospectForm {
18 pub token: String,
19}
20
21#[derive(Debug, Serialize)]
22pub struct TokenIntrospectResponse {
23 pub active: bool,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub client_id: Option<String>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub username: Option<String>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub scope: Option<String>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub token_type: Option<String>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub exp: Option<i64>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub iat: Option<i64>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub sub: Option<String>,
38}
39
40#[derive(Debug, Deserialize)]
41pub struct PARForm {
42 pub client_id: String,
43 pub response_type: Option<String>,
44 pub redirect_uri: Option<String>,
45 pub scope: Option<String>,
46 pub state: Option<String>,
47}
48
49#[derive(Debug, Serialize)]
50pub struct PARResponse {
51 pub request_uri: String,
52 pub expires_in: u64,
53}
54
55#[derive(Debug, Deserialize)]
56pub struct DeviceAuthForm {
57 pub client_id: String,
58 pub scope: Option<String>,
59}
60
61#[derive(Debug, Serialize)]
62pub struct DeviceAuthResponse {
63 pub device_code: String,
64 pub user_code: String,
65 pub verification_uri: String,
66 pub verification_uri_complete: Option<String>,
67 pub expires_in: u64,
68 pub interval: Option<u64>,
69}
70
71pub async fn introspect_token(
76 State(_state): State<ApiState>,
77 Form(_form): Form<TokenIntrospectForm>,
78) -> ApiResponse<TokenIntrospectResponse> {
79 let response = TokenIntrospectResponse {
82 active: false, client_id: None,
84 username: None,
85 scope: None,
86 token_type: None,
87 exp: None,
88 iat: None,
89 sub: None,
90 };
91 ApiResponse::success(response)
92}
93
94pub async fn pushed_authorization_request(
97 State(_state): State<ApiState>,
98 Form(_form): Form<PARForm>,
99) -> ApiResponse<PARResponse> {
100 let request_uri = format!("urn:ietf:params:oauth:request_uri:{}", uuid::Uuid::new_v4());
102
103 let response = PARResponse {
107 request_uri,
108 expires_in: 60, };
110
111 ApiResponse::success(response)
112}
113
114pub async fn device_authorization(
117 State(_state): State<ApiState>,
118 Form(_form): Form<DeviceAuthForm>,
119) -> ApiResponse<DeviceAuthResponse> {
120 let device_code = format!("dc_{}", generate_random_string(32));
122 let user_code = generate_user_friendly_code();
123
124 let verification_uri = "http://localhost:8080/device".to_string();
125 let verification_uri_complete = format!("{}?user_code={}", verification_uri, user_code);
126
127 let response = DeviceAuthResponse {
131 device_code,
132 user_code,
133 verification_uri,
134 verification_uri_complete: Some(verification_uri_complete),
135 expires_in: 600, interval: Some(5), };
138
139 ApiResponse::success(response)
140}
141
142fn generate_random_string(length: usize) -> String {
145 use rand::Rng;
146 const CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
147 let mut rng = rand::rng();
148 (0..length)
149 .map(|_| {
150 let idx = rng.random_range(0..CHARS.len());
151 CHARS[idx] as char
152 })
153 .collect()
154}
155
156fn generate_user_friendly_code() -> String {
157 use rand::Rng;
158 const CHARS: &[u8] = b"ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; let mut rng = rand::rng();
160 (0..8)
161 .map(|_| {
162 let idx = rng.random_range(0..CHARS.len());
163 CHARS[idx] as char
164 })
165 .collect()
166}