1use serde_json::{Value, json};
6
7pub fn generate_openapi_spec() -> Value {
9 json!({
10 "openapi": "3.0.3",
11 "info": {
12 "title": "AuthFramework API",
13 "description": "Comprehensive authentication and authorization framework",
14 "version": "0.4.0",
15 "contact": {
16 "name": "AuthFramework Team",
17 "url": "https://github.com/ciresnave/auth-framework"
18 },
19 "license": {
20 "name": "MIT OR Apache-2.0",
21 "url": "https://github.com/ciresnave/auth-framework/blob/main/LICENSE"
22 }
23 },
24 "servers": [
25 {
26 "url": "https://api.example.com/v1",
27 "description": "Production server"
28 },
29 {
30 "url": "http://localhost:8080/v1",
31 "description": "Development server"
32 }
33 ],
34 "paths": generate_paths(),
35 "components": {
36 "schemas": generate_schemas(),
37 "securitySchemes": {
38 "bearerAuth": {
39 "type": "http",
40 "scheme": "bearer",
41 "bearerFormat": "JWT"
42 },
43 "apiKey": {
44 "type": "apiKey",
45 "in": "header",
46 "name": "X-API-Key"
47 }
48 }
49 },
50 "security": [
51 { "bearerAuth": [] }
52 ]
53 })
54}
55
56fn generate_paths() -> Value {
57 json!({
58 "/auth/login": {
59 "post": {
60 "tags": ["Authentication"],
61 "summary": "User login",
62 "description": "Authenticate user with credentials",
63 "requestBody": {
64 "required": true,
65 "content": {
66 "application/json": {
67 "schema": { "$ref": "#/components/schemas/LoginRequest" }
68 }
69 }
70 },
71 "responses": {
72 "200": {
73 "description": "Login successful",
74 "content": {
75 "application/json": {
76 "schema": { "$ref": "#/components/schemas/LoginResponse" }
77 }
78 }
79 },
80 "401": {
81 "description": "Invalid credentials",
82 "content": {
83 "application/json": {
84 "schema": { "$ref": "#/components/schemas/ErrorResponse" }
85 }
86 }
87 }
88 }
89 }
90 },
91 "/auth/refresh": {
92 "post": {
93 "tags": ["Authentication"],
94 "summary": "Refresh access token",
95 "description": "Get new access token using refresh token",
96 "requestBody": {
97 "required": true,
98 "content": {
99 "application/json": {
100 "schema": { "$ref": "#/components/schemas/RefreshRequest" }
101 }
102 }
103 },
104 "responses": {
105 "200": {
106 "description": "Token refreshed successfully",
107 "content": {
108 "application/json": {
109 "schema": { "$ref": "#/components/schemas/RefreshResponse" }
110 }
111 }
112 }
113 }
114 }
115 },
116 "/auth/logout": {
117 "post": {
118 "tags": ["Authentication"],
119 "summary": "User logout",
120 "description": "Invalidate user session and tokens",
121 "security": [{ "bearerAuth": [] }],
122 "responses": {
123 "200": {
124 "description": "Logout successful",
125 "content": {
126 "application/json": {
127 "schema": { "$ref": "#/components/schemas/MessageResponse" }
128 }
129 }
130 }
131 }
132 }
133 },
134 "/health": {
135 "get": {
136 "tags": ["System"],
137 "summary": "Health check",
138 "description": "Get system health status",
139 "responses": {
140 "200": {
141 "description": "System is healthy",
142 "content": {
143 "application/json": {
144 "schema": { "$ref": "#/components/schemas/HealthResponse" }
145 }
146 }
147 }
148 }
149 }
150 }
151 })
152}
153
154fn generate_schemas() -> Value {
155 json!({
156 "LoginRequest": {
157 "type": "object",
158 "required": ["username", "password"],
159 "properties": {
160 "username": {
161 "type": "string",
162 "description": "User's username or email"
163 },
164 "password": {
165 "type": "string",
166 "format": "password",
167 "description": "User's password"
168 },
169 "mfa_code": {
170 "type": "string",
171 "description": "Multi-factor authentication code (if required)"
172 },
173 "remember_me": {
174 "type": "boolean",
175 "default": false,
176 "description": "Extended session duration"
177 }
178 }
179 },
180 "LoginResponse": {
181 "type": "object",
182 "properties": {
183 "success": { "type": "boolean" },
184 "data": {
185 "type": "object",
186 "properties": {
187 "access_token": { "type": "string" },
188 "refresh_token": { "type": "string" },
189 "token_type": { "type": "string", "example": "Bearer" },
190 "expires_in": { "type": "integer" },
191 "user": { "$ref": "#/components/schemas/UserInfo" }
192 }
193 }
194 }
195 },
196 "UserInfo": {
197 "type": "object",
198 "properties": {
199 "id": { "type": "string" },
200 "username": { "type": "string" },
201 "roles": {
202 "type": "array",
203 "items": { "type": "string" }
204 },
205 "permissions": {
206 "type": "array",
207 "items": { "type": "string" }
208 }
209 }
210 },
211 "ErrorResponse": {
212 "type": "object",
213 "properties": {
214 "success": { "type": "boolean", "example": false },
215 "error": {
216 "type": "object",
217 "properties": {
218 "code": { "type": "string" },
219 "message": { "type": "string" },
220 "details": { "type": "object" }
221 }
222 }
223 }
224 },
225 "MessageResponse": {
226 "type": "object",
227 "properties": {
228 "success": { "type": "boolean" },
229 "message": { "type": "string" }
230 }
231 },
232 "HealthResponse": {
233 "type": "object",
234 "properties": {
235 "status": { "type": "string", "enum": ["healthy", "degraded", "unhealthy"] },
236 "timestamp": { "type": "string", "format": "date-time" },
237 "checks": {
238 "type": "object",
239 "additionalProperties": {
240 "type": "object",
241 "properties": {
242 "status": { "type": "string" },
243 "details": { "type": "object" }
244 }
245 }
246 }
247 }
248 }
249 })
250}
251
252pub async fn serve_openapi_json() -> axum::Json<Value> {
254 axum::Json(generate_openapi_spec())
255}
256
257pub fn generate_swagger_ui() -> String {
259 r#"<!DOCTYPE html>
260<html lang="en">
261<head>
262 <meta charset="UTF-8">
263 <meta name="viewport" content="width=device-width, initial-scale=1.0">
264 <title>AuthFramework API Documentation</title>
265 <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui.css" />
266 <style>
267 html { box-sizing: border-box; overflow: -moz-scrollbars-vertical; overflow-y: scroll; }
268 *, *:before, *:after { box-sizing: inherit; }
269 body { margin:0; background: #fafafa; }
270 </style>
271</head>
272<body>
273 <div id="swagger-ui"></div>
274 <script src="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui-bundle.js"></script>
275 <script src="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui-standalone-preset.js"></script>
276 <script>
277 window.onload = function() {
278 const ui = SwaggerUIBundle({
279 url: '/api/openapi.json',
280 dom_id: '#swagger-ui',
281 deepLinking: true,
282 presets: [
283 SwaggerUIBundle.presets.apis,
284 SwaggerUIStandalonePreset
285 ],
286 plugins: [
287 SwaggerUIBundle.plugins.DownloadUrl
288 ],
289 layout: "StandaloneLayout"
290 });
291 };
292 </script>
293</body>
294</html>"#.to_string()
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300
301 #[test]
302 fn test_openapi_spec_generation() {
303 let spec = generate_openapi_spec();
304 assert_eq!(spec["openapi"], "3.0.3");
305 assert_eq!(spec["info"]["title"], "AuthFramework API");
306 assert!(spec["paths"].is_object());
307 assert!(spec["components"]["schemas"].is_object());
308 }
309
310 #[test]
311 fn test_swagger_ui_generation() {
312 let html = generate_swagger_ui();
313 assert!(html.contains("swagger-ui"));
314 assert!(html.contains("/api/openapi.json"));
315 }
316}
317
318