1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//! The pieces a sign-in screen is made of.
//!
//! `arcature::auth` owns the seams -- hash a password, bind a user to a
//! session, authorize an action. This module owns the handful of small,
//! security-critical decisions that sit between those seams and a login
//! form, and that every application otherwise rewrites: the ones where the
//! obvious implementation is wrong in a way nothing tells you about.
//!
//! Behind the `auth-flows` feature, which is off by default.
//!
//! # What lives here, and why it is not left to the application
//!
//! Each item here exists because the naive version leaks something:
//!
//! * [`CredentialChecker`] -- "no such user" and "wrong password" must be one
//! answer, and must cost the same. An `if let Some(user)` that skips the
//! Argon2 verification when the address is unknown turns the *response
//! time* into a working list of who has an account.
//! * [`EmailVerification`] -- a verification link has to be bound to the
//! address it was mailed to, not just to the account. A link that only names
//! the account verifies whichever address the account holds when it is
//! clicked, so registering with your own address, changing it to somebody
//! else's, and then clicking gets you a verified address you cannot read.
//! * [`LoginThrottle`] -- a form that answers in constant time and gives one
//! message away is still a free oracle if it will answer ten thousand
//! times. Counting failures per account is the obvious guard and it misses
//! the actual attack, which is one guess each against ten thousand
//! different accounts; counting them per account *only* also hands anybody
//! a way to lock anybody else out.
//! * [`PasswordConfirmation`] -- asking for the password again before
//! something irreversible only means anything if it can go stale, and the
//! obvious implementation cannot. A boolean in the session says a password
//! was proved at some point and never says when, so one confirmation covers
//! every irreversible action for as long as the session lives; and a
//! confirmation that does not name who made it is inherited by whoever holds
//! the session next.
//! * `PasswordResets` -- a reset link has to be spendable exactly once, and
//! the obvious implementation spends it twice. Checking "is this token
//! valid?" and then deleting it is two statements with a gap, and two
//! requests carrying the same link both pass the check before either
//! deletes. Behind the `auth-reset` feature, which is off by default because
//! it needs a database.
//! * `RememberTokens` -- a "stay signed in" cookie is a bearer credential
//! that lives for weeks, and the obvious implementation cannot tell that one
//! was copied. Rotating the secret on every use turns a stolen cookie into
//! something that both stops working and *announces itself*, because
//! whichever party goes second presents a secret that is already retired.
//! Behind the `auth-remember` feature, which is off by default because it
//! needs a database.
//!
//! # What does not live here
//!
//! The user table, the handlers, the routes, and the HTML. Those are the
//! application's. `arc new` writes none of them -- a fresh scaffold has no
//! user table and no sign-in route -- and `arc make:auth <name>` writes all
//! but the last: an account model, three controllers, a route collection and
//! a migration, headless, with the screens left to `arc make:page` and
//! `arc make:view`. This module is the part that has to be right rather than
//! the part that has to be yours.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;