bluesky-oauth-kit v0.2.15
Bluesky OAuth (2.0) Kit
A drop-in, ready-to-use, Bluesky OAuth login solution for Node.js and Javascript (any client).
Accelerate the migration of third-party Bluesky apps and integrations from insecure authentication methods (password stored in plaintext), to modern OAuth.
This package aims to adhere to OAuth 2.0 norms, while providing a simple to understand and ready-to-use Node.js backend that can be used with any sort of frontend client (Javascript, Vue, React, React Native, Ionic/Capacitor, Android, Swift, Electron, PWA, etc.)
Examples provided for Express (with hopefully more to come.) Testing an PRs welcome!
Installation
npm i -s bluesky-oauth-kit
Usage
See examples
directory.
Express (Default Options)
const express = require('express');
const { setupExpressAuth } = require('bluesky-oauth-kit');
const app = express();
// Basic setup
await setupExpressAuth(app);
// With additional options
await setupExpressAuth(app, {
baseUrl: 'http://localhost:5001',
redirectUrl: '/dashboard',
clientName: 'My OAuth App',
// Optional: Custom storage implementations
stateStore: customStateStore,
sessionStore: customSessionStore
});
Express (composable)
const express = require('express');
const { initializeOAuth, authenticateToken, setupOauthRoutes } = require('bluesky-oauth-kit');
const app = express();
(async function() {
const { client, sessionStore, stateStore } = await initializeOAuth(options);
setupOauthRoutes(app, sessionStore);
})();
Framework Support
The library works with multiple frameworks:
- Express (primary support)
Other frameworks can be supported by PR.
Environment Variables
# Required
OAUTH_JWT_SECRET=your-jwt-secret
OAUTH_BASE_URL=http://localhost:5001
OAUTH_PRIVATE_KEY_1=your-private-key-1
OAUTH_PRIVATE_KEY_2=your-private-key-2
OAUTH_PRIVATE_KEY_3=your-private-key-3
# Optional
OAUTH_CLIENT_NAME=My OAuth App
OAUTH_USE_COOKIES=true
OAUTH_REDIRECT_URL=/dashboard
NODE_ENV=development
Custom Storage
The library uses in-memory storage by default, but you can implement your own storage:
class CustomStore {
async get(key) { /* ... */ }
async set(key, value) { /* ... */ }
async del(key) { /* ... */ }
}
// Redis example
class RedisStore {
constructor(redis) {
this.redis = redis;
}
async get(key) { return JSON.parse(await this.redis.get(key)); }
async set(key, value) { await this.redis.set(key, JSON.stringify(value)); }
async del(key) { await this.redis.del(key); }
}
Available Endpoints
The library sets up the following endpoints:
/login
- Serves a login form (optional, can be disabled)/oauth/login
- Initiates the OAuth flow/oauth/callback
- Handles the OAuth callback/oauth/userinfo
- Returns info about the authenticated user/oauth/revoke
- Revokes the current session/oauth/client-metadata.json
- Serves OAuth client metadata/oauth/jwks.json
- Serves the JSON Web Key Set
Configuration
await setupExpressAuth(app, {
baseUrl: 'http://localhost:5001',
serveLoginPage: true, // Set to false to disable built-in login page
serveErrorPage: true, // Set to false to disable built-in error page
loginPageTitle: 'Login with Bluesky', // Customize login page
display: 'page', // 'page', 'popup', or 'touch' for mobile
maxAge: 48 * 60 * 60 * 1000, // Cookie lifetime
cookieDomain: '.yourdomain.com',
cookiePath: '/',
cookieSecret: 'your-secret',
addHeaders: true,
forceHTTPS: true,
// ... other options
});
Development
Running the Example
- Clone the repository
- Install dependencies:
npm install
- Copy .env.example to .env and fill in your values:
cp .env.example .env
- Run the example:
node examples/express.js
The example server will start on http://localhost:5001
Generating Keys
You can generate OAuth keys in two ways:
- Using npx (recommended):
npx bluesky-oauth-kit generate-oauth-keys
- After installing as a dependency:
npm run generate-keys
This will either create a new .env file or append OAuth keys to your existing one.
Authentication Flow
The library implements standard OAuth 2.0 authentication, providing:
A JWT containing:
sub
: The user's DID (standard OpenID Connect subject identifier)did
: The user's DID (AT Protocol identifier)iss
: The issuer ('bsky.social')iat
: Token issue timestamp
The
/oauth/userinfo
endpoint returns this basic profile information.
Note: For richer profile data (handle, displayName, avatar, etc.), you'll need to:
1. Install @atproto/api
2. Use the session to create an Agent
3. Call agent.getProfile()
Example:
const { Agent } = require('@atproto/api');
// Get rich profile data
const agent = new Agent(session);
const profile = await agent.getProfile({ actor: agent.did });
console.log(profile.data); // Contains handle, displayName, avatar, etc.
Security Configuration
The library includes several security features that can be configured:
Cookie Options
When using OAUTH_USE_COOKIES=true
, you can configure cookie security:
await setupExpressAuth(app, {
maxAge: 7 * 24 * 60 * 60 * 1000, // Cookie lifetime (default 48h)
cookieDomain: '.yourdomain.com', // Cookie domain
cookiePath: '/', // Cookie path
cookieSecret: 'your-secret', // Enable signed cookies
});
Security Headers
The library automatically sets security headers:
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
CORS
Configure CORS in your Express app:
app.use(cors({
origin: process.env.OAUTH_BASE_URL,
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization'],
}));
HTTPS
In production (NODE_ENV=production
):
- Cookies are automatically set as
Secure
- HTTP requests are redirected to HTTPS
- Cookies use
SameSite=Strict
Rate Limiting
The example includes rate limiting. You should include this (or something similar) in your app.
const rateLimit = require('express-rate-limit');
app.use(['/login', '/oauth/*'], rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // Limit each IP to 100 requests per window
}));
Available Scopes
The Bluesky OAuth server supports these scopes:
atproto
- Standard AT Protocol accesstransition:generic
- Generic transition scopetransition:chat.bsky
- Chat transition scope (future use)
OAuth Implementation Details
This library implements the AT Protocol OAuth specification via @atproto/oauth-client-node
:
- Secure OAuth 2.0 flow with PAR, DPoP, and PKCE
- Session management
- Token handling and refresh
- Framework integrations (Express)
- Configurable storage backends
Using the Bluesky API (ATProto) with this package
This kit provides a helper function, getClient()
, to retrieve your authenticated client instance,
which can be passed to @atproto/api
to make authenticated API requests.
See examples/express.js
.