systemprompt_models/auth/
types.rs1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4use super::enums::UserType;
5use super::permission::Permission;
6
7pub const BEARER_PREFIX: &str = "Bearer ";
8
9#[derive(Clone, Debug, Serialize, Deserialize)]
10pub struct AuthenticatedUser {
11 pub id: Uuid,
12 pub username: String,
13 pub email: String,
14 pub permissions: Vec<Permission>,
15 #[serde(default)]
16 pub roles: Vec<String>,
17 #[serde(default)]
18 pub department: Option<String>,
19}
20
21impl AuthenticatedUser {
22 pub const fn new(
23 id: Uuid,
24 username: String,
25 email: String,
26 permissions: Vec<Permission>,
27 ) -> Self {
28 Self {
29 id,
30 username,
31 email,
32 permissions,
33 roles: Vec::new(),
34 department: None,
35 }
36 }
37
38 pub const fn new_with_roles(
39 id: Uuid,
40 username: String,
41 email: String,
42 permissions: Vec<Permission>,
43 roles: Vec<String>,
44 ) -> Self {
45 Self {
46 id,
47 username,
48 email,
49 permissions,
50 roles,
51 department: None,
52 }
53 }
54
55 #[must_use]
56 pub fn with_department(mut self, department: Option<String>) -> Self {
57 self.department = department;
58 self
59 }
60
61 #[must_use]
62 pub fn department(&self) -> Option<&str> {
63 self.department.as_deref()
64 }
65
66 pub fn has_permission(&self, permission: Permission) -> bool {
67 self.permissions.contains(&permission)
68 || self.permissions.iter().any(|p| p.implies(&permission))
69 }
70
71 pub fn is_admin(&self) -> bool {
72 self.has_permission(Permission::Admin)
73 }
74
75 pub fn permissions(&self) -> &[Permission] {
76 &self.permissions
77 }
78
79 pub fn has_role(&self, role: &str) -> bool {
80 self.roles.iter().any(|r| r == role)
81 }
82
83 pub fn roles(&self) -> &[String] {
84 &self.roles
85 }
86
87 pub fn user_type(&self) -> UserType {
88 if self.has_permission(Permission::Admin) {
89 UserType::Admin
90 } else if self.has_permission(Permission::User) {
91 UserType::User
92 } else if self.has_permission(Permission::A2a) {
93 UserType::A2a
94 } else if self.has_permission(Permission::Mcp) {
95 UserType::Mcp
96 } else if self.has_permission(Permission::Service) {
97 UserType::Service
98 } else {
99 UserType::Anon
100 }
101 }
102}
103
104#[derive(Debug, thiserror::Error)]
105pub enum AuthError {
106 #[error("Invalid token format")]
107 InvalidTokenFormat,
108
109 #[error("Token expired")]
110 TokenExpired,
111
112 #[error("Token signature invalid")]
113 InvalidSignature,
114
115 #[error("User not found")]
116 UserNotFound,
117
118 #[error("Insufficient permissions")]
119 InsufficientPermissions,
120
121 #[error("Authentication failed: {message}")]
122 AuthenticationFailed { message: String },
123
124 #[error("Invalid OAuth request: {reason}")]
125 InvalidRequest { reason: String },
126
127 #[error("CSRF token (state) is required")]
128 MissingState,
129
130 #[error("Redirect URI is required and must be registered")]
131 InvalidRedirectUri,
132
133 #[error("PKCE code_challenge is required")]
134 MissingCodeChallenge,
135
136 #[error("PKCE method '{method}' not allowed (must be S256)")]
137 WeakPkceMethod { method: String },
138
139 #[error("Client ID {client_id} not found")]
140 ClientNotFound { client_id: String },
141
142 #[error("Scope '{scope}' is invalid")]
143 InvalidScope { scope: String },
144
145 #[error("Token revocation requires authenticated user")]
146 UnauthenticatedRevocation,
147
148 #[error("WebAuthn RP ID could not be determined")]
149 InvalidRpId,
150
151 #[error("Client registration validation failed: {reason}")]
152 RegistrationFailed { reason: String },
153
154 #[error("Internal error: {0}")]
155 Internal(String),
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub enum PkceMethod {
160 S256,
161}
162
163impl std::str::FromStr for PkceMethod {
164 type Err = AuthError;
165
166 fn from_str(s: &str) -> Result<Self, Self::Err> {
167 match s {
168 "S256" => Ok(Self::S256),
169 "plain" => Err(AuthError::WeakPkceMethod {
170 method: s.to_string(),
171 }),
172 _ => Err(AuthError::InvalidRequest {
173 reason: format!("Unknown PKCE method: {s}"),
174 }),
175 }
176 }
177}
178
179impl std::fmt::Display for PkceMethod {
180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181 match self {
182 Self::S256 => write!(f, "S256"),
183 }
184 }
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq)]
188pub enum GrantType {
189 AuthorizationCode,
190 RefreshToken,
191 ClientCredentials,
192}
193
194impl std::str::FromStr for GrantType {
195 type Err = AuthError;
196
197 fn from_str(s: &str) -> Result<Self, Self::Err> {
198 match s {
199 "authorization_code" => Ok(Self::AuthorizationCode),
200 "refresh_token" => Ok(Self::RefreshToken),
201 "client_credentials" => Ok(Self::ClientCredentials),
202 _ => Err(AuthError::InvalidRequest {
203 reason: format!("Unknown grant type: {s}"),
204 }),
205 }
206 }
207}
208
209impl std::fmt::Display for GrantType {
210 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211 match self {
212 Self::AuthorizationCode => write!(f, "authorization_code"),
213 Self::RefreshToken => write!(f, "refresh_token"),
214 Self::ClientCredentials => write!(f, "client_credentials"),
215 }
216 }
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq)]
220pub enum ResponseType {
221 Code,
222 Token,
223}
224
225impl std::str::FromStr for ResponseType {
226 type Err = AuthError;
227
228 fn from_str(s: &str) -> Result<Self, Self::Err> {
229 match s {
230 "code" => Ok(Self::Code),
231 "token" => Ok(Self::Token),
232 _ => Err(AuthError::InvalidRequest {
233 reason: format!("Unknown response type: {s}"),
234 }),
235 }
236 }
237}
238
239impl std::fmt::Display for ResponseType {
240 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241 match self {
242 Self::Code => write!(f, "code"),
243 Self::Token => write!(f, "token"),
244 }
245 }
246}