Skip to main content

Overview

The @atproto/oauth-client-browser package provides a complete OAuth client implementation specifically designed for browser-based Single Page Applications (SPAs). It uses WebCrypto for cryptographic operations and IndexedDB for persistent storage.
This package is optimized for client-side applications without a backend server. For applications with a backend, consider using @atproto/oauth-client-node on the server side for enhanced security.

Installation

Requirements

  • Modern browser with WebCrypto API support
  • HTTPS connection (required for WebCrypto)
  • IndexedDB support
  • ECMAScript 2020+ features
WebCrypto requires HTTPS. For local development, use http://127.0.0.1 (not http://localhost) or set up a local HTTPS server.

Quick Start

BrowserOAuthClient Class

Static Methods

load

Creates a new browser OAuth client by loading metadata from the client ID.
string
required
The client ID URL. Must be either:
  • https:// URL for discoverable clients (fetches metadata)
  • http://127.0.0.1 or http://[::1] for loopback clients (development)
string | URL | HandleResolver
Service URL for resolving AT Protocol handles. Commonly 'https://bsky.social'.
Using Bluesky’s resolver may leak user IPs and handles to Bluesky. Consider running your own resolver or PDS.
'query' | 'fragment'
default:"'fragment'"
How OAuth responses are returned:
  • fragment: Parameters in URL fragment (safer, not sent to server)
  • query: Parameters in URL query string
string
PLC directory URL for DID resolution. Defaults to 'https://plc.directory'.
AbortSignal
Signal to cancel metadata loading.
Example:

Constructor

Creates a client with inline metadata (avoids network request).
OAuthClientMetadataInput
Client metadata object. If omitted, uses loopback client metadata based on current origin.
'query' | 'fragment'
default:"'fragment'"
OAuth response mode.
string | URL | HandleResolver
Handle resolver service.
string
PLC directory URL.
Fetch
Custom fetch implementation.
Example:

Instance Methods

init

Initializes the client on page load. Must be called once when your app loads. This method:
  1. Checks URL for OAuth callback parameters
  2. Processes callback if present
  3. Restores the last active session if available
OAuthSession
The authenticated session.
string
The application state from authorization (only present on callbacks).
Returned when no session is available.
Example:
Call init() only once per page load. Calling it multiple times may cause errors or unexpected behavior.

signIn

Initiates sign-in flow and redirects the user. This promise never resolves (redirects immediately).
string
required
User’s handle (e.g., alice.bsky.social) or DID.
string
Application state to preserve.
'none' | 'login' | 'consent'
Authorization prompt behavior:
  • none: Silent sign-in (SSO)
  • login: Force re-authentication
  • consent: Force re-consent
string
OAuth scopes to request.
string
Preferred UI languages (e.g., 'fr-CA fr en').
AbortSignal
Signal to cancel authorization.
Example:

signInCallback

Manually process OAuth callback. Usually not needed (use init() instead).
URLSearchParams
URL parameters. Defaults to current page’s URL parameters.
string
The redirect URI used in authorization.
OAuthSession
The authenticated session.
string
The application state.
Example:

restore

Restores a previously authenticated session.
string
required
The user’s DID.
boolean | 'auto'
default:"'auto'"
Token refresh behavior:
  • 'auto': Refresh if expired (recommended)
  • true: Force refresh
  • false: Use cached tokens
OAuthSession
The restored session.
Throws:
  • TokenRefreshError if session cannot be refreshed
  • TokenRevokedError if session was revoked
Example:

getSession

Retrieves a session without triggering refresh.
string
required
The user’s DID.
OAuthSession
The session if available.
If no session exists for this DID.

revoke

Revokes and deletes a session.
string
required
The user’s DID to revoke.
Example:

Properties

ClientMetadata
The client metadata being used.
OAuthResponseMode
The configured response mode.

Session Management

The browser client automatically manages sessions using IndexedDB:

Automatic Storage

  • Sessions persist across page reloads
  • Tokens refresh automatically when expired
  • DPoP keys stored securely in IndexedDB
  • Cross-tab synchronization using BroadcastChannel

Multi-Tab Support

Monitoring Sessions

OAuth Session Usage

The OAuthSession returned by the client can be used directly:

With @atproto/api

Direct API Calls

Development Setup

Loopback Client (localhost)

For local development without deploying client metadata:
Requirements:
  • Origin must be http://127.0.0.1:<port> or http://[::1]:<port>
  • Cannot use http://localhost:<port> (browser security)
  • Limited features: no custom branding, shorter token lifetime
Auto-redirect from localhost:

Production Setup

For production, host your client metadata: 1. Create client-metadata.json:
2. Serve with correct headers:
3. Load in your app:

Complete Example

React Application

Vanilla JavaScript

Error Handling

Security Considerations

WebCrypto Key Storage

DPoP keys are stored in IndexedDB as non-extractable CryptoKeys:

Storage Security

  • IndexedDB: Encrypted at rest by browser
  • Same-origin policy: Data only accessible by your origin
  • No localStorage: Tokens not exposed to JavaScript string access
  • Cross-tab sync: Uses BroadcastChannel (same-origin only)

HTTPS Requirement

WebCrypto requires HTTPS in production. HTTP is only allowed for 127.0.0.1 and [::1] loopback addresses.

Token Lifecycle

  • Access tokens refresh automatically before expiration
  • Refresh tokens stored encrypted in IndexedDB
  • Sessions auto-deleted on server-side revocation
  • Stale sessions cleaned up on init

Browser Compatibility

Required APIs

  • ✓ WebCrypto API (crypto.subtle)
  • ✓ IndexedDB
  • ✓ BroadcastChannel (optional, for multi-tab)
  • ✓ Web Locks API (optional, for concurrent refresh prevention)
  • ✓ ECMAScript 2020 features

Supported Browsers

  • Chrome 87+
  • Firefox 78+
  • Safari 14+
  • Edge 88+

Polyfills

No polyfills required for modern browsers. Older browsers may need:

Utility Functions

buildLoopbackClientId

Builds a loopback client ID from the current location.

@atproto/oauth-client

Core OAuth client (platform-agnostic)

@atproto/oauth-client-node

Node.js OAuth client for backend apps

@atproto/api

High-level AT Protocol API

@atproto/crypto

Cryptographic operations and key management

TypeScript Types