io.eezo.auth
Members list
Type members
Classlikes
What identifies the user behind a request, for one way of signing in: a password.
What identifies the user behind a request, for one way of signing in: a password.
Built from two functions, a URL and how long a sign in should last, and nothing else. find turns the key in the session into the application's own user, and credentials turns an email into the key and the stored hash that go with it. Both are the application's, which is what keeps modules/auth off modules/db: the guard never learns that a table exists, and examples/blog supplies two one-line closures over its own read. Constructing one therefore touches nothing, so a boot that never serves opens no connection.
credentials says where the hash is and stops there; comparing it against what was typed is the guard's, in submitted and nowhere else. An application that did its own comparison had to be trusted to answer an unknown email as slowly as a wrong password, which is not a thing a one-line lookup does by accident, and the plain text had to travel out to it to be compared.
The login it carries is unthrottled, and deliberately: every attempt that decodes, right or wrong, known email or not, costs one bcrypt at Password's shipped strength, so a few hundred guesses a second is also a few hundred quarter seconds of this server's CPU a second. A body that will not decode, an empty password or one past the 72 bytes bcrypt reads, never gets that far. Refusing an address after so many tries is the reverse proxy's job, where the addresses and the rest of the application's traffic already are, and not something a guard can do honestly from inside one process.
One guard per user model. required, only and except are how it becomes a Guarded, and every declaration it makes carries carries, the same three route instances, so an application that guards anything has mounted the page it redirects to without writing a line.
Attributes
- Companion
- object
- Source
- Guard.scala
- Supertypes
Attributes
- Companion
- class
- Source
- Guard.scala
- Supertypes
- Self type
-
Guard.type
What a guard's required, only and except answer: the declaration a model writes, still holding the guard that made it.
What a guard's required, only and except answer: the declaration a model writes, still holding the guard that made it.
A io.eezo.http.Guarded and nothing more as far as every existing call site can tell, which is why given Guarded[Post] = User.guard.required still says exactly what it said. The one thing it adds is owning, and it can add it only because it still knows who the guard says is behind a request, its currentUser: a plain Guarded has a Route => Route and no way back to the user, so ownership could not be chained onto one.
Value parameters
- currentUser
-
who is signed in, as the key that a row's owner field holds, and nobody when the request carries no live sign in. Read out of the session rather than through the guard's
find, because a covered route is a guarded route and the wrapper already looked the user up before the handler ran; a second lookup per request would buy nothing. - identify
-
the guard's own naming, handed on to
Guardedand read by nothing here; see io.eezo.http.Guarded.identify.
Attributes
- Source
- Owning.scala
- Supertypes
What a login form decodes to.
What a login form decodes to.
Keyless on purpose, which is the shape Form was built to allow and Resource to refuse: it will never be a row, it mounts nothing, and the guard's own POST route is the only handler that reads one. Password.Plain rather than Password is the whole point of the pair: this is the text as typed, on its way to a verify, and the type says so all the way from the input to the one place a Guard verifies anything.
Attributes
- Source
- Guard.scala
- Supertypes
An ownership declaration waiting to be told which routes it covers.
An ownership declaration waiting to be told which routes it covers.
The chain ends in one of three, and the three read as what they are: all for a model nobody but its owner may even see, except for one whose reading routes are everybody's, only for the odd shape neither of those describes. There is no default, because "which routes does owning a row decide" has no safe silence: covering everything makes a blog's index private, and covering nothing makes the declaration do nothing at all.
These are not io.eezo.http.Guarded's only and except under another name, and the difference is worth saying twice. There the question is who has to be signed in. Here every covered route already needs a signed in user, and the question is which of them are narrowed to that user's own rows.
Attributes
- Source
- Owning.scala
- Supertypes
Attributes
- Source
- Password.scala
- Supertypes
- Self type
-
Password.type
Types
The stored hash of a user's password: one self describing string that names its own algorithm and its own cost, and the only form of a password a model carries or a table stores.
The stored hash of a user's password: one self describing string that names its own algorithm and its own cost, and the only form of a password a model carries or a table stores.
Opaque over String rather than a class, because the hash is what goes in the column and Column[String].imap(Password.stored)(_.value) is the whole of its storage. It is not a secret from the database that holds it, so nothing here redacts; Password.Plain is the value that does.
Being opaque is what keeps the two directions apart. A String read out of a row is a hash and becomes one through Password.stored; a String typed into a browser is not, and the only door from there is Password.hash or the Field below, both of which hash. Nothing in between can pass one for the other by having the same underlying type.
Attributes
- Source
- Password.scala