{"id":16766,"library":"better-auth-atproto","title":"better-auth AT Protocol (Bluesky) Plugin","description":"This package provides an AT Protocol (Bluesky) OAuth plugin for the `better-auth` framework. It enables users to sign in with their Bluesky or any AT Protocol Personal Data Server (PDS) account, leveraging OAuth 2.0 with PKCE and DPoP for secure authentication. Currently at version 0.1.0 (initial release), its release cadence is expected to be event-driven as a new, specialized plugin. Key differentiators include built-in support for localhost development without tunnels via AT Protocol's loopback client, automatic hosting of necessary OAuth discovery endpoints (`client-metadata.json` and `jwks.json`), and persistence of OAuth state and session data through `better-auth`'s database adapter. It also features automatic profile synchronization, updating user details like DID, handle, and display name.","status":"active","version":"0.1.0","language":"javascript","source_language":"en","source_url":"https://github.com/vineyardbovines/better-auth-atproto","tags":["javascript","better-auth","better-auth-plugin","atproto","bluesky","oauth","did","dpop"],"install":[{"cmd":"npm install better-auth-atproto","lang":"bash","label":"npm"},{"cmd":"yarn add better-auth-atproto","lang":"bash","label":"yarn"},{"cmd":"pnpm add better-auth-atproto","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Core authentication framework dependency.","package":"better-auth","optional":false},{"reason":"Schema validation, likely for configuration or API responses within the better-auth ecosystem.","package":"zod","optional":false}],"imports":[{"note":"This is the primary named export for the server-side plugin. The package is ESM-first.","wrong":"const atprotoAuth = require('better-auth-atproto');","symbol":"atprotoAuth","correct":"import { atprotoAuth } from 'better-auth-atproto';"},{"note":"The client-side plugin is imported from a specific subpath. The README snippet incorrectly references 'better-auth-bsky/client' due to a likely typo.","wrong":"import { atprotoAuthClient } from 'better-auth-atproto';","symbol":"atprotoAuthClient","correct":"import { atprotoAuthClient } from 'better-auth-atproto/client';"},{"note":"The core 'better-auth' factory function is a named export, not a default export.","wrong":"import betterAuth from 'better-auth';","symbol":"betterAuth","correct":"import { betterAuth } from 'better-auth';"},{"note":"The client-side factory for 'better-auth' is imported from its dedicated client subpath.","wrong":"import { createAuthClient } from 'better-auth';","symbol":"createAuthClient","correct":"import { createAuthClient } from 'better-auth/client';"}],"quickstart":{"code":"import { betterAuth } from 'better-auth';\nimport { atprotoAuth } from 'better-auth-atproto';\nimport { createAuthClient } from 'better-auth/client';\nimport { atprotoAuthClient } from 'better-auth-atproto/client';\n\n// 1. (Optional) Generate a private key for production:\n// openssl ecparam -name prime256v1 -genkey -noout -out ec-private.pem\n\n// 2. Server-side configuration\nexport const auth = betterAuth({\n  baseURL: process.env.NEXT_PUBLIC_BASE_URL || 'http://localhost:3000', // Required for callbacks and discovery endpoints\n  plugins: [\n    atprotoAuth({\n      privateKey: process.env.BSKY_PRIVATE_KEY ?? '', // Required for production, optional for localhost\n      clientMetadata: {\n        clientName: 'My Awesome App',\n        scope: 'atproto transition:generic'\n      },\n      mapProfileToUser: (profile) => ({\n        name: profile.displayName || `@${profile.handle}`,\n        image: profile.avatar,\n      }),\n    }),\n  ],\n});\n\n// 3. Run migrations after adding plugin: `npx auth migrate` or `npx auth generate`\n\n// 4. Client-side configuration\nexport const authClient = createAuthClient({\n  plugins: [atprotoAuthClient()],\n});\n\n// 5. Example client-side sign-in flow\nasync function initiateSignIn(handle: string, callbackURL: string) {\n  try {\n    await authClient.signIn.bsky({\n      handle: handle,\n      callbackURL: callbackURL,\n    });\n    console.log('Bluesky sign-in initiated successfully!');\n  } catch (error) {\n    console.error('Failed to initiate Bluesky sign-in:', error);\n  }\n}\n\n// Example usage (e.g., in a React component or server action)\n// initiateSignIn('example.bsky.social', '/dashboard');\n","lang":"typescript","description":"This quickstart demonstrates how to integrate the better-auth-atproto plugin on both the server and client sides, including basic configuration with a private key (for production) and initiating a Bluesky sign-in flow."},"warnings":[{"fix":"Use `npm install better-auth-atproto` and `import { atprotoAuthClient } from 'better-auth-atproto/client';` consistently.","message":"The provided README snippet incorrectly suggests 'better-auth-bsky' for installation and client imports. The correct package name for installation and client-side imports is 'better-auth-atproto'.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Generate a private key (e.g., `openssl ecparam -name prime256v1 -genkey -noout -out ec-private.pem`) and provide its contents via the `privateKey` option in `atprotoAuth` configuration for production environments.","message":"A PEM-encoded ES256 private key is mandatory for production deployments to secure OAuth interactions (DPoP). It is not needed for localhost development due to AT Protocol's loopback client support.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Execute `npx auth migrate` (or `npx auth generate` during development) after installing and configuring the plugin.","message":"Database migrations must be run after adding the plugin to your `better-auth` configuration. This extends the `user` table and creates new tables for OAuth state and sessions.","severity":"gotcha","affected_versions":">=0.1.0"},{"fix":"Ensure `baseURL` is explicitly set in your `betterAuth` configuration, e.g., `baseURL: 'https://myapp.com'`.","message":"The `baseURL` option is a required global configuration for the core `better-auth` instance. It is used by the plugin to construct callback URLs and auto-hosted discovery endpoint URLs.","severity":"gotcha","affected_versions":">=0.1.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Verify that `process.env.BSKY_PRIVATE_KEY` contains the full PEM-encoded ES256 private key string and is correctly passed to the `privateKey` option in `atprotoAuth`.","cause":"Missing, invalid, or improperly formatted private key provided in a production environment.","error":"TypeError: Cannot read properties of undefined (reading 'split') OR Invalid DPoP Private Key: The privateKey option must be a valid PEM-encoded ES256 private key."},{"fix":"Ensure `import { atprotoAuthClient } from 'better-auth-atproto/client';` is used and `atprotoAuthClient()` is passed within the `plugins` array to `createAuthClient`.","cause":"The client-side `atprotoAuthClient` plugin was not correctly configured or imported into `createAuthClient`.","error":"Error: Failed to initiate Bluesky sign-in: authClient.signIn.bsky is not a function"},{"fix":"Run `npx auth migrate` in your project's root directory to apply the necessary database schema changes for the plugin.","cause":"Database migrations were not executed after the `better-auth-atproto` plugin was added to the server configuration.","error":"Database error: column \"bskyDid\" of relation \"user\" does not exist (or similar schema mismatch error)"}],"ecosystem":"npm","meta_description":null}