OAuth 2.0
Use OAuth 2.0 with the Authorization Code + PKCE flow to build third-party integrations that act on behalf of Rekall users. OAuth tokens use the rat_ prefix and support granular scopes.
When to Use OAuth
OAuth 2.0 is designed for applications that need to access Rekall on behalf of other users. If you are building a server-side integration for your own account, use API keys instead.
Registering an OAuth App
Before using the OAuth flow, you need to register your application in the Rekall Developer Dashboard.
- Go to Settings → OAuth Apps
- Click Register New App
- Provide your app name, description, and website URL
- Add one or more redirect URIs (see Redirect URIs)
- Select the default scopes your app will request
- Click Create Application
After registration, you will receive a client_id. Since Rekall uses the PKCE flow, no client secret is issued -- this makes it safe for single-page applications and mobile apps.
{"client_id": "rkapp_a1b2c3d4e5f6g7h8","app_name": "My Agent Platform","redirect_uris": ["https://myapp.com/callback","http://localhost:3000/callback"],"default_scopes": ["memories:read", "memories:write"]}
Authorization Code + PKCE Flow
The PKCE (Proof Key for Code Exchange) flow is a secure authorization flow that does not require a client secret. It works in four steps:
code_verifier and code_challengeStep 1: Generate PKCE Parameters
Generate a cryptographically random code_verifier and derive the code_challenge from it using SHA-256:
import crypto from 'crypto';// Generate a random code verifier (43-128 characters)const codeVerifier = crypto.randomBytes(32).toString('base64url');// Derive the code challenge using SHA-256const codeChallenge = crypto.createHash('sha256').update(codeVerifier).digest('base64url');// Store the code_verifier -- you'll need it in Step 4// e.g., save to session storage or a server-side session
Step 2: Redirect to Authorization
Redirect the user to the Rekall authorization endpoint with your app details:
const authUrl = new URL('https://api.rekall.ai/v1/oauth/authorize');authUrl.searchParams.set('client_id', 'rkapp_your_client_id');authUrl.searchParams.set('redirect_uri', 'https://myapp.com/callback');authUrl.searchParams.set('response_type', 'code');authUrl.searchParams.set('scope', 'memories:read memories:write entities:read');authUrl.searchParams.set('code_challenge', codeChallenge);authUrl.searchParams.set('code_challenge_method', 'S256');authUrl.searchParams.set('state', generateRandomState()); // CSRF protection// Redirect the userwindow.location.href = authUrl.toString();
| Parameter | Required | Description |
|---|---|---|
client_id | Yes | Your OAuth app client ID |
redirect_uri | Yes | Must match a registered redirect URI |
response_type | Yes | Must be "code" |
scope | Yes | Space-separated list of scopes |
code_challenge | Yes | Base64url-encoded SHA-256 hash of code_verifier |
code_challenge_method | Yes | Must be "S256" |
state | Recommended | Random string for CSRF protection |
Step 3: Handle the Callback
After the user approves the request, Rekall redirects back to your redirect_uri with an authorization code and the state parameter:
https://myapp.com/callback?code=rkcode_abc123def456&state=your_random_state
// In your callback route handlerexport async function GET(request: Request) {const url = new URL(request.url);const code = url.searchParams.get('code');const state = url.searchParams.get('state');// Verify the state parameter matches what you sentif (state !== getStoredState()) {return new Response('Invalid state parameter', { status: 400 });}// Proceed to token exchange (Step 4)const tokens = await exchangeCodeForTokens(code);// ...}
Step 4: Exchange Code for Tokens
Exchange the authorization code and your original code_verifier for access and refresh tokens:
const response = await fetch('https://api.rekall.ai/v1/oauth/token', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({grant_type: 'authorization_code',client_id: 'rkapp_your_client_id',code: authorizationCode,redirect_uri: 'https://myapp.com/callback',code_verifier: storedCodeVerifier,}),});const tokens = await response.json();// {// access_token: "rat_abc123...",// refresh_token: "rrt_def456...",// token_type: "Bearer",// expires_in: 3600,// scope: "memories:read memories:write entities:read"// }
Access Token Format
OAuth access tokens use the rat_ prefix (Rekall Access Token) and are used identically to API keys:
| Token Type | Prefix | Lifetime |
|---|---|---|
| Access Token | rat_ | 1 hour |
| Refresh Token | rrt_ | 30 days |
curl https://api.rekall.ai/v1/memories \-H "Authorization: Bearer rat_abc123def456ghi789"
Refreshing Tokens
Access tokens expire after 1 hour. Use the refresh token to obtain a new access token without requiring the user to re-authorize:
const response = await fetch('https://api.rekall.ai/v1/oauth/token', {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({grant_type: 'refresh_token',client_id: 'rkapp_your_client_id',refresh_token: storedRefreshToken, // rrt_...}),});const newTokens = await response.json();// New access_token (rat_...) and optionally a rotated refresh_token
Refresh Token Rotation
Rekall uses refresh token rotation for security. Each time you use a refresh token, a new refresh token is issued and the old one is invalidated. Always store the latest refresh token.
Scopes in OAuth
When redirecting users to the authorization page, specify the scopes your app needs as a space-separated list. Users will see these scopes and must approve them.
// Request only the scopes your app needsconst scopes = ['memories:read','memories:write','entities:read',].join(' ');authUrl.searchParams.set('scope', scopes);
The access token will only have the scopes that the user approved. If a user denies a scope, the token will be issued without it. Always check the scope field in the token response to see which scopes were granted. See the full list of available scopes.
Redirect URIs
Redirect URIs must be registered in your OAuth app settings before they can be used. Rekall enforces exact-match validation to prevent open redirect attacks.
Production
https://myapp.com/callbackLocal Development
http://localhost:3000/callbackCustom Scheme (Mobile)
myapp://callbackExact Match
Redirect URIs are matched exactly, including the path, query string, and fragment. https://myapp.com/callback and https://myapp.com/callback/ (with trailing slash) are treated as different URIs.
Error Handling
OAuth errors are returned as query parameters on the redirect URI or as JSON responses from the token endpoint:
| Error Code | Description |
|---|---|
invalid_client | Unknown or invalid client_id |
invalid_redirect_uri | Redirect URI not registered |
invalid_scope | Requested scope is not allowed |
invalid_grant | Authorization code is expired or used |
invalid_pkce | code_verifier does not match challenge |
access_denied | User denied the authorization request |
{"error": "invalid_grant","error_description": "The authorization code has expired. Codes are valid for 10 minutes.","error_uri": "https://docs.rekall.ai/authentication/oauth#error-handling"}
