bluesky-oauth-kit
Version:
A complete OAuth backend implementation for Bluesky
269 lines (204 loc) • 7.23 kB
Markdown
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
```bash
npm i -s bluesky-oauth-kit
```
# Usage
See `examples` directory.
## Express (Default Options)
```js
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
});
```
```js
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);
})();
```
The library works with multiple frameworks:
- Express (primary support)
Other frameworks can be supported by PR.
```env
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
OAUTH_CLIENT_NAME=My OAuth App
OAUTH_USE_COOKIES=true
OAUTH_REDIRECT_URL=/dashboard
NODE_ENV=development
```
The library uses in-memory storage by default, but you can implement your own storage:
```js
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); }
}
```
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
```js
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
});
```
1. Clone the repository
2. Install dependencies:
```bash
npm install
```
3. Copy .env.example to .env and fill in your values:
```bash
cp .env.example .env
```
4. Run the example:
```bash
node examples/express.js
```
The example server will start on http://localhost:5001
You can generate OAuth keys in two ways:
1. Using npx (recommended):
```bash
npx bluesky-oauth-kit generate-oauth-keys
```
2. After installing as a dependency:
```bash
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:
1. 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
2. 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:
```js
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.
```
The library includes several security features that can be configured:
When using `OAUTH_USE_COOKIES=true`, you can configure cookie security:
```js
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
});
```
The library automatically sets security headers:
- `X-Content-Type-Options: nosniff`
- `X-Frame-Options: DENY`
- `X-XSS-Protection: 1; mode=block`
Configure CORS in your Express app:
```js
app.use(cors({
origin: process.env.OAUTH_BASE_URL,
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type', 'Authorization'],
}));
```
In production (`NODE_ENV=production`):
- Cookies are automatically set as `Secure`
- HTTP requests are redirected to HTTPS
- Cookies use `SameSite=Strict`
The example includes rate limiting. You should include this (or something similar) in your app.
```js
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
}));
```
The Bluesky OAuth server supports these scopes:
- `atproto` - Standard AT Protocol access
- `transition:generic` - Generic transition scope
- `transition: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`.