auth_framework/api/
openapi.rs

1//! OpenAPI Documentation Generator
2//!
3//! Generates OpenAPI 3.0 specifications for the AuthFramework API
4
5use serde_json::{Value, json};
6
7/// Generate OpenAPI specification
8pub 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
252/// Serve OpenAPI documentation
253pub async fn serve_openapi_json() -> axum::Json<Value> {
254    axum::Json(generate_openapi_spec())
255}
256
257/// Generate Swagger UI HTML
258pub 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