AtOAuth

class AtOAuth(clientMetadataUrl: String, redirectUri: String, sessionStore: OAuthSessionStore, httpClient: HttpClient, json: Json = Json { ignoreUnknownKeys = true }, scope: String = "atproto transition:generic")

AT Protocol OAuth 2.0 flow orchestrator for public clients.

Implements the full authorization flow: handle → DID → PDS → authorization server discovery, PAR with PKCE + DPoP, browser-based authorization, token exchange, and session management with transparent refresh.

Supports two entry points:

  • beginLogin — keyed on a user's handle/DID; full discovery chain.

  • beginSignup — keyed on a known auth server (default bsky.social); short-circuits discovery and sends OIDC prompt=create so the auth server renders its signup UI. Identity is hydrated post-token-exchange.

Both flows complete via completeLogin — the redirect handling is shared.

Consumer usage

val oauth = AtOAuth(
clientMetadataUrl = "https://example.app/oauth/client-metadata.json",
redirectUri = "app.example:/oauth-redirect",
sessionStore = mySessionStore,
httpClient = myKtorClient,
)
// Login path:
val authUrl = oauth.beginLogin("alice.bsky.social")
// Signup path:
val signupUrl = oauth.beginSignup() // defaults to bsky.social
// Both paths: open the URL in a browser, then call:
oauth.completeLogin(redirectUri)
val client = oauth.createClient()

Requesting additional scopes

To request umbrella scopes beyond the default atproto transition:generic (e.g. transition:chat.bsky for Bluesky chat, transition:email for email), pass them via scope. The value must be a subset of the scope field declared in the hosted client-metadata.json document.

val oauth = AtOAuth(
clientMetadataUrl = "...",
redirectUri = "app.example:/oauth-redirect",
sessionStore = ...,
httpClient = ...,
scope = "atproto transition:generic transition:chat.bsky",
)

The value must be a subset of the scope field declared in the hosted client metadata document.

Constructors

Link copied to clipboard
constructor(clientMetadataUrl: String, redirectUri: String, sessionStore: OAuthSessionStore, httpClient: HttpClient, json: Json = Json { ignoreUnknownKeys = true }, scope: String = "atproto transition:generic")

Functions

Link copied to clipboard
suspend fun beginLogin(handleOrDid: String): String

Starts the OAuth login flow.

Link copied to clipboard
suspend fun beginSignup(authServer: String = "bsky.social", requirePromptCreateSupport: Boolean = true): String

Starts the OAuth signup flow against a known authorization server.

Link copied to clipboard
suspend fun completeLogin(redirectUri: String)

Completes the OAuth login flow after the browser redirects back.

Link copied to clipboard
suspend fun createClient(): XrpcClient

Creates an authenticated XrpcClient from the persisted session. The client uses DpopAuthProvider for DPoP proof-of-possession on every request.

Link copied to clipboard
suspend fun logout()

Revokes the current tokens on the authorization server, then clears the persisted session. If revocation fails (network error, missing endpoint), the local session is still cleared.