AlphaBlock Auth — Developer Integration Guide
Last updated: April 2026
Overview
AlphaBlock Auth provides authentication and user identity for all AlphaBlock projects via a centralised hub at auth.alphablock.io. Your project does not handle user accounts, passwords, or sessions directly. Instead it redirects users to the hub, receives a signed JWT back, and uses that token to identify the user and determine their access level.
Prerequisites
- Your project is registered in the AlphaBlock Auth admin panel
- You have been issued a
PROJECT_ID(public) and aPROJECT_SECRET(private) - Your callback URL has been added to the project's permitted
callbackUrlslist - The JWKS endpoint is accessible at
auth.alphablock.io/.well-known/jwks.json
Environment Variables
# .env.local
ALPHABLOCK_PROJECT_ID=your_project_id
ALPHABLOCK_PROJECT_SECRET=your_project_secret
ALPHABLOCK_AUTH_URL=https://auth.alphablock.io
ALPHABLOCK_CALLBACK_URL=https://your-project.alphablock.io/auth/callbackStep 1 — Redirect to the Auth Hub
When a user needs to authenticate, redirect them to the AlphaBlock Auth hub.
// lib/auth/getAuthUrl.ts
export function getAuthUrl(): string {
const base = process.env.ALPHABLOCK_AUTH_URL
const projectId = process.env.ALPHABLOCK_PROJECT_ID
const callbackUrl = process.env.ALPHABLOCK_CALLBACK_URL
const params = new URLSearchParams({
projectId: projectId!,
callbackUrl: callbackUrl!,
})
return `${base}/authorize?${params.toString()}`
}Step 2 — Handle the Callback
Create a route handler at /auth/callback that receives the one-time code and exchanges it for a signed JWT.
// app/auth/callback/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { cookies } from 'next/headers'
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url)
const code = searchParams.get('code')
if (!code) {
return NextResponse.redirect(new URL('/login?error=missing_code', request.url))
}
const tokenRes = await fetch(`${process.env.ALPHABLOCK_AUTH_URL}/api/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
code,
projectId: process.env.ALPHABLOCK_PROJECT_ID,
projectSecret: process.env.ALPHABLOCK_PROJECT_SECRET,
}),
})
if (!tokenRes.ok) {
return NextResponse.redirect(new URL('/login?error=token_exchange_failed', request.url))
}
const { token } = await tokenRes.json()
const cookieStore = await cookies()
cookieStore.set('ab_session', token, {
httpOnly: true,
secure: true,
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 7,
})
return NextResponse.redirect(new URL('/dashboard', request.url))
}Step 3 — Verify the JWT
Install jose: npm install jose
// lib/auth/verifyToken.ts
import { createRemoteJWKSet, jwtVerify } from 'jose'
const JWKS = createRemoteJWKSet(
new URL('https://auth.alphablock.io/.well-known/jwks.json')
)
export type AlphaBlockSession = {
userId: string
email: string
walletAddress: string | null
discordId: string | null
pricingTier: 'genesis' | 'founding' | 'early' | 'core' | 'standard' | 'open'
slotNumber: number
accessLevel: 'tools' | 'full'
billingCycle: 'monthly' | 'biannual' | 'lifetime'
roles: string[]
tierExpiry: string | null
issuedBy: string
iat: number
exp: number
}
export async function verifyToken(token: string): Promise<AlphaBlockSession | null> {
try {
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'auth.alphablock.io',
})
return payload as unknown as AlphaBlockSession
} catch {
return null
}
}Step 4 — Read the Session
// lib/auth/getSession.ts
import { cookies } from 'next/headers'
import { verifyToken, AlphaBlockSession } from './verifyToken'
export async function getSession(): Promise<AlphaBlockSession | null> {
const cookieStore = await cookies()
const token = cookieStore.get('ab_session')?.value
if (!token) return null
return verifyToken(token)
}Step 5 — Protect Routes with Middleware
// middleware.ts (project root)
import { NextRequest, NextResponse } from 'next/server'
import { verifyToken } from '@/lib/auth/verifyToken'
const PROTECTED_PATHS = ['/dashboard', '/tools', '/settings']
const FULL_ACCESS_PATHS = ['/discord', '/perks', '/support']
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl
const isProtected = PROTECTED_PATHS.some(p => pathname.startsWith(p))
const requiresFull = FULL_ACCESS_PATHS.some(p => pathname.startsWith(p))
if (!isProtected && !requiresFull) return NextResponse.next()
const token = request.cookies.get('ab_session')?.value
if (!token) return NextResponse.redirect(new URL('/login', request.url))
const session = await verifyToken(token)
if (!session) {
const response = NextResponse.redirect(new URL('/login', request.url))
response.cookies.delete('ab_session')
return response
}
if (requiresFull && session.accessLevel !== 'full') {
return NextResponse.redirect(new URL('/upgrade', request.url))
}
return NextResponse.next()
}Step 6 — Log Out
// app/auth/logout/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { cookies } from 'next/headers'
export async function GET(request: NextRequest) {
const cookieStore = await cookies()
cookieStore.delete('ab_session')
return NextResponse.redirect(new URL('/login', request.url))
}Access Control Reference
| Property | Type | Use for |
|---|---|---|
accessLevel | 'tools' | 'full' | Gating Discord, perks, support |
pricingTier | string | Tier-based feature flags |
tierExpiry | string | null | Active subscription check |
billingCycle | string | Displaying subscription info |
roles | string[] | Custom role-based access |
Check subscription active
export function isSubscriptionActive(session: AlphaBlockSession): boolean {
if (session.billingCycle === 'lifetime') return true
if (!session.tierExpiry) return false
return new Date(session.tierExpiry) > new Date()
}Check minimum tier
const TIER_RANK: Record<string, number> = {
genesis: 1, founding: 2, early: 3, core: 4, standard: 5, open: 6,
}
export function meetsMinimumTier(
session: AlphaBlockSession,
minimum: string
): boolean {
return (TIER_RANK[session.pricingTier] ?? 99) <= (TIER_RANK[minimum] ?? 99)
}Files to Create
- .env.local
- lib/auth/getAuthUrl.ts
- lib/auth/verifyToken.ts
- lib/auth/getSession.ts
- app/auth/callback/route.ts
- app/auth/logout/route.ts
- middleware.ts
Only dependency: jose. Do not install NextAuth or any other auth library into consuming projects.
Endpoints
| Endpoint | Method | Description |
|---|---|---|
| /authorize | GET | Begin auth flow |
| /api/token | POST | Exchange code for JWT |
| /.well-known/jwks.json | GET | Public key set |
POST /api/token
// Request
{
"code": "one-time code from callback URL",
"projectId": "your project ID",
"projectSecret": "your project secret"
}
// Response
{
"token": "signed JWT string"
}Error Codes
| Status | Meaning |
|---|---|
| 400 | Missing required fields |
| 401 | Invalid projectSecret |
| 403 | Project not active or not registered |
| 410 | Code expired or already used |
Notes for AI Assistants
- Do not install NextAuth into consuming projects.
joseis the only auth dependency.- Session cookie is named
ab_session, ishttpOnly. - Always use
createRemoteJWKSetpointing to the JWKS endpoint. - The callback route must be a Route Handler, not a Server Component.
getSession()is async — always await it.tierExpiryisnullfor lifetime subscribers.accessLevelis the primary access gate.- Payment processing is stubbed — do not implement payments in consuming projects.