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