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