Auth Block

Enterprise-grade authentication for modern applications
The Auth Block is a complete authentication and authorization service that provides secure user management, JWT token handling, multi-factor authentication, passwordless login, AI agent identity, service tokens, OIDC provider capabilities, and multi-tenant support. It uses RS256 JWT tokens with company-specific RSA key pairs and follows the JSON:API specification.
Key Features
- RS256 JWT Authentication - Asymmetric encryption with company-level RSA keys
- Multi-Factor Authentication - Two channels: email OTP (zero-friction) and TOTP (authenticator apps) with backup codes
- Passwordless Login via Email OTP - 6-digit OTP with anti-enumeration and MFA gate
- OIDC Provider - Discovery, JWKS, and token endpoint with 4 grant types including agent-identity
- AI Agent Identity - Agent registration, Ed25519 signature verification, and OAuth token exchange
- Service Tokens (M2M) - Agent tokens (24h) and service tokens (90 days) with scope validation
- reCAPTCHA Bot Protection - Per-tenant inline enforcement with kill switch
- Magic Link Authentication - Create, validate, exchange, and send passwordless login links
- API Key Management - Service-to-service authentication with AWS Gateway integration
- Multi-tenant Architecture - Complete data isolation per tenant using PostgreSQL schemas
- OAuth 2.0 Refresh Tokens - Optional long-lived session support
- Role-Based Access Control - Fine-grained permissions system
- Block Permissions Provisioning - HMAC-authenticated internal scope registry
API Endpoint
| Service | URL |
|---|---|
| Auth | https://auth.api.us.23blocks.com |
Environment Routing: Use the same URL for all environments. Your API key determines the environment:
pk_test_*/sk_test_*→ Routes to Stagingpk_live_*/sk_live_*→ Routes to Production
Quick Start
Installation
npm install @23blocks/sdk
Basic Authentication
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { authentication: 'https://auth.api.us.23blocks.com' },
apiKey: 'your-api-key', // Use pk_test_* for staging, pk_live_* for production
});
// Register a new user
const user = await client.auth.register({
email: 'user@example.com',
password: 'SecurePassword123',
name: 'John Doe',
confirmSuccessUrl: 'https://yourapp.com/confirmed'
});
// Login
const session = await client.auth.login({
email: 'user@example.com',
password: 'SecurePassword123'
});
// Access token is available
console.log(session.accessToken);
Authentication
Required Headers
All authenticated requests require:
Authorization: Bearer [JWT_TOKEN]
X-API-Key: [COMPANY_API_KEY]
Content-Type: application/json
JWT Token Structure
Tokens are signed using RS256 with company-specific RSA keys. You can verify tokens using the public JWKS endpoint:
GET /:company_url_id/.well-known/jwks.json
API Reference
User Registration
curl -X POST https://auth.api.us.23blocks.com/auth \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"user": {
"email": "newuser@example.com",
"password": "SecurePassword123",
"password_confirmation": "SecurePassword123",
"name": "Jane Smith",
"confirm_success_url": "https://yourapp.com/email-confirmed"
}
}'
Response:
{
"data": {
"id": "usr_def456",
"type": "user",
"attributes": {
"unique_id": "usr_def456",
"email": "newuser@example.com",
"name": "Jane Smith",
"confirmed": false,
"confirmation_sent_at": "2024-01-15T10:30:00Z"
}
},
"meta": {
"message": "A confirmation email has been sent to newuser@example.com"
}
}
Sign In
curl -X POST https://auth.api.us.23blocks.com/auth/sign_in \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"email": "user@example.com",
"password": "SecurePassword123"
}'
Response:
{
"data": {
"id": "usr_abc123",
"type": "user",
"attributes": {
"unique_id": "usr_abc123",
"email": "user@example.com",
"name": "John Doe",
"confirmed": true,
"role_id": 5,
"mfa_enabled": false,
"mfa_channel": null
}
},
"meta": {
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 86400
}
}
If the user has MFA enabled, the sign-in response will include an mfa_required flag. Send the MFA code in a follow-up request.
Token Validation
curl -X GET https://auth.api.us.23blocks.com/auth/validate_token \
-H "Authorization: Bearer your-jwt-token" \
-H "X-API-Key: your-api-key"
Password Reset
Request reset:
curl -X POST https://auth.api.us.23blocks.com/auth/password \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"email": "user@example.com",
"redirect_url": "https://app.example.com/reset-password"
}'
Update password:
curl -X PUT https://auth.api.us.23blocks.com/auth/password \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"password": "NewSecurePassword123",
"password_confirmation": "NewSecurePassword123",
"reset_password_token": "abc123resettoken"
}'
Passwordless Login via Email OTP
Send a 6-digit one-time password to the user's email for passwordless authentication. Anti-enumeration protection ensures the endpoint always returns 200 regardless of whether the email exists.
Request OTP
curl -X POST https://auth.api.us.23blocks.com/auth/passwordless/request \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"email": "user@example.com"
}'
Response (always 200 for anti-enumeration):
{
"meta": {
"message": "If the email exists, a verification code has been sent."
}
}
Verify OTP
curl -X POST https://auth.api.us.23blocks.com/auth/passwordless/verify \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"email": "user@example.com",
"otp_code": "123456"
}'
Response:
{
"data": {
"id": "usr_abc123",
"type": "user",
"attributes": {
"unique_id": "usr_abc123",
"email": "user@example.com",
"name": "John Doe"
}
},
"meta": {
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 86400
}
}
OTP Rules:
- 6-digit numeric code
- 10-minute TTL
- Maximum 5 verification attempts
- MFA gate applies on verify (same as sign_in)
Multi-Factor Authentication
Auth Block supports two MFA channels:
- Email - Zero-friction OTP sent to the user's email
- TOTP - Time-based one-time passwords via authenticator apps (Google Authenticator, Authy, etc.)
MFA Endpoints
All MFA endpoints are under /users/:unique_id/mfa/:
| Endpoint | Method | Description |
|---|---|---|
/mfa/setup | POST | Initialize MFA setup (returns QR code for TOTP, or sends email OTP) |
/mfa/enable | POST | Enable MFA after verifying code |
/mfa/disable | POST | Disable MFA (requires verification) |
/mfa/verify | POST | Verify an MFA code |
/mfa/status | GET | Check MFA status and channel |
Setup MFA (TOTP)
curl -X POST https://auth.api.us.23blocks.com/users/usr_abc123/mfa/setup \
-H "Authorization: Bearer your-jwt-token" \
-H "X-API-Key: your-api-key" \
-d '{"channel": "totp"}'
Response:
{
"data": {
"id": "mfa_setup_123",
"type": "mfa_setup",
"attributes": {
"channel": "totp",
"secret": "JBSWY3DPEHPK3PXP",
"qr_code_url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
"backup_codes": ["12345678", "87654321", "11111111", "22222222", "33333333", "44444444", "55555555", "66666666", "77777777", "88888888"]
}
}
}
Setup MFA (Email)
curl -X POST https://auth.api.us.23blocks.com/users/usr_abc123/mfa/setup \
-H "Authorization: Bearer your-jwt-token" \
-H "X-API-Key: your-api-key" \
-d '{"channel": "email"}'
Enable MFA
After verifying the setup code:
curl -X POST https://auth.api.us.23blocks.com/users/usr_abc123/mfa/enable \
-H "Authorization: Bearer your-jwt-token" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"mfa_code": "123456"}'
Login with MFA
When MFA is enabled, sign-in and passwordless verify flows require an MFA code:
curl -X POST https://auth.api.us.23blocks.com/auth/sign_in \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"email": "user@example.com",
"password": "SecurePassword123",
"mfa_code": "123456"
}'
Backup Codes: 10 one-time backup codes are generated during setup. Each can be used once in place of an MFA code. Usage is tracked.
reCAPTCHA Bot Protection
Inline reCAPTCHA enforcement protects authentication endpoints from automated attacks. Configurable per-tenant with an instant kill switch.
How It Works
- Enable reCAPTCHA enforcement in your tenant configuration (
auth_config['recaptcha_enforcement']) - Frontend obtains a reCAPTCHA token from Google
- Send the token as a top-level
recaptcha_tokenparameter with protected requests
Protected Endpoints
- User registration (
POST /auth) - Sign in (
POST /auth/sign_in) - Passwordless OTP request (
POST /auth/passwordless/request) - Password reset (
POST /auth/password) - Password OTP (
POST /auth/password/otp)
Example with reCAPTCHA
curl -X POST https://auth.api.us.23blocks.com/auth \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"recaptcha_token": "03AGdBq24PBCbwiDRaS_MJ7Z...",
"user": {
"email": "newuser@example.com",
"password": "SecurePassword123",
"password_confirmation": "SecurePassword123",
"name": "Jane Smith"
}
}'
reCAPTCHA Error Codes
| Code | Status | Description |
|---|---|---|
| GW-RC-001 | 422 | reCAPTCHA token required (enforcement is enabled but no token sent) |
| GW-RC-003 | 403 | reCAPTCHA verification failed (invalid or expired token) |
| GW-RC-004 | 403 | reCAPTCHA score too low (suspected bot) |
Rate Limiting
In addition to reCAPTCHA, Rack::Attack rate limiting enforces 5 registrations per IP per 5 minutes.
Kill Switch
Tenants can disable reCAPTCHA enforcement instantly by toggling the feature flag. No frontend changes required — the API will simply stop requiring the recaptcha_token parameter.
User Invitations
Invite users to join your application:
curl -X POST https://auth.api.us.23blocks.com/auth/invitation \
-H "Authorization: Bearer your-jwt-token" \
-H "Content-Type: application/json" \
-d '{
"user": {
"email": "newuser@example.com",
"name": "New User",
"role_id": 5,
"appid": "your-api-key",
"accept_invitation_url": "https://app.example.com/accept-invite"
}
}'
Magic Link Authentication
Magic links provide passwordless authentication via secure, time-limited tokens with delivery lifecycle tracking and audit trails.
Endpoints
| Endpoint | Method | Description |
|---|---|---|
/companies/:id/magic_links | POST | Create a new magic link |
/companies/:id/magic_links/:id/validate | GET | Validate a magic link token |
/companies/:id/magic_links/:id/exchange | POST | Exchange magic link for JWT |
/companies/:id/magic_links/:id/send_email | POST | Send magic link via email |
/companies/:id/magic_links/:id/verify_code | POST | Verify exchange code |
/companies/:id/magic_links/:id | DELETE | Destroy a magic link |
Create Magic Link
curl -X POST https://auth.api.us.23blocks.com/companies/your-company/magic_links \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"email": "user@example.com",
"redirect_url": "https://app.example.com/dashboard",
"expires_in": 900
}'
Exchange Code Pattern
Magic links use an exchange code pattern with delivery lifecycle tracking:
- Create the magic link
- Send via email (tracks delivery status)
- Exchange the link token for a short-lived exchange code
- Verify the exchange code to receive a JWT
AI Agent Identity & Authentication
Register and authenticate AI agents with Ed25519 cryptographic signatures. Agents receive their own OAuth tokens via a dedicated grant type.
Agent Registration
curl -X POST https://auth.api.us.23blocks.com/companies/your-company/agents \
-H "Authorization: Bearer your-jwt-token" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"agent": {
"name": "backend-processor",
"description": "Processes background tasks",
"public_key": "MCowBQYDK2VwAyEA...",
"scopes": ["files:read", "files:write", "crm:read"]
}
}'
Agent Lifecycle
| Endpoint | Method | Description |
|---|---|---|
/companies/:id/agents | POST | Register a new agent |
/companies/:id/agents | GET | List all agents |
/companies/:id/agents/:id | GET | Get agent details |
/companies/:id/agents/:id | PUT | Update agent |
/companies/:id/agents/:id | DELETE | Delete agent |
/companies/:id/agents/:id/suspend | POST | Suspend agent |
/companies/:id/agents/:id/reactivate | POST | Reactivate agent |
Agent OAuth Token Exchange
Agents authenticate by signing a challenge with their Ed25519 private key:
curl -X POST https://auth.api.us.23blocks.com/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "urn:aid:agent-identity",
"agent_id": "agent_abc123",
"signature": "base64-encoded-ed25519-signature",
"timestamp": "2026-05-19T12:00:00Z"
}'
Response:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 86400,
"scope": "files:read files:write crm:read"
}
Token Introspection (RFC 7662)
Introspect both user and agent tokens:
curl -X POST https://auth.api.us.23blocks.com/oauth/introspect \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"token": "eyJhbGciOiJSUzI1NiIs..."
}'
Response:
{
"active": true,
"sub": "agent_abc123",
"client_id": "your-company",
"scope": "files:read files:write crm:read",
"token_type": "bearer",
"exp": 1716220800,
"iat": 1716134400,
"iss": "https://auth.api.us.23blocks.com/your-company"
}
Service Tokens (M2M)
Service tokens provide long-lived authentication for machine-to-machine communication. Two categories are available:
| Category | Lifetime | Use Case |
|---|---|---|
| Agent | 24 hours | Short-lived agent operations |
| Service | 90 days | Long-running service integrations |
Endpoints
| Endpoint | Method | Description |
|---|---|---|
/companies/:id/service_tokens | POST | Create a service token |
/companies/:id/service_tokens | GET | List service tokens |
/companies/:id/service_tokens/:id | GET | Get token details |
/companies/:id/service_tokens/:id | DELETE | Revoke a token |
/companies/:id/service_tokens/:id/regenerate | POST | Regenerate a token |
Create Service Token
curl -X POST https://auth.api.us.23blocks.com/companies/your-company/service_tokens \
-H "Authorization: Bearer your-jwt-token" \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"service_token": {
"name": "payment-processor",
"category": "service",
"scopes": ["wallet:read", "wallet:write"]
}
}'
Limits: Maximum 100 active service tokens per company. Usage is tracked per token.
OIDC Provider
The Auth Block acts as a full OpenID Connect provider with discovery, JWKS, and a multi-grant token endpoint. Supports both company-level and app-level OIDC.
Discovery Document
# Company-level
curl https://auth.api.us.23blocks.com/your-company/.well-known/openid-configuration
# App-level
curl https://auth.api.us.23blocks.com/your-company/apps/your-app/.well-known/openid-configuration
JWKS Endpoint
curl https://auth.api.us.23blocks.com/your-company/.well-known/jwks.json
Token Endpoint
The token endpoint supports four grant types:
| Grant Type | Use Case |
|---|---|
authorization_code | Standard OAuth flow for user login |
refresh_token | Refresh an expired access token |
client_credentials | M2M service authentication |
urn:aid:agent-identity | AI agent authentication with Ed25519 |
# Client credentials example
curl -X POST https://auth.api.us.23blocks.com/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "your-client-id",
"client_secret": "your-client-secret",
"scope": "read write"
}'
Block Permissions Provisioning
Internal API for provisioning cross-block permissions using HMAC authentication:
curl -X POST https://auth.api.us.23blocks.com/block_permissions/provision \
-H "Content-Type: application/json" \
-H "X-HMAC-Signature: sha256=..." \
-d '{
"block": "files",
"scopes": ["files:read", "files:write", "files:delete"]
}'
The internal scope registry covers all blocks: Files, Sales, Search, Rewards, Content, CRM, Forms, Wallet, and more.
Data Types
User
| Field | Type | Description |
|---|---|---|
unique_id | string | Unique identifier |
email | string | User's email address |
name | string | Full name |
confirmed | boolean | Email confirmed status |
role_id | integer | Assigned role ID |
mfa_enabled | boolean | MFA status |
mfa_channel | string | MFA channel: "email", "totp", or null |
Company
| Field | Type | Description |
|---|---|---|
unique_id | string | Unique identifier |
url_id | string | URL-safe identifier |
name | string | Company name |
api_access_key | string | API key |
oidc_issuer | string | OpenID Connect issuer URL |
Error Handling
The API uses JSON:API error format:
{
"errors": [
{
"status": "422",
"source": "Authentication Service",
"code": "10001",
"title": "Validation Error",
"detail": "Email is required and must be valid"
}
]
}
Common Error Codes
| Status | Code | Description |
|---|---|---|
| 400 | 10001 | Invalid request format |
| 401 | 10002 | Invalid authentication |
| 401 | 103 | Missing or invalid API key |
| 403 | 10003 | Insufficient permissions |
| 403 | GW-RC-003 | reCAPTCHA verification failed |
| 403 | GW-RC-004 | reCAPTCHA score too low |
| 404 | 10004 | Resource not found |
| 422 | 10005 | Validation failed |
| 422 | GW-RC-001 | reCAPTCHA token required |
| 429 | 10006 | Rate limited |
Rate Limiting
| Endpoint Type | Limit |
|---|---|
| Authentication | 10 requests/minute/IP |
| Registration | 5 requests/5 minutes/IP (with reCAPTCHA) |
| Token validation | 60 requests/minute/token |
| General endpoints | 1000 requests/hour/API key |
Rate limit headers in responses:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1642248000
Security Features
- RS256 JWT - Asymmetric encryption with per-company RSA keys
- JWKS Endpoints - Public key verification for token validation
- Ed25519 Agent Signatures - Cryptographic agent identity verification
- Key Rotation - Automated key rotation with graceful transitions
- 24-hour Token Lifetime - Short-lived access tokens
- Tenant Isolation - PostgreSQL schema separation per tenant
- reCAPTCHA Enforcement - Bot protection with per-tenant kill switch
- Audit Logging - Complete audit trail for security events
- Anti-enumeration - Passwordless endpoints always return 200
SDK Examples
TypeScript/JavaScript
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { authentication: 'https://auth.api.us.23blocks.com' },
apiKey: process.env.BLOCKS_API_KEY,
});
// Check if token is valid
const isValid = await client.auth.validateToken();
// Get current user
const user = await client.auth.getCurrentUser();
// Update user profile
await client.auth.updateProfile({
name: 'Updated Name',
timezone: 'America/New_York'
});
// Enable MFA
const mfaSetup = await client.auth.setupMfa();
// Show QR code to user
console.log(mfaSetup.qrCodeUrl);
// After user scans QR and enters code
await client.auth.enableMfa('123456');
React Hook Example
import { useAuth } from '@23blocks/react';
function LoginForm() {
const { login, isLoading, error } = useAuth();
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
await login({
email: formData.get('email'),
password: formData.get('password')
});
};
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" required />
<input name="password" type="password" required />
<button type="submit" disabled={isLoading}>
{isLoading ? 'Signing in...' : 'Sign In'}
</button>
{error && <p>{error.message}</p>}
</form>
);
}
Related Resources
- API Reference - Interactive API documentation
- Frontend SDK - Client-side integration
- Platform Status - Service uptime monitoring