DocumentationExample Projects

Example Projects

End-to-end examples showing CoreSDK integration in production-style applications. Each demonstrates the OAuth app-store model: register app → get credentials → issue tokens → validate everywhere.

All examples work against a local sidecar. Set CORESDK_ENV=development for instant local testing. See Local Emulator.


FastAPI Auth Middleware
Python FastAPI service with CoreSDK middleware — every route validates Bearer tokens via gRPC and emits audit events automatically.
PythonFastAPIcoresdk
View Code
Next.js PKCE Flow
Next.js App Router app with full authorization_code + PKCE OAuth flow — users authenticate, consent to scopes, and the app holds a rotating refresh token.
Next.js 15TypeScript@cpod/sdk
View Code
Go Token Validator CLI
CLI tool that validates tokens, checks rate limits, and evaluates Rego policies — useful for debugging production auth issues without a web UI.
Gocobrasdk-go
View Code
Spring Boot Microservice
Java Spring Boot service with @EnableCoreSDK — auto-wired token validation, scope enforcement per endpoint, and OPA policy evaluation via annotation.
Java 21Spring Boot 3coresdk-spring
View Code

1. FastAPI Auth Middleware (Python)

CoreSDK as a FastAPI dependency — validates every request, emits an audit event, and injects the decoded claims into the route handler.

# app/auth.py
import os
from fastapi import Depends, HTTPException, Request
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from cpod import AsyncCpodClient
from cpod.errors import AuthError, CpodError
 
sdk = AsyncCpodClient.from_env()
bearer = HTTPBearer()
 
async def require_auth(
    credentials: HTTPAuthorizationCredentials = Depends(bearer),
    request: Request = None,
) -> dict:
    token = f"Bearer {credentials.credentials}"
    try:
        decision = await sdk.authorize(
            token,
            action=request.method.lower(),
            resource=request.url.path,
        )
    except AuthError as e:
        raise HTTPException(status_code=401, detail=str(e))
    except CpodError as e:
        raise HTTPException(status_code=403, detail=str(e))
 
    # Emit audit trail
    await sdk.audit.emit(
        actor=decision.claims.get("sub"),
        action=request.method.lower(),
        resource=request.url.path,
        outcome="allow",
    )
    return decision.claims
# app/main.py
from fastapi import FastAPI, Depends
from .auth import require_auth
 
app = FastAPI()
 
@app.get("/api/orders")
async def list_orders(claims: dict = Depends(require_auth)):
    return {"subject": claims["sub"], "orders": []}
 
@app.get("/api/reports")
async def get_reports(claims: dict = Depends(require_auth)):
    # claims["scope"] contains space-separated granted scopes
    if "reports.read" not in claims.get("scope", "").split():
        raise HTTPException(status_code=403, detail="Missing reports.read scope")
    return {"reports": []}
# Start with env vars pointing at local cpod-backend
CPOD_API_URL=http://localhost:8000 \
CPOD_CLIENT_ID=app-myservice \
CPOD_CLIENT_SECRET=cs_xxxx \
  fastapi dev app/main.py

2. Next.js PKCE Flow (TypeScript)

Full OAuth authorization_code + PKCE flow in a Next.js App Router application. The server-side route handler holds the token; the browser never sees it.

// app/auth/callback/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { cookies } from 'next/headers'
 
export async function GET(req: NextRequest) {
  const { searchParams } = new URL(req.url)
  const code = searchParams.get('code')
  const state = searchParams.get('state')
 
  // Validate CSRF state
  const cookieStore = await cookies()
  const savedState = cookieStore.get('oauth_state')?.value
  if (state !== savedState) {
    return NextResponse.json({ error: 'state_mismatch' }, { status: 400 })
  }
 
  const codeVerifier = cookieStore.get('code_verifier')?.value!
 
  // Exchange code for tokens (server-side — never in browser JS)
  const tokenResp = await fetch(`${process.env.CPOD_API_URL}/v1/oauth/token`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'authorization_code',
      client_id: process.env.CPOD_CLIENT_ID!,
      code: code!,
      redirect_uri: process.env.OAUTH_REDIRECT_URI!,
      code_verifier: codeVerifier,
    }),
  })
 
  const { access_token, refresh_token, expires_in } = await tokenResp.json()
 
  const response = NextResponse.redirect(new URL('/dashboard', req.url))
 
  // Store tokens in HttpOnly cookies — never expose to browser JS
  response.cookies.set('access_token', access_token, {
    httpOnly: true, secure: true, sameSite: 'lax',
    maxAge: expires_in,
  })
  response.cookies.set('refresh_token', refresh_token, {
    httpOnly: true, secure: true, sameSite: 'lax',
    maxAge: 60 * 60 * 24 * 30, // 30 days
  })
 
  return response
}
// app/auth/login/route.ts — initiate PKCE flow
import { NextResponse } from 'next/server'
import { cookies } from 'next/headers'
import crypto from 'crypto'
 
