import { WorkOS } from '@workos-inc/node';
import { NextResponse, type NextRequest } from 'next/server';
const workos = new WorkOS(process.env.WORKOS_API_KEY);
const clientId = process.env.WORKOS_CLIENT_ID!;
/**
* Enterprise SSO Callback: Handles SAML 2.0 / OIDC assertion from Okta/Azure AD
* Translates enterprise identity assertions into standard local JWT session.
*/
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const code = searchParams.get('code');
if (!code) {
return NextResponse.json({ error: 'Missing SSO authorization code' }, { status: 400 });
}
try {
// 1. Authenticate SAML assertion with WorkOS without $20k Auth0 Enterprise MSA
const { user, profile } = await workos.sso.getProfileAndToken({
code,
clientId,
});
// 2. Extract verified corporate claims
const { email, firstName, lastName, organizationId, idpId } = profile;
// 3. Upsert user in your own primary database (Supabase, Postgres, Prisma)
const dbUser = await upsertCorporateUser({
email,
fullName: `${firstName} ${lastName}`,
enterpriseTenantId: organizationId,
ssoProviderId: idpId,
emailVerified: true,
});
// 4. Issue standard HTTP-only session cookie
const sessionToken = await createSessionToken(dbUser);
const response = NextResponse.redirect(new URL('/dashboard', request.url));
response.cookies.set('session', sessionToken, {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 7, // 7 days
});
return response;
} catch (error) {
console.error('WorkOS SSO callback error:', error);
return NextResponse.redirect(new URL('/login?error=sso_failed', request.url));
}
}
version: '3.8'
services:
# BoxyHQ SAML Jackson: Converts SAML 2.0 Identity assertions to OAuth 2.0 / OIDC
# Replaces Auth0 Enterprise SSO wall with $0 license fee container proxy
saml-jackson:
image: boxyhq/jackson:latest
container_name: boxyhq_jackson
restart: unless-stopped
ports:
- "5225:5225"
environment:
- EXTERNAL_URL=https://sso.yourcompany.com
- JACKSON_API_KEYS=prod_secret_jackson_api_key_9948
- DB_ENGINE=postgres
- DB_URL=postgresql://jackson_user:secure_password@postgres:5432/jackson_db?sslmode=disable
- CLIENT_SECRET_VERIFIER=super_secret_session_verifier_token
- PRE_LOADED_CONFIG=/etc/jackson/saml-config
- AUDIENCE_URL=https://sso.yourcompany.com
depends_on:
- postgres
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5225/api/health"]
interval: 10s
timeout: 5s
retries: 3
postgres:
image: postgres:16-alpine
container_name: jackson_postgres
restart: unless-stopped
environment:
POSTGRES_USER: jackson_user
POSTGRES_PASSWORD: secure_password
POSTGRES_DB: jackson_db
volumes:
- jackson_pgdata:/var/lib/postgresql/data
volumes:
jackson_pgdata:
# Terraform AWS ECS Fargate Cluster for BoxyHQ Jackson SAML Proxy
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.0" }
}
}
resource "aws_ecs_cluster" "sso_cluster" {
name = "enterprise-sso-proxy-cluster"
}
resource "aws_ecs_task_definition" "jackson_task" {
family = "saml-jackson"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "512" # 0.5 vCPU
memory = "1024" # 1 GB RAM
container_definitions = jsonencode([
{
name = "saml-jackson"
image = "boxyhq/jackson:v1.30.0"
essential = true
portMappings = [{ containerPort = 5225, hostPort = 5225 }]
environment = [
{ name = "EXTERNAL_URL", value = "https://auth-sso.yourdomain.com" },
{ name = "DB_ENGINE", value = "postgres" }
]
secrets = [
{ name = "DB_URL", valueFrom = aws_ssm_parameter.sso_db_url.arn },
{ name = "JACKSON_API_KEYS", valueFrom = aws_ssm_parameter.sso_api_key.arn }
]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = "/ecs/saml-jackson"
"awslogs-region" = "us-east-1"
"awslogs-stream-prefix" = "sso"
}
}
}
])
}
resource "aws_ecs_service" "jackson_service" {
name = "saml-jackson-service"
cluster = aws_ecs_cluster.sso_cluster.id
task_definition = aws_ecs_task_definition.jackson_task.arn
desired_count = 2
launch_type = "FARGATE"
network_configuration {
subnets = ["subnet-abc123", "subnet-def456"]
security_groups = ["sg-sso-01"]
assign_public_ip = true
}
}