Example relying app
This is the canonical copy-paste reference: the app asks Atmosphere to choose an account, verifies the signed selection on callback, starts its own AT Protocol OAuth flow, then creates an app-owned signed-in session.
Popup mode keeps this page open and finishes through the same server-verified callback.
The picker returns to the reference callback. When the token checks pass, the callback redirects into the app's ATProto OAuth start route.
<button
data-atmosphere-login
data-client-id="https://atmosphereaccount.com/examples/atmosphere-login/client-metadata.json"
data-return-uri="https://atmosphereaccount.com/examples/atmosphere-login/callback"
data-scope="atproto"
data-app-name="Atmosphere Login reference app"
data-app-homepage="https://atmosphereaccount.com/examples/atmosphere-login/app"
></button>
<script src="https://login.atmosphereaccount.com/atmosphere-login.js" defer></script><button
data-atmosphere-login
data-client-id="https://atmosphereaccount.com/examples/atmosphere-login/client-metadata.json"
data-return-uri="https://atmosphereaccount.com/examples/atmosphere-login/callback?mode=popup"
data-scope="atproto"
data-app-name="Atmosphere Login reference app"
data-app-homepage="https://atmosphereaccount.com/examples/atmosphere-login/app"
data-mode="popup"
></button>
<script src="https://login.atmosphereaccount.com/atmosphere-login.js" defer></script>const selection = AtmosphereLogin.consumeSelection({
clientId: "https://atmosphereaccount.com/examples/atmosphere-login/client-metadata.json",
});
if (!selection) return showSignedOut();
const verified = await verifyAtmosphereLoginCallback({
url: request.url,
publicJwk,
expectedIssuer: "https://login.atmosphereaccount.com",
expectedClientId: "https://atmosphereaccount.com/examples/atmosphere-login/client-metadata.json",
expectedReturnUri: "https://atmosphereaccount.com/examples/atmosphere-login/callback",
expectedState: selection.state,
replayStore,
});
if (!verified.ok) throw new Error(verified.error);
return redirect("/examples/atmosphere-login/oauth/start?" + new URLSearchParams({
handle: verified.claims.handle,
did: verified.claims.sub,
}));// /examples/atmosphere-login/oauth/start
await startAtprotoOAuth({
clientId: "https://atmosphereaccount.com/examples/atmosphere-login/oauth/client-metadata.json",
redirectUri: "https://atmosphereaccount.com/examples/atmosphere-login/oauth/callback",
scope: "atproto",
loginHint: verified.claims.handle || verified.claims.sub,
});
// /examples/atmosphere-login/oauth/callback
const account = await completeAtprotoOAuthCallback(request.url);
return createAppSession(account);