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

Result returned after creating a login session.

pub type LoginSession {
  LoginSession(session: session.Session, csrf_token: String)
}

Constructors

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.

  • 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_ws does, 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).

✨ Search Document