ProtoRune.Atproto.OAuth.SessionManager (proto_rune v0.5.1)

Copy Markdown

A GenServer that keeps an OAuth session fresh for long-running applications.

The manager is opt-in: the SDK starts no processes on its own, so add it to the host application's supervision tree:

children = [
  {Registry, keys: :unique, name: MyApp.OAuthRegistry},
  {ProtoRune.Atproto.OAuth.SessionManager,
   session: session,
   client: client,
   store: {ProtoRune.Security.TokenStore.Dets, path: "/var/myapp/tokens.dets"},
   key: key,
   registry: MyApp.OAuthRegistry}
]

It schedules a refresh at 75% of the remaining token lifetime (from the session's expires_at; a 30 minute lifetime is assumed when the session carries none), rotates the tokens through ProtoRune.Atproto.OAuth.refresh/2 and persists each new session through the configured ProtoRune.Security.TokenStore backend under the session's DID.

Sessions are always encrypted at rest: the serialized session is encrypted with ProtoRune.Security.Crypto before it reaches the store, so TokenStore backends never see plaintext. This is mandatory because the session's dpop_key is private key material, as sensitive as the tokens themselves.

When a refresh fails the manager stops with {:refresh_failed, reason} and lets the supervisor decide the restart policy. logout/1 revokes the refresh token (best effort: a revocation failure does not block the logout), deletes the stored session and stops the process.

Hosts that manage their own processes can keep calling ProtoRune.Atproto.OAuth.refresh/2 and ProtoRune.Atproto.OAuth.revoke/2 directly; the manager only composes them.

Options

Telemetry

The manager emits :telemetry events following the same span conventions as the bot framework (see the ProtoRune.Bot moduledoc):

  • [:proto_rune, :oauth, :refresh, :start] / :stop / :exception - wrap each token refresh via :telemetry.span/3. Metadata: :did; stop events for failed refreshes also carry :error.
  • [:proto_rune, :oauth, :revoke, :start] / :stop / :exception - wrap the revocation performed by logout/1. Same metadata conventions as the refresh events.

Summary

Types

A reference to a manager: a pid, a registered name or a via tuple.

Functions

Returns a specification to start this module under a supervisor.

Revokes the session, deletes it from the store and stops the manager with reason :normal.

Returns the session currently held by server (a pid or, when the manager was started with :registry, a via tuple).

Starts a session manager. See the moduledoc for the accepted options.

Types

server()

@type server() :: GenServer.server()

A reference to a manager: a pid, a registered name or a via tuple.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

logout(server)

@spec logout(server()) :: :ok

Revokes the session, deletes it from the store and stops the manager with reason :normal.

Revocation is best effort: a failure is logged and emitted through telemetry but does not block the logout.

session(server)

Returns the session currently held by server (a pid or, when the manager was started with :registry, a via tuple).

start_link(opts)

@spec start_link(keyword()) :: GenServer.on_start()

Starts a session manager. See the moduledoc for the accepted options.