Skip to main content

systemprompt_security/
error.rs

1//! Error types raised by the security infrastructure.
2//!
3//! Public APIs in this crate return `thiserror`-derived error enums:
4//!
5//! - [`AuthError`] — request validation, JWT decoding, claim extraction.
6//! - [`JwtError`] — JWT minting (admin tokens, session tokens).
7//! - [`ManifestSigningError`] — Ed25519 signing of bridge manifests.
8//!
9//! All three implement `std::error::Error` and can be composed into larger
10//! `thiserror` enums via `#[from]`.
11//!
12//! Copyright (c) systemprompt.io — Business Source License 1.1.
13//! See <https://systemprompt.io> for licensing details.
14
15use 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
80#[derive(Debug, Error)]
81pub enum JwtError {
82    #[error("jwt encoding failed: {0}")]
83    Encoding(#[from] jsonwebtoken::errors::Error),
84
85    #[error("jwt signing key unavailable: {0}")]
86    Signing(String),
87}
88
89#[derive(Debug, Error)]
90pub enum ManifestSigningError {
91    #[error("manifest signing seed unavailable: {0}")]
92    SeedUnavailable(String),
93
94    #[error("jcs canonicalize: {0}")]
95    Canonicalize(String),
96
97    #[error("signing key missing after initialization")]
98    KeyMissing,
99}
100
101pub type AuthResult<T> = Result<T, AuthError>;
102
103pub type JwtResult<T> = Result<T, JwtError>;
104
105pub type ManifestSigningResult<T> = Result<T, ManifestSigningError>;