@plinthjs/auth
v0.1.0
Published
Mason authentication: guards, user providers, and middleware (the Illuminate\Auth equivalent).
Maintainers
Readme
@plinthjs/auth
Mason's authentication layer, the Illuminate\Auth equivalent. The Authenticatable /
UserProvider / Guard contracts, an in-memory ArrayUserProvider over GenericUsers (passwords
verified through @plinthjs/encryption), a stateful SessionGuard (with attemptWhen, HTTP Basic,
remember-me and the logout variants), a stateless TokenGuard, an AuthManager for named guards,
password resets, signed email verification, the Laravel auth event classes, and HTTP middleware.
Install
npm install @plinthjs/authUsage
import { Hasher } from '@plinthjs/encryption'
import { Session } from '@plinthjs/session'
import { ArrayUserProvider, GenericUser, SessionGuard } from '@plinthjs/auth'
const hasher = new Hasher()
const users = new ArrayUserProvider(
[new GenericUser({ id: 1, email: '[email protected]', password: hasher.make('secret') })],
hasher,
)
const guard = new SessionGuard(users, new Session('session-id', {}))
await guard.attempt({ email: '[email protected]', password: 'secret' }) // true
await guard.check() // true
await guard.id() // 1
// Extra checks that must all pass before the user is logged in.
await guard.attemptWhen({ email: '[email protected]', password: 'secret' }, [
(user) => user.getAuthIdentifier() !== 2,
])
await guard.logout() // also cycles the remember tokenSessionGuard also offers once, loginUsingId, remember(user) / loginUsingRecaller(value),
basic() / onceBasic() (HTTP Basic, returning a 401 challenge response on failure),
logoutCurrentDevice() and logoutOtherDevices(password).
Token and request guards
import { Request } from '@plinthjs/http'
import { AuthManager, TokenGuard } from '@plinthjs/auth'
// Reads a bearer token, or the `api_token` query/input field.
const api = new TokenGuard(users, new Request({ headers: { authorization: 'Bearer tok-123' } }))
await api.user()
const auth = new AuthManager('web')
.extend('api', (request) => new TokenGuard(users, request!))
.viaRequest('custom', (request) =>
request.bearerToken() === 'tok-123' ? users.retrieveById(1) : null,
)
auth.guard('custom', new Request({ headers: { authorization: 'Bearer tok-123' } })) // a RequestGuard
auth.guard('nope') // throws: guard not definedMiddleware
import { HttpKernel, Response, Router } from '@plinthjs/http'
import { ArraySessionStore, SessionManager, startSession } from '@plinthjs/session'
import { authenticateSession, authenticateToken, getUser, requirePassword } from '@plinthjs/auth'
const router = new Router()
router.get('/me', (req) => Response.json({ id: getUser(req)?.getAuthIdentifier() ?? null }))
const sessions = new SessionManager(new ArraySessionStore())
const kernel = new HttpKernel(router, {
middleware: [startSession(sessions), authenticateSession(users)],
})authenticateSession(provider)resolves the user from the session (throwing a 401AuthenticationErrorotherwise) and logs out sessions whose stored password hash is stale.authenticateToken(provider, inputKey?)is the stateless API equivalent.redirectIfAuthenticated(provider, '/home')keeps signed-in users off guest pages.requirePassword('/confirm-password', seconds)demands a recentconfirmPassword(...).
Password resets
import { ArrayTokenRepository, PasswordBroker, PasswordStatus } from '@plinthjs/auth'
const broker = new PasswordBroker(users, new ArrayTokenRepository({ hasher, expireMinutes: 60 }))
let token = ''
await broker.sendResetLink({ email: '[email protected]' }, (user, issued) => {
token = issued // email the link to the user here
})
const status = await broker.reset(
{ email: '[email protected]', token, password: 'new-secret' },
async (user, password) => saveHashedPassword(user, hasher.make(password)),
)
status === PasswordStatus.PasswordResetEmail verification and events
EmailVerifier({ secret, expireMinutes }) signs a { expires, signature } payload for any user
implementing MustVerifyEmail; verify(user, payload) checks it and marks the email verified.
Guards, the broker and the verifier dispatch Attempting, Login, Failed, Logout,
PasswordReset, Verified and the other Laravel event classes through any object with a
dispatch(event) method (the AuthEvents seam), passed as the events option, or as the third
argument of the PasswordBroker constructor.
