Skip to main content

uselesskey_token_spec/
lib.rs

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