systemprompt_security/
error.rs1use thiserror::Error;
16
17#[derive(Debug, Error)]
18pub enum AuthError {
19 #[error("missing authorization header")]
20 MissingAuthorization,
21
22 #[error("invalid JWT token: {0}")]
23 InvalidToken(#[source] jsonwebtoken::errors::Error),
24
25 #[error("missing session_id in token")]
26 MissingSessionId,
27
28 #[error("hook token: missing or non-`hook` audience")]
29 HookAudienceMissing,
30
31 #[error("hook token: required scope `{0}` not present")]
32 HookScopeMissing(&'static str),
33
34 #[error("hook token: missing `plugin_id` claim")]
35 HookPluginIdMissing,
36
37 #[error(
38 "hook token: plugin_id `{actual}` in claim does not match request plugin_id `{expected}`"
39 )]
40 HookPluginIdMismatch { expected: String, actual: String },
41
42 #[error("token has unsupported algorithm `{got}`; only RS256 is accepted")]
43 UnsupportedAlgorithm { got: String },
44
45 #[error("audience policy is empty; token decoding requires at least one expected audience")]
46 EmptyAudiencePolicy,
47
48 #[error("token is missing `kid` header")]
49 MissingKid,
50
51 #[error("token `kid` `{0}` does not match any known signing key")]
52 UnknownKid(String),
53
54 #[error("signing key lookup failed: {0}")]
55 KeyLookup(String),
56
57 #[error("issuer `{0}` is not trusted")]
58 UntrustedIssuer(String),
59
60 #[error("JWKS fetch failed for issuer `{issuer}`: {source}")]
61 JwksFetch {
62 issuer: String,
63 #[source]
64 source: crate::keys::JwksClientError,
65 },
66
67 #[error("token `act` delegation chain exceeds maximum depth of {max} (got {depth})")]
68 ActChainTooDeep { depth: usize, max: usize },
69
70 #[error("token is missing the `scope` claim")]
71 MissingScope,
72
73 #[error("token `user_type` claim `{claimed}` does not match permissions (derived `{derived}`)")]
74 UserTypeMismatch {
75 claimed: systemprompt_models::auth::UserType,
76 derived: systemprompt_models::auth::UserType,
77 },
78}
79
80impl AuthError {
81 #[must_use]
82 pub fn is_issuer_mismatch(&self) -> bool {
83 matches!(
84 self,
85 Self::InvalidToken(inner)
86 if matches!(inner.kind(), jsonwebtoken::errors::ErrorKind::InvalidIssuer)
87 )
88 }
89}
90
91#[derive(Debug, Error)]
92pub enum JwtError {
93 #[error("jwt encoding failed: {0}")]
94 Encoding(#[from] jsonwebtoken::errors::Error),
95
96 #[error("jwt signing key unavailable: {0}")]
97 Signing(String),
98}
99
100#[derive(Debug, Error)]
101pub enum ManifestSigningError {
102 #[error("manifest signing seed unavailable: {0}")]
103 SeedUnavailable(String),
104
105 #[error("jcs canonicalize: {0}")]
106 Canonicalize(String),
107
108 #[error("signing key missing after initialization")]
109 KeyMissing,
110}
111
112pub type AuthResult<T> = Result<T, AuthError>;
113
114pub type JwtResult<T> = Result<T, JwtError>;
115
116pub type ManifestSigningResult<T> = Result<T, ManifestSigningError>;