atsw.js#
A very minimal OAuth browser client for atproto using a service worker. Make authenticated requests to your PDS using plain fetch calls. Check out a live demo.
This is very experimental — you should probably use atcute if you're building something real.
Installation#
Copy and paste atsw.js into your project! It's less than 500 lines of readable vanilla JavaScript.
Usage#
Client metadata#
Create a client-metadata.json:
{
"client_id": "https://example.com/client-metadata.json",
"client_uri": "https://example.com",
"redirect_uris": ["https://example.com"],
"application_type": "native",
"client_name": "Your App",
"dpop_bound_access_tokens": true,
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"scope": "atproto repo?collection=com.atproto.server.getSession",
"token_endpoint_auth_method": "none"
}
Setup#
Register atsw.js as a service worker:
await navigator.serviceWorker.register("./atsw.js", { type: "module" });
await navigator.serviceWorker.ready;
Logging in#
Pass the configuration and handle to logIn. It'll automatically redirect the user to their PDS:
import { logIn } from "./atsw.js";
const config = {
clientId: "https://example.com/client-metadata.json",
redirectUri: "https://example.com",
scope: "atproto repo?collection=com.atproto.server.getSession"
};
await logIn(config, "you.bsky.social");
The configuration must match the corresponding fields from client-metadata.json.
Resuming sessions#
When the user is redirected back to your application, they'll be authenticated! You can get the PDS host from the session:
import { getSession, listSessions } from "./atsw.js";
const session = await getSession("did:plc:users-did-goes-here");
const allSessions = await listSessions();
Making requests#
Make authenticated requests using plain fetch calls:
const res = await fetch(`${session.pds}/xrpc/com.atproto.server.getSession`);
const data = await res.json();
Any requests to the authenticated user's PDS will automatically include an auth token and handle DPoP retries and token refreshes.
If a user has multiple authenticated sessions with the same PDS, use the x-atsw-did header to indicate which user's authentication token to use:
const res = await fetch(`${session.pds}/xrpc/com.atproto.server.getSession`, {
headers: { "x-atsw-did": session.did }
});
const data = await res.json();
Logging out#
import { logout } from "./atsw.js";
await logout(session.did);
How does it work?#
atsw.js uses a service worker as an "auth proxy" that intercepts outgoing and incoming requests.
- When the PDS redirects the user back to your callback URL, the service worker reads the URL and finishes setting up the session.
- When you make a request to the authenticated user's PDS, the service worker adds an
authorizationheader with the user's auth token. If the server rejects the DPoP nonce, the service worker will automatically retry with a new one. - If the auth token has expired, the service worker will refresh it before making a request.