| 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 | |
| 25 | pub mod access; |
| 26 | pub mod password; |
| 27 | pub mod scope; |
| 28 | pub mod session; |
| 29 | pub mod token; |
| 30 | |
| 31 | pub use access::{Access, Permission, Viewer, access}; |
| 32 | pub use password::{HashParams, hash_password, verify_password}; |
| 33 | pub use scope::{Scope, format_scopes, parse_scopes}; |
| 34 | pub use session::{SESSION_TOKEN_BYTES, SessionToken, new_session_token, session_id}; |
| 35 | pub 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)] |
| 44 | pub 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 | } |