1use axum::{middleware::from_fn_with_state, response::IntoResponse};
6use rapid_rs::auth::{
7 auth_routes_with_store, create_token_pair, hash_password,
8 middleware::{inject_auth_config, RequireRoles},
9 models::AuthUserInfo,
10 AuthAppState, AuthConfig, AuthResponse, AuthUser, CreateUserData, InMemoryUserStore,
11 RegisterRequest, UserStore,
12};
13use rapid_rs::prelude::*;
14
15async fn protected_route(user: AuthUser) -> impl IntoResponse {
17 Json(serde_json::json!({
18 "message": format!("Hello, {}! You are authenticated.", user.email),
19 "user_id": user.id,
20 "roles": user.roles,
21 }))
22}
23
24async fn admin_route(user: AuthUser) -> Result<impl IntoResponse, ApiError> {
26 user.require_role("admin")
27 .map_err(|_| ApiError::Forbidden)?;
28
29 Ok(Json(serde_json::json!({
30 "message": "Welcome to the admin panel!",
31 "admin_id": user.id,
32 })))
33}
34
35async fn public_route() -> impl IntoResponse {
37 Json(serde_json::json!({
38 "message": "This is a public endpoint. Anyone can access it!",
39 }))
40}
41
42async fn middleware_admin() -> impl IntoResponse {
44 Json(serde_json::json!({
45 "status": "success",
46 "message": "You have the ADMIN role, so you can see this!",
47 }))
48}
49
50async fn middleware_user() -> impl IntoResponse {
52 Json(serde_json::json!({
53 "status": "success",
54 "message": "You have the USER role, so you can see this!",
55 }))
56}
57
58async fn register_admin(
60 State(state): State<AuthAppState<InMemoryUserStore>>,
61 ValidatedJson(payload): ValidatedJson<RegisterRequest>,
62) -> Result<Json<AuthResponse>, ApiError> {
63 let password_hash = hash_password(&payload.password, &state.config)?;
64
65 let user = state
66 .user_store
67 .create(CreateUserData {
68 email: payload.email,
69 name: payload.name,
70 password_hash,
71 })
72 .await?;
73
74 let token_pair = create_token_pair(
75 &user.id,
76 &user.email,
77 vec!["admin".to_string()],
78 &state.config,
79 )?;
80
81 Ok(Json(AuthResponse {
82 access_token: token_pair.access_token,
83 refresh_token: token_pair.refresh_token,
84 token_type: token_pair.token_type,
85 expires_in: token_pair.expires_in,
86 user: AuthUserInfo {
87 id: user.id,
88 email: user.email,
89 name: user.name,
90 roles: vec!["admin".to_string()],
91 },
92 }))
93}
94
95#[tokio::main]
96async fn main() {
97 let mut auth_config = AuthConfig::from_env();
100 auth_config.jwt_secret =
101 "rapid-rs-dev-secret-change-me-in-production-make-it-at-least-32-chars".to_string();
102
103 let user_store = InMemoryUserStore::new();
104
105 let app_state = AuthAppState {
106 config: auth_config.clone(),
107 user_store: user_store.clone(),
108 };
109
110 let admin_middleware_routes = Router::new()
111 .route("/middleware/admin", get(middleware_admin))
112 .layer(RequireRoles::any(vec!["admin"]));
113
114 let user_middleware_routes = Router::new()
115 .route("/middleware/user", get(middleware_user))
116 .layer(RequireRoles::any(vec!["user"]));
117
118 let protected_routes = Router::new()
120 .route("/protected", get(protected_route))
121 .route("/admin", get(admin_route))
122 .route("/public", get(public_route));
123
124 let auth_extras = Router::new()
126 .route("/auth/register-admin", post(register_admin))
127 .with_state(app_state);
128
129 println!("🔐 Auth API Example");
130 println!("==================");
131 println!();
132 println!("📝 Register a user:");
133 println!(" curl -X POST http://localhost:3000/auth/register -H \"Content-Type: application/json\" -d \"{{\\\"email\\\":\\\"user@example.com\\\",\\\"password\\\":\\\"SecurePass123\\\",\\\"name\\\":\\\"John Doe\\\"}}\"");
134 println!();
135 println!("🔑 Login:");
136 println!(" curl -X POST http://localhost:3000/auth/login -H \"Content-Type: application/json\" -d \"{{\\\"email\\\":\\\"user@example.com\\\",\\\"password\\\":\\\"SecurePass123\\\"}}\"");
137 println!();
138 println!("🔒 Access protected route:");
139 println!(" curl -X GET http://localhost:3000/protected -H \"Authorization: Bearer <access_token>\"");
140 println!();
141 println!("Scenario 1: Regular User");
142 println!("> Register a regular User:");
143 println!(" curl -X POST http://localhost:3000/auth/register -H \"Content-Type: application/json\" -d \"{{\\\"email\\\":\\\"user@example.com\\\",\\\"password\\\":\\\"SecurePass123\\\",\\\"name\\\":\\\"Basic Joe\\\"}}\"");
144 println!();
145 println!("> Test Admin Route (Should fail with code 403 Forbidden):");
146 println!(" curl -X GET http://localhost:3000/middleware/admin -H \"Authorization: Bearer <access_token>\"");
147 println!();
148 println!("Scenario 2: Admin User");
149 println!("> Register an ADMIN User:");
150 println!(" curl -X POST http://localhost:3000/auth/register-admin -H \"Content-Type: application/json\" -d \"{{\\\"email\\\":\\\"admin@example.com\\\",\\\"password\\\":\\\"SecurePass123\\\",\\\"name\\\":\\\"Admin Joe\\\"}}\"");
151 println!();
152 println!("> Test Admin Route (Should succed):");
153 println!(" curl -X GET http://localhost:3000/middleware/admin -H \"Authorization: Bearer <access_token>\"");
154
155 println!();
156
157 let api_routes = Router::new()
158 .merge(protected_routes)
159 .merge(admin_middleware_routes)
160 .merge(user_middleware_routes)
161 .merge(auth_extras)
162 .layer(from_fn_with_state(auth_config.clone(), inject_auth_config));
163
164 App::new()
165 .auto_configure()
166 .mount(auth_routes_with_store(auth_config, user_store))
167 .mount(api_routes)
168 .run()
169 .await
170 .unwrap();
171}