rustlavel_oauth_provider/lib.rs
1//! rustlavel-oauth-provider: be an OAuth 2.1 authorization server.
2//!
3//! This is Laravel Passport's job — letting *other* applications sign users in
4//! through yours. The mirror half, signing your users in through somebody
5//! else's, is `rustlavel-oauth`, whose types this crate speaks so both ends
6//! agree on the wire format by construction.
7//!
8//! ```ignore
9//! use rustlavel_oauth_provider::prelude::*;
10//!
11//! let clients = MemoryClientStore::new().with(
12//! Client::confidential("checkout", &secret)
13//! .named("Checkout")
14//! .redirect_uri("https://checkout.test/oauth/callback")
15//! .scopes(Scopes::of(["orders.read", "orders.write"])),
16//! );
17//!
18//! App::new()?
19//! .plugin(OAuthProvider::new(
20//! AuthorizationServer::new(clients).issued_by("https://accounts.example.com"),
21//! ))
22//! .serve()
23//! .await
24//! ```
25//!
26//! That mounts six routes: `/oauth/authorize` (`GET` and `POST`),
27//! `/oauth/token`, `/oauth/revoke`, `/oauth/introspect`, and the RFC 8414
28//! discovery document at `/.well-known/oauth-authorization-server`. Guarding
29//! your own API with the tokens it issues is [`RequireToken`].
30//!
31//! # What this server will not do
32//!
33//! It is OAuth **2.1**, not 2.0 with the dangerous parts left in:
34//!
35//! * **PKCE is required**, for every client, and only `S256` is accepted.
36//! `plain` is refused, and an absent `code_challenge_method` is an error
37//! rather than a default — RFC 7636 §4.3 defaults it to `plain`, which would
38//! let a client turn PKCE off by omitting a parameter.
39//! * **The implicit flow is gone.** `response_type=token` returns an access
40//! token in a URL fragment, where it lands in browser history and in every
41//! script on the page.
42//! * **The password grant is gone.** It asks the user to type their password
43//! into somebody else's application, cannot carry a second factor, and hands
44//! the client a credential that works everywhere.
45//! * **Redirect URIs are matched byte for byte** against the registered set. No
46//! prefixes, no wildcards. [`redirect`] explains what each relaxation lets
47//! through.
48//! * **A leaked credential takes its family down with it.** A replayed
49//! authorisation code and a reused refresh token both revoke every token
50//! descended from that authorisation, because either one means two parties
51//! hold a credential that was issued to one.
52//!
53//! # Where the data lives
54//!
55//! Four traits — [`ClientStore`], [`CodeStore`], [`TokenStore`],
56//! [`ConsentStore`] — each with two implementations: an in-memory one for tests
57//! and a development server, and a table-backed one in `database` behind the
58//! `db` feature. **Deploy the second.** A restart empties the first, and an
59//! empty store answers "unknown token" to everything.
60//!
61//! The database is optional rather than absent: an application with no database
62//! compiles none of it, and one with its own tables implements the traits
63//! instead. See [`store`] for the reasoning, and for the two operations that
64//! must be atomic whichever you use.
65//!
66//! # Cryptography
67//!
68//! SHA-256, from `sha2`, is the only primitive here: every credential is stored
69//! as a digest and compared in constant time, and PKCE's `S256` is the same
70//! hash. Nothing else is invented. There is no JWT option and no JWKS: an
71//! access token here is opaque so that revoking it revokes it, which a
72//! self-contained signed token cannot offer.
73
74pub mod client;
75/// The stores, backed by a table each. Behind the `db` feature: an
76/// authorization server with its own storage should not drag the database
77/// package into one that has none.
78#[cfg(feature = "db")]
79pub mod database;
80pub mod clock;
81pub mod code;
82pub mod consent;
83pub mod endpoints;
84pub mod page;
85pub mod plugin;
86pub mod redirect;
87pub mod resource;
88pub mod server;
89pub mod store;
90pub mod token;
91
92pub use client::{Client, ClientStore, MemoryClientStore, generate_secret};
93pub use clock::Clock;
94pub use code::{AuthorizationCode, CodeStore, Consumption, MemoryCodeStore};
95pub use consent::{ConsentStore, Grant, MemoryConsentStore};
96pub use plugin::OAuthProvider;
97pub use resource::{BearerExt, RequireToken, TokenClaims};
98pub use server::{AuthorizationServer, Settings};
99pub use store::StoreFuture;
100pub use token::{AccessToken, MemoryTokenStore, RefreshToken, TokenStore};
101
102/// The protocol vocabulary, re-exported so an application needs one import.
103pub use rustlavel_oauth::{ChallengeMethod, OAuthError, OAuthErrorCode, Scopes, TokenResponse};
104
105pub use rustlavel_core::{Error, Result};
106
107/// What a `main.rs` or a controller imports to use this package.
108pub mod prelude {
109 pub use crate::{
110 AuthorizationServer, BearerExt, Client, ClientStore, CodeStore, ConsentStore, Grant,
111 MemoryClientStore, MemoryCodeStore, MemoryConsentStore, MemoryTokenStore, OAuthProvider,
112 RequireToken, Scopes, Settings, TokenClaims, TokenStore, generate_secret,
113 };
114}