brazier/auth
Types
Result returned after creating a login response.
pub type LoginResult {
LoginResult(
session: session.Session,
csrf_token: String,
response: response.Response(server.ResponseBody),
)
}
Constructors
-
LoginResult( session: session.Session, csrf_token: String, response: response.Response(server.ResponseBody), )
Result returned after creating a login session.
pub type LoginSession {
LoginSession(session: session.Session, csrf_token: String)
}
Constructors
-
LoginSession(session: session.Session, csrf_token: String)
Why a helper that builds a login or logout response failed.
pub type ResponseError {
CookieRejected(cookie.CookieError)
StoreFailed(session.SessionStoreError)
}
Constructors
-
CookieRejected(cookie.CookieError)The session cookie config is invalid; no session was created.
-
StoreFailed(session.SessionStoreError)The session store failed. Login: no session was created. Logout: the session may still exist, but its live sockets were told to close.
Session auth configuration.
pub type SessionConfig {
SessionConfig(
cookie_name: String,
csrf_header: String,
cookie_options: cookie.CookieOptions,
)
}
Constructors
-
SessionConfig( cookie_name: String, csrf_header: String, cookie_options: cookie.CookieOptions, )Arguments
- cookie_name
-
Cookie name that stores the opaque session ID.
- csrf_header
-
Header name used by state-changing API requests.
- cookie_options
-
Cookie attributes for login and logout responses.
Why a high-level auth helper rejected a request.
pub type SessionError {
MissingSessionCookie
InvalidSession
MissingUser
MissingCsrfToken
InvalidCsrfToken
SessionStoreUnavailable(reason: String)
}
Constructors
-
MissingSessionCookie -
InvalidSession -
MissingUser -
MissingCsrfToken -
InvalidCsrfToken -
SessionStoreUnavailable(reason: String)The session store could not answer (an External backend failed). The reason is logged; it is not part of any client-facing text.
Values
pub fn allow_guests_ws(
store: session.SessionStore,
config: SessionConfig,
) -> fn(request.Request(server.Connection)) -> Result(Nil, String)
WebSocket auth for apps whose pages guests may open too. Use it in place
of protect_ws when init renders a guest page (init_from_session).
- A live session with a user is bound to the socket, as
protect_wsdoes, so logout, rotation or expiry closes it with 4401. - No session cookie, a cookie naming no live session and a session without a user connect as a guest. They are not bound to anything.
- A socket that was bound stays signed in or closes: when a recheck finds its session gone, expired or without a user, it is refused and closes with 4401, so the page reloads for whoever the cookie now names. It never carries on as a guest with the signed-in page still on screen. This catches a revocation the socket missed (a logout on a node it could not reach, a row removed from an External database).
- A session store that cannot answer refuses the socket (401 before the upgrade, 4401 on a recheck): the socket cannot be verified either way, and treating it as a guest would hide the outage.
protect_ws refuses guests, so a guest page’s socket would reconnect
forever. The server still checks every event against the page’s view,
and update must refuse what a guest may not do.
Reference: Phoenix LiveView disconnects the sockets of a signed-out
live_socket_id; the client reconnects and mount re-checks the
session.
pub fn authenticated(
store: session.SessionStore,
config: SessionConfig,
handler: fn(
request.Request(server.Connection),
session.Session,
String,
) -> response.Response(server.ResponseBody),
) -> fn(request.Request(server.Connection)) -> response.Response(
server.ResponseBody,
)
Wrap an API route handler with session + current-user validation.
pub fn change_privileges(
store: session.SessionStore,
session_id: String,
updates: List(#(String, String)),
) -> Result(LoginSession, SessionError)
Change privileges (role, permissions, impersonation) on a live session.
Rotates the session id, applies updates to the new session, and issues a
new CSRF token. The old id is deleted and every WebSocket bound to it is
closed. The caller MUST send the returned session’s cookie
(with_session_cookie) and CSRF token to the client.
pub fn create_login(
store: session.SessionStore,
user_id: String,
) -> Result(LoginSession, SessionError)
Create a login session and session-bound CSRF token.
pub fn create_login_rotating(
store: session.SessionStore,
old_session_id: option.Option(String),
user_id: String,
) -> Result(LoginSession, SessionError)
Create a login session and CSRF token, deleting the previous session id.
As with login_rotating, a failed delete creates nothing.
pub fn csrf_authenticated(
store: session.SessionStore,
config: SessionConfig,
handler: fn(
request.Request(server.Connection),
session.Session,
String,
) -> response.Response(server.ResponseBody),
) -> fn(request.Request(server.Connection)) -> response.Response(
server.ResponseBody,
)
Wrap a state-changing API route with session, user, and CSRF validation.
pub fn csrf_protection(
store: session.SessionStore,
) -> fn(
request.Request(server.Connection),
fn(request.Request(server.Connection)) -> response.Response(
server.ResponseBody,
),
) -> response.Response(server.ResponseBody)
CSRF middleware — validates CSRF token on state-changing requests.
Allows GET, HEAD, OPTIONS. Requires valid token on POST, PUT, DELETE, PATCH.
Uses default_session_config(); see csrf_protection_with_config.
pub fn csrf_protection_with_config(
store: session.SessionStore,
config: SessionConfig,
) -> fn(
request.Request(server.Connection),
fn(request.Request(server.Connection)) -> response.Response(
server.ResponseBody,
),
) -> response.Response(server.ResponseBody)
CSRF middleware using the configured cookie and header names. Tokens are compared in constant time.
pub fn current_user(sess: session.Session) -> Result(String, Nil)
Get the current user ID from a session.
pub fn current_user_from_request(
req: request.Request(body),
store: session.SessionStore,
config: SessionConfig,
) -> Result(String, SessionError)
Get the current user ID from a request cookie.
pub fn current_user_route(
path: String,
store: session.SessionStore,
config: SessionConfig,
) -> api.Route
Standard current-user route.
pub const default_csrf_header_name: String
pub fn default_session_config() -> SessionConfig
Secure production defaults: HttpOnly, Secure, SameSite=Lax.
pub const default_session_cookie_name: String
pub fn dev_session_config() -> SessionConfig
Explicit local-development config.
This is intentionally named dev_ because it disables the Secure cookie
attribute so localhost HTTP examples can receive the session cookie.
pub fn error_to_string(err: SessionError) -> String
Convert a high-level auth error into a stable text reason.
pub fn init_from_session(
store: session.SessionStore,
config: SessionConfig,
guest_init guest_init: fn(brazier.Context) -> Result(
model,
error.InitError,
),
authenticated_init authenticated_init: fn(
brazier.Context,
session.Session,
String,
) -> Result(model, error.InitError),
) -> fn(brazier.Context) -> Result(model, error.InitError)
An init for apps whose first state depends on who is signed in. Pass
it as a constructor’s init, so the server render already shows the
signed-in page.
- A live session with a user runs
authenticated_init(ctx, session, user_id). - No session cookie runs
guest_init(ctx). - A cookie naming no live session (logged out, expired, rotated) and a
session without a user are logged as warnings and run
guest_init. - A session store that cannot answer is not a guest: the session cannot
be verified either way. It returns
error.unavailable, so the server render answers 503 and the WebSocket closes with 4503 (the browser reconnects with backoff). The store’s reason is logged and never sent to the browser.
pub fn login(
store: session.SessionStore,
user_id: String,
) -> Result(session.Session, SessionError)
Log in a user — creates a fresh session and stores the user ID.
Always issues a NEW session id. If the request already carried a session
(anonymous or another user’s), use login_rotating so the old id is
deleted and cannot be replayed (session fixation).
pub fn login_json_response(
store: session.SessionStore,
user_id: String,
config: SessionConfig,
) -> Result(LoginResult, ResponseError)
Log in a user and return a standard JSON response.
The response body is {"ok":true,"csrf":"..."} and the session ID is
sent only as an HttpOnly cookie.
pub fn login_json_response_rotating(
store: session.SessionStore,
old_session_id: option.Option(String),
user_id: String,
config: SessionConfig,
) -> Result(LoginResult, ResponseError)
Like login_json_response, but deletes the previous session id first.
pub fn login_response(
store: session.SessionStore,
user_id: String,
resp: response.Response(server.ResponseBody),
config: SessionConfig,
) -> Result(LoginResult, ResponseError)
Log in a user and attach an HttpOnly session cookie to a response.
The returned CSRF token is stored in the server-side session and should be sent by the app’s client code in the configured CSRF header for state-changing API requests.
pub fn login_rotating(
store: session.SessionStore,
old_session_id: option.Option(String),
user_id: String,
) -> Result(session.Session, SessionError)
Log in a user, deleting the previous session id (if any).
Live WebSockets bound to the old session are told to close. When the old session cannot be deleted no new one is created: logging in must never leave a replayable previous id behind.
pub fn login_route(
path: String,
store: session.SessionStore,
config: SessionConfig,
authenticate: fn(request.Request(server.Connection)) -> Result(
String,
String,
),
) -> api.Route
Standard login route.
authenticate receives the request so the app can read form, JSON, or
headers. Return Ok(user_id) to create the session; return Error(reason)
to reject the login with 401.
pub fn logout(
store: session.SessionStore,
session_id: String,
) -> Result(Nil, SessionError)
Log out — destroys the session. Its live sockets are told to close even
when the store fails (see session.try_delete); the error is returned.
pub fn logout_response(
store: session.SessionStore,
sess: session.Session,
resp: response.Response(server.ResponseBody),
config: SessionConfig,
) -> Result(response.Response(server.ResponseBody), ResponseError)
Destroy the session and attach a matching cookie deletion header. The session is destroyed even when the cookie config is invalid (the error is still returned). When the store fails no response is built: clearing the cookie would tell the user they are logged out while the session may still exist.
pub fn logout_route(
path: String,
store: session.SessionStore,
config: SessionConfig,
) -> api.Route
Standard logout route.
pub fn protect_api(
store: session.SessionStore,
config: SessionConfig,
handler: fn(
request.Request(server.Connection),
session.Session,
String,
) -> response.Response(server.ResponseBody),
) -> fn(request.Request(server.Connection)) -> response.Response(
server.ResponseBody,
)
Alias for the common authenticated API route wrapper.
pub fn protect_api_with_csrf(
store: session.SessionStore,
config: SessionConfig,
handler: fn(
request.Request(server.Connection),
session.Session,
String,
) -> response.Response(server.ResponseBody),
) -> fn(request.Request(server.Connection)) -> response.Response(
server.ResponseBody,
)
Alias for the common authenticated + CSRF API route wrapper.
pub fn protect_ws(
store: session.SessionStore,
config: SessionConfig,
) -> fn(request.Request(server.Connection)) -> Result(Nil, String)
Alias for the common WebSocket protection hook.
pub fn require_auth(
store: session.SessionStore,
) -> fn(
request.Request(server.Connection),
fn(request.Request(server.Connection)) -> response.Response(
server.ResponseBody,
),
) -> response.Response(server.ResponseBody)
Auth middleware — rejects unauthenticated requests with 401.
Checks for a session cookie and validates it against the store.
Uses default_session_config(); see require_auth_with_config.
pub fn require_auth_with_config(
store: session.SessionStore,
config: SessionConfig,
) -> fn(
request.Request(server.Connection),
fn(request.Request(server.Connection)) -> response.Response(
server.ResponseBody,
),
) -> response.Response(server.ResponseBody)
Auth middleware using the configured cookie name.
pub fn response_error_to_string(err: ResponseError) -> String
Convert a login or logout response error into a text reason.
pub fn session_from_context(
ctx: brazier.Context,
store: session.SessionStore,
config: SessionConfig,
) -> Result(session.Session, SessionError)
The session named by the Context’s session cookie, for code that runs
in an app’s init. The same outcomes as session_from_request.
pub fn session_from_request(
req: request.Request(body),
store: session.SessionStore,
config: SessionConfig,
) -> Result(session.Session, SessionError)
Get the current session from a request cookie.
pub fn session_id_from_request(
req: request.Request(body),
config: SessionConfig,
) -> Result(String, SessionError)
Read the raw session id from the configured cookie.
Does NOT validate the session against a store; use session_from_request
for that. Intended for binding other credentials (e.g. the WebSocket join
token) to the session via session.binding_fingerprint.
pub fn session_routes(
store: session.SessionStore,
config: SessionConfig,
authenticate: fn(request.Request(server.Connection)) -> Result(
String,
String,
),
) -> fn(request.Request(server.Connection)) -> option.Option(
response.Response(server.ResponseBody),
)
Standard /api/login, /api/me, and /api/logout route handler.
pub fn validate_csrf(
req: request.Request(body),
sess: session.Session,
config: SessionConfig,
) -> Result(Nil, SessionError)
Validate the session-bound CSRF header for a request.
pub fn with_session_cookie(
resp: response.Response(server.ResponseBody),
sess: session.Session,
config: SessionConfig,
) -> Result(
response.Response(server.ResponseBody),
cookie.CookieError,
)
Attach the configured HttpOnly session cookie to a response.
Errors when config names an invalid cookie (see cookie.validate).
pub fn ws_session_auth(
store: session.SessionStore,
config: SessionConfig,
) -> fn(request.Request(server.Connection)) -> Result(Nil, String)
WebSocket auth function that accepts only requests with a valid session user.
The transport runs ws_auth inside the WebSocket handler process, both
before the upgrade and periodically afterwards. On success this binds that
process to the session, so logout, rotation, or expiry of the session
closes the live socket immediately (see session.bind_current_process).