Skip to main content

uselesskey_token/srp/
spec.rs

1//! Stable token fixture specification.
2//!
3//! Defines [`TokenSpec`], the enum of supported token shapes (API key,
4//! bearer, OAuth/JWT-shape) used by `uselesskey-token` and its compatibility
5//! shims.
6
7/// Specification for token fixture generation.
8#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
9pub enum TokenSpec {
10    /// API key style token (e.g. `uk_test_<base62>`).
11    ApiKey,
12    /// Opaque bearer token (base64url body).
13    Bearer,
14    /// OAuth access token in JWT shape (`header.payload.signature`).
15    OAuthAccessToken,
16}
17
18impl TokenSpec {
19    /// Create an API-key spec (`uk_test_<base62>`).
20    pub const fn api_key() -> Self {
21        Self::ApiKey
22    }
23
24    /// Create an opaque bearer-token spec (base64url body).
25    pub const fn bearer() -> Self {
26        Self::Bearer
27    }
28
29    /// Create an OAuth access-token spec in JWT shape (`header.payload.signature`).
30    pub const fn oauth_access_token() -> Self {
31        Self::OAuthAccessToken
32    }
33
34    /// Return a short, stable name for this token kind (e.g. `"api_key"`).
35    pub const fn kind_name(&self) -> &'static str {
36        match self {
37            Self::ApiKey => "api_key",
38            Self::Bearer => "bearer",
39            Self::OAuthAccessToken => "oauth_access_token",
40        }
41    }
42
43    /// Stable encoding for cache keys / deterministic derivation.
44    ///
45    /// If you change this, bump the derivation version in `uselesskey-core`.
46    pub const fn stable_bytes(&self) -> [u8; 4] {
47        match self {
48            Self::ApiKey => [0, 0, 0, 1],
49            Self::Bearer => [0, 0, 0, 2],
50            Self::OAuthAccessToken => [0, 0, 0, 3],
51        }
52    }
53
54    /// HTTP authorization scheme associated with this token shape.
55    pub const fn authorization_scheme(&self) -> &'static str {
56        match self {
57            Self::ApiKey => "ApiKey",
58            Self::Bearer | Self::OAuthAccessToken => "Bearer",
59        }
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn stable_bytes_are_unique() {
69        let api = TokenSpec::api_key().stable_bytes();
70        let bearer = TokenSpec::bearer().stable_bytes();
71        let oauth = TokenSpec::oauth_access_token().stable_bytes();
72
73        assert_ne!(api, bearer);
74        assert_ne!(api, oauth);
75        assert_ne!(bearer, oauth);
76    }
77
78    #[test]
79    fn kind_names_are_stable() {
80        assert_eq!(TokenSpec::api_key().kind_name(), "api_key");
81        assert_eq!(TokenSpec::bearer().kind_name(), "bearer");
82        assert_eq!(
83            TokenSpec::oauth_access_token().kind_name(),
84            "oauth_access_token"
85        );
86    }
87
88    #[test]
89    fn authorization_schemes_are_stable() {
90        assert_eq!(TokenSpec::api_key().authorization_scheme(), "ApiKey");
91        assert_eq!(TokenSpec::bearer().authorization_scheme(), "Bearer");
92        assert_eq!(
93            TokenSpec::oauth_access_token().authorization_scheme(),
94            "Bearer"
95        );
96    }
97}