fabrica

hanna/fabrica

2639 bytes
Raw
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Authentication and authorization primitives.
6//!
7//! This crate is deliberately I/O-free: it turns bytes into decisions and never
8//! touches the database or the network. The [`store`](../store/index.html) crate
9//! persists what these functions produce (password hashes, session-id digests,
10//! token rows) and the `web`/`api`/`ssh` crates call in at request time.
11//!
12//! Four concerns, one per module:
13//!
14//! * [`password`] — Argon2id hashing and constant-time verification. A missing
15//! hash still does the work of a real verify so account existence never leaks
16//! through timing.
17//! * [`session`] — opaque session tokens: 32 bytes of OS randomness handed to the
18//! browser, only their SHA-256 kept server-side.
19//! * [`token`] — HS256 JSON Web Tokens for the API, minted and verified against
20//! the instance secret. Revocation is a database concern; this module only
21//! proves a token is authentic and unexpired.
22//! * [`scope`] and [`access`] — the authorization vocabulary. [`access::access`]
23//! is the single, exhaustively tested function every route funnels through.
24
25pub mod access;
26pub mod password;
27pub mod scope;
28pub mod session;
29pub mod token;
30
31pub use access::{Access, Permission, Viewer, access};
32pub use password::{HashParams, hash_password, verify_password};
33pub use scope::{Scope, format_scopes, parse_scopes};
34pub use session::{SESSION_TOKEN_BYTES, SessionToken, new_session_token, session_id};
35pub use token::{Claims, mint_jwt, new_secret, verify_jwt};
36
37/// Errors produced by the authentication primitives.
38///
39/// Variants stay coarse on purpose: a caller authenticating a request should not
40/// be able to tell *why* a credential failed (bad signature vs. expired vs.
41/// malformed), so verification failures collapse into a single
42/// [`AuthError::InvalidToken`].
43#[derive(Debug, thiserror::Error)]
44pub enum AuthError {
45 /// A password could not be hashed, or a stored hash could not be parsed.
46 /// Carries the library's message; never contains the password itself.
47 #[error("password hashing failed: {0}")]
48 Hash(String),
49
50 /// A JWT was malformed, carried a bad signature, or had expired. One variant
51 /// for every failure mode so nothing distinguishes them to the caller.
52 #[error("invalid or expired token")]
53 InvalidToken,
54
55 /// A scope string held a token outside the known set.
56 #[error("unknown scope {0:?}")]
57 UnknownScope(String),
58}