Auto-indexing service and GraphQL API for AT Protocol Records quickslice.slices.network/
atproto gleam graphql
at main 181 lines 4.6 kB view raw view rendered
1# Authentication 2 3Quickslice proxies OAuth between your app and users' Personal Data Servers (PDS). Your app never handles AT Protocol credentials directly. 4 5## How It Works 6 71. User clicks login in your app 82. Your app redirects to Quickslice's `/oauth/authorize` endpoint 93. Quickslice redirects to the user's PDS for authorization 104. User enters credentials and approves your app 115. PDS redirects back to Quickslice with an auth code 126. Quickslice exchanges the code for tokens 137. Quickslice redirects back to your app with a code 148. Your app exchanges the code for an access token 15 16The access token authorizes mutations that write to the user's repository. 17 18## Setting Up OAuth 19 20### Generate a Signing Key 21 22Quickslice needs a private key to sign OAuth tokens. Generate one with `goat`: 23 24```bash 25brew install goat 26goat key generate -t p256 27``` 28 29Set the output as your `OAUTH_SIGNING_KEY` environment variable. 30 31### Register an OAuth Client 32 331. Open your Quickslice instance and navigate to **Settings** 342. Scroll to **OAuth Clients** and click **Register New Client** 353. Fill in the form: 36 - **Client Name**: Your app's name 37 - **Client Type**: Public (browser apps) or Confidential (server apps) 38 - **Redirect URIs**: Where users return after auth (e.g., `http://localhost:3000`) 39 - **Scope**: Leave as `atproto transition:generic` 404. Copy the **Client ID** 41 42### Public vs Confidential Clients 43 44| Type | Use Case | Secret | 45|------|----------|--------| 46| **Public** | Browser apps, mobile apps | No secret (client cannot secure it) | 47| **Confidential** | Server-side apps, backend services | Secret (stored securely on server) | 48 49## Using the Client SDK 50 51The Quickslice client SDK handles OAuth, PKCE, DPoP, token refresh, and GraphQL requests. 52 53### Install 54 55```bash 56npm install quickslice-client-js 57``` 58 59Or via CDN: 60 61```html 62<script src="https://unpkg.com/quickslice-client-js/dist/quickslice-client.min.js"></script> 63``` 64 65### Initialize 66 67```javascript 68import { createQuicksliceClient } from 'quickslice-client'; 69 70const client = await createQuicksliceClient({ 71 server: "https://yourapp.slices.network", 72 clientId: "YOUR_CLIENT_ID", 73}); 74``` 75 76### Login 77 78```javascript 79await client.loginWithRedirect({ 80 handle: "alice.bsky.social", 81}); 82``` 83 84### Handle the Callback 85 86After authentication, the user returns to your redirect URI: 87 88```javascript 89if (window.location.search.includes("code=")) { 90 await client.handleRedirectCallback(); 91} 92``` 93 94### Check Authentication State 95 96```javascript 97const isLoggedIn = await client.isAuthenticated(); 98 99if (isLoggedIn) { 100 const user = client.getUser(); 101 console.log(user.did); // "did:plc:abc123..." 102} 103``` 104 105### Logout 106 107```javascript 108await client.logout(); 109``` 110 111## Making Authenticated Requests 112 113### With the SDK 114 115The SDK adds authentication headers automatically: 116 117```javascript 118// Public query (no auth needed) 119const data = await client.publicQuery(` 120 query { xyzStatusphereStatus { edges { node { status } } } } 121`); 122 123// Authenticated query 124const viewer = await client.query(` 125 query { viewer { did handle } } 126`); 127 128// Mutation (requires auth) 129const result = await client.mutate(` 130 mutation { createXyzStatusphereStatus(input: { status: "🎉", createdAt: "${new Date().toISOString()}" }) { uri } } 131`); 132``` 133 134### Without the SDK 135 136Without the SDK, include headers based on your OAuth flow: 137 138**DPoP flow** (public clients): 139``` 140Authorization: DPoP <access_token> 141DPoP: <dpop_proof> 142``` 143 144**Bearer token flow** (confidential clients): 145``` 146Authorization: Bearer <access_token> 147``` 148 149## The Viewer Query 150 151The `viewer` query returns the authenticated user: 152 153```graphql 154query { 155 viewer { 156 did 157 handle 158 appBskyActorProfileByDid { 159 displayName 160 avatar { url } 161 } 162 } 163} 164``` 165 166Returns `null` when not authenticated (no error thrown). 167 168## Security: PKCE and DPoP 169 170The SDK implements two security mechanisms for browser apps: 171 172**PKCE (Proof Key for Code Exchange)** prevents authorization code interception. Before redirecting, the SDK generates a random secret and sends only its hash to the server. When exchanging the code for tokens, the SDK proves it initiated the request. 173 174**DPoP (Demonstrating Proof-of-Possession)** binds tokens to a cryptographic key in your browser. Each request includes a signed proof. An attacker who steals your access token cannot use it without the key. 175 176## OAuth Endpoints 177 178- `GET /oauth/authorize` - Start the OAuth flow 179- `POST /oauth/token` - Exchange authorization code for tokens 180- `GET /.well-known/oauth-authorization-server` - Server metadata 181- `GET /oauth/oauth-client-metadata.json` - Client metadata