gleamunison/auth

Typed request identity, scopes, and bind policy.

Data over dispatch: routes map to scopes, identities carry scopes, and authorization is a pure decision. The Erlang HTTP/sync servers call required_scope/1 and bind_decision/3; they do not re-implement policy.

Types

pub type BindDecision {
  Allow
  Refuse(reason: String)
}

Constructors

  • Allow
  • Refuse(reason: String)
pub type BindInput {
  BindInput(
    address: String,
    profile: String,
    http_token_present: Bool,
    sync_token_present: Bool,
  )
}

Constructors

  • BindInput(
      address: String,
      profile: String,
      http_token_present: Bool,
      sync_token_present: Bool,
    )
pub type Identity {
  Anonymous
  LocalPeer
  Bearer(token_hash: String)
  ProxyPrincipal(name: String, scopes: List(Scope))
}

Constructors

  • Anonymous
  • LocalPeer
  • Bearer(token_hash: String)
  • ProxyPrincipal(name: String, scopes: List(Scope))
pub type Scope {
  Public
  DataRead
  DataWrite
  Admin
}

Constructors

  • Public
  • DataRead
  • DataWrite
  • Admin

Values

pub fn authorize(
  identity: Identity,
  path: String,
) -> Result(Identity, String)
pub fn bearer_identity(token: option.Option(String)) -> Identity
pub fn bind_decision(input: BindInput) -> BindDecision

Non-loopback binding is a refusal boundary, not a configuration option. Loopback binds always allow. Remote binds require the authenticated-remote profile and an authentication contract (HTTP token for the reverse proxy; direct TLS termination is intentionally not implemented in this runtime).

pub fn identity_scopes(identity: Identity) -> List(Scope)
pub fn is_loopback(address: String) -> Bool
pub fn required_scope(path: String) -> Scope
pub fn scope_at_least(have: Scope, need: Scope) -> Bool
pub fn scope_name(scope: Scope) -> String
✨ Search Document