A lexicon-driven AppView for ATProto.
Something went wrong. Try again.
README.md
@happyview/oauth-client-node #
Node.js OAuth client for authenticating with a HappyView instance using AT Protocol.
Built on top of @happyview/oauth-client. Matches the API surface of @atproto/oauth-client-node.
Installation #
npm install @happyview/oauth-client-node
Usage #
Setup #
import { HappyViewNodeClient } from "@happyview/oauth-client-node";
const client = new HappyViewNodeClient({
instanceUrl: "https://happyview.example.com",
clientId: "https://example.com/oauth-client-metadata.json",
clientKey: "hvc_your_client_key",
clientSecret: "hvs_your_secret", // optional, for confidential clients
redirectUri: "https://example.com/oauth/callback",
storage: myStorageAdapter,
});
Authorize #
Generate an authorization URL and redirect the user:
const url = await client.authorize("alice.bsky.social");
// Redirect the user to url.href
With options:
const url = await client.authorize("alice.bsky.social", {
scope: "atproto transition:generic",
redirect_uri: "https://example.com/alt-callback",
prompt: "login",
display: "page",
});
Callback #
On your callback route, process the OAuth response:
const params = new URLSearchParams(req.url.split("?")[1]);
const { session, state } = await client.callback(params);
Restore Session #
Restore a session by DID:
const session = await client.restore("did:plc:abc123");
Session #
// Authenticated requests
const response = await session.fetchHandler(
"/xrpc/com.example.getStuff?limit=10",
{ method: "GET" },
);
// Token metadata
const info = session.getTokenInfo();
// { sub, scope, iss, aud }
// Self-revoke
await session.signOut();
Using with @atproto/api #
import { Agent } from "@atproto/api";
const agent = new Agent(session);
const profile = await agent.getProfile({ actor: agent.did });
Revoke Session #
await client.revoke("did:plc:abc123");
// or
await session.signOut();
Abort Request #
const url = await client.authorize("alice.bsky.social");
// ...later:
await client.abortRequest(url);
Validate Client Metadata #
const metadata = await HappyViewNodeClient.fetchMetadata({
clientId: "https://example.com/oauth-client-metadata.json",
});
Identity Resolution #
const did = await client.handleResolver.resolve("alice.bsky.social");
const doc = await client.didResolver.resolve(did);
Storage #
You must provide a StorageAdapter. The built-in MemoryStorage works for testing but won't survive restarts:
import { MemoryStorage } from "@happyview/oauth-client-node";
const client = new HappyViewNodeClient({
// ...
storage: new MemoryStorage(),
});
For production, implement the StorageAdapter interface backed by your database or cache:
interface StorageAdapter {
get(key: string): Promise<string | null>;
set(key: string, value: string): Promise<void>;
delete(key: string): Promise<void>;
}
Exports #
This package re-exports everything from @happyview/oauth-client, plus:
HappyViewNodeClient-- the main Node.js clientAuthorizeOptions,CallbackOptions,HappyViewNodeClientOptionstypes