function generatePKCE() {
  const verifier = crypto.randomBytes(32).toString('base64url')
  const challenge = crypto.createHash('sha256').update(verifier).digest('base64url')
  return { verifier, challenge }
}
 
export async function GET() {
  const { verifier, challenge } = generatePKCE()
  const state = crypto.randomBytes(16).toString('hex')
 
  const params = new URLSearchParams({
    client_id: process.env.CPOD_CLIENT_ID!,
    response_type: 'code',
    redirect_uri: process.env.OAUTH_REDIRECT_URI!,
    scope: 'jobs.read files.read',
    code_challenge: challenge,
    code_challenge_method: 'S256',
    state,
  })
 
  const response = NextResponse.redirect(
    `${process.env.CPOD_API_URL}/oauth/authorize?${params}`
  )
 
  const cookieStore = await cookies()
  response.cookies.set('code_verifier', verifier, { httpOnly: true, sameSite: 'lax', maxAge: 300 })
  response.cookies.set('oauth_state', state, { httpOnly: true, sameSite: 'lax', maxAge: 300 })
 
  return response
}

3. Go Token Validator CLI

// cmd/validate.go
package cmd
 
import (
    "context"
    "encoding/json"
    "fmt"
    "os"
 
    cpod "github.com/cpod-ai/cpod-sdk-go"
    "github.com/spf13/cobra"
)
 
var validateCmd = &cobra.Command{
    Use:   "validate <token>",
    Short: "Validate a Bearer token and print claims",
    Args:  cobra.ExactArgs(1),
    RunE: func(cmd *cobra.Command, args []string) error {
        sdk, err := cpod.FromEnv()
        if err != nil {
            return err
        }
 
        decision, err := sdk.Authorize(context.Background(), "Bearer "+args[0])
        if err != nil {
            fmt.Fprintln(os.Stderr, "denied:", err)
            os.Exit(1)
        }
 
        out, _ := json.MarshalIndent(map[string]any{
            "allowed": decision.Allowed,
            "subject": decision.Claims["sub"],
            "scope":   decision.Claims["scope"],
        }, "", "  ")
        fmt.Println(string(out))
        return nil
    },
}
go run ./cmd validate eyJhbGci...
# {
#   "allowed": true,
#   "subject": "app-myservice",
#   "scope": "jobs.read files.write"
# }

4. Spring Boot Microservice (Java)

// CoreSDKConfig.java
@Configuration
@EnableCoreSDK
public class CoreSDKConfig {
    @Bean
    public CoreSDKProperties properties() {
        return CoreSDKProperties.builder()
            .apiUrl(System.getenv("CPOD_API_URL"))
            .clientId(System.getenv("CPOD_CLIENT_ID"))
            .clientSecret(System.getenv("CPOD_CLIENT_SECRET"))
            .build();
    }
}
// OrderController.java
@RestController
@RequestMapping("/api/orders")
public class OrderController {
 
    @Autowired private CoreSDK sdk;
 
    @GetMapping
    public ResponseEntity<?> listOrders(
        @RequestHeader("Authorization") String authHeader
    ) {
        AuthDecision decision = sdk.authorize(authHeader, "read", "/api/orders");
        if (!decision.isAllowed()) {
            return ResponseEntity.status(403).body(Map.of("error", decision.getReason()));
        }
        return ResponseEntity.ok(Map.of(
            "subject", decision.getClaims().getSub(),
            "orders", List.of()
        ));
    }
}
# application.yml
cpod:
  api-url: http://localhost:8000
  client-id: ${CPOD_CLIENT_ID}
  client-secret: ${CPOD_CLIENT_SECRET}

Running Locally

All examples follow the same pattern:

# 1. Start the local stack (see Local Emulator guide for full docker-compose)
docker compose up -d
 
# 2. Set SDK env vars (points at cpod-backend, not the sidecar directly)
export CPOD_API_URL=http://localhost:8000
export CPOD_CLIENT_ID=app-local
export CPOD_CLIENT_SECRET=cs_xxxx
 
# 3. Clone + run
git clone https://github.com/cpod-ai/example-<name>
cd example-<name>
# Python:     pip install -e . && fastapi dev
# TypeScript: npm install && npm run dev
# Go:         go run ./cmd
# Java:       ./mvnw spring-boot:run