Skip to Content
Developer GuideAuthentication

ARK Authentication System

ARK implements a comprehensive authentication system supporting both user authentication and service-to-service authentication. The system includes OpenID Connect (OIDC) for user access to the dashboard and HTTP Basic Authentication with API keys for programmatic service access.

Overview

The authentication system consists of several key components:

User Authentication (Dashboard)

  • OIDC Provider Integration: Connects to any OIDC-compliant identity provider
  • Session Management: Handles user sessions with automatic token refresh
  • Middleware Authentication: Protects routes and proxies API calls with authentication
  • Frontend Integration: Provides user authentication state across React components
  • Token Management: Automatic access token refresh and propagation to backend services

Service Authentication (APIs)

  • API Key Management: Create, manage, and revoke API keys for service-to-service communication
  • HTTP Basic Authentication: Secure authentication using API key credentials
  • Flexible Authentication Modes: Support for OIDC-only, API key-only, or hybrid authentication
  • Kubernetes Secret Storage: API keys stored securely as Kubernetes secrets with bcrypt hashing

Architecture

┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ User Browser │ │ ARK Dashboard │ │ ARK API Server │ │ │ │ │ │ │ │ ┌─────────────┐│ │ ┌──────────────┐ │ │ ┌─────────────┐ │ │ │ React App ││ │ │ NextAuth.js │ │ │ │ API Services│ │ │ │ Components ││ │ │ + Middleware │ │ │ │ │ │ │ └─────────────┘│ │ └──────────────┘ │ │ └─────────────┘ │ └─────────────────┘ └──────────────────┘ └─────────────────┘ │ │ │ │ 1. Access protected │ │ │ page │ │ ├───────────────────────>│ │ │ │ 2. Check auth │ │ │ middleware │ │ │ │ │ 3. Redirect to OIDC │ │ │ if unauthenticated │ │ │<───────────────────────┤ │ │ │ │ │ 4. API calls with │ 5. Proxy with Bearer │ │ session context │ token │ ├───────────────────────>├───────────────────────>│ │ │ │

Configuration

Environment Variables

The ARK Dashboard uses environment variables for authentication configuration. You can find a complete template in the .env.example file in the dashboard directory.

The following environment variables are required for authentication:

Core Authentication Settings

# NextAuth.js Configuration AUTH_SECRET="your-secret-key-here" # Required: JWT signing secret (used to encrypt tokens) BASE_URL="http://localhost:3000" # Required: Base URL of the Next.js application AUTH_URL="http://localhost:3000/api/auth" # Required: URL used for handling auth related calls # OIDC Provider Configuration OIDC_PROVIDER_ID="your-provider-id" # Required: Custom ID for the OIDC provider OIDC_ISSUER_URL="https://your-oidc-provider.com" # Required: OIDC issuer URL OIDC_CLIENT_ID="your-client-id" # Required: OIDC client ID OIDC_CLIENT_SECRET="your-client-secret" # Optional: OIDC client secret OIDC_PROVIDER_NAME="Your Provider" # Optional: Display name for the OIDC provider in UI # Authentication Mode AUTH_MODE="hybrid" # Required: see Authentication Modes section below

Authentication Modes

The AUTH_MODE environment variable controls the authentication behavior:

  • sso: OIDC/JWT authentication only. Users must authenticate with the OIDC provider. Service-to-service access requires JWT tokens.
  • basic: API key authentication only. All access requires HTTP Basic Authentication with API keys. Dashboard authentication is disabled.
  • hybrid (recommended): Both OIDC and API key authentication. Users can access the dashboard via OIDC, services can use API keys.
  • open: No authentication required. The application allows unrestricted access without signing in.

⚠️ Security Warning: Only use AUTH_MODE=open in development or trusted environments. Production deployments should use hybrid or sso.

⚠️ Invalid Values: If AUTH_MODE is set to an invalid value (not sso, basic, hybrid, or open), it will automatically default to open for development safety. A warning will be logged when this occurs.

OIDC Provider Setup

The dashboard supports any OIDC-compliant identity provider. Configure your provider with:

Redirect URIs

Configure your OIDC provider with the following callback URL pattern:

https://your-domain.com/api/auth/callback/{OIDC_PROVIDER_ID}

For example, if your OIDC_PROVIDER_ID=mid, the callback URL would be:

https://your-domain.com/api/auth/callback/mid

Note: This callback URL is handled by the /api/auth/[...nextauth] catch-all route, which processes the callback/mid path segment to identify the provider and complete the OIDC authorization code flow.

Logout URIs

https://your-domain.com/signout

Required Scopes

  • openid - Core OIDC functionality
  • profile - User profile information
  • email - User email address

Components and Hooks

Authentication Hooks

useSession()

Access the current user session from NextAuth.js:

import { useSession } from "next-auth/react"; export function MyComponent() { const { data: session, status } = useSession(); if (status === "loading") return <div>Loading...</div>; if (status === "unauthenticated") return <div>Not authenticated</div>; return <div>Welcome, {session?.user?.name}!</div>; }

useRefreshAccessToken()

Automatically handles token refresh on window focus and interval:

import { useRefreshAccessToken } from "@/hooks/useRefreshAccessToken"; export function DashboardLayout({ children }: { children: React.ReactNode }) { useRefreshAccessToken(); // Handles automatic token refresh return <div>{children}</div>; }

useAutoSignout()

Automatically signs out users when their session expires:

import { useAutoSignout } from "@/hooks/useAutoSignout"; export function DashboardLayout({ children }: { children: React.ReactNode }) { useAutoSignout(); // Handles automatic signout on expiration return <div>{children}</div>; }

Authentication Components

UserDetails

Displays current user information with avatar:

import { UserDetails } from "@/components/user"; export function AppSidebar() { return ( <div> <UserDetails /> {/* Shows user name, email, and avatar */} </div> ); }

SessionProvider

Provides session context to the entire application:

import { SessionProvider } from "next-auth/react"; import { auth } from "@/auth"; export default async function RootLayout({ children }: { children: React.ReactNode }) { const session = await auth(); // Server-side session return ( <SessionProvider session={session}> {children} </SessionProvider> ); }

Middleware Authentication

The middleware handles route protection and API proxying with automatic token injection.

Route Protection

All dashboard routes (except auth routes) are protected by default:

export const config = { matcher: '/((?!api/auth|signout|_next/static|_next/image|favicon.ico).*)' };

API Token Proxying

API calls to /api/* are automatically:

  1. Proxied to the backend ARK API service
  2. Enhanced with authentication headers
  3. Configured with forwarding headers for backend context
// Middleware automatically adds these headers: headers.set('Authorization', `Bearer ${token.access_token}`); headers.set('X-Forwarded-Prefix', '/api'); headers.set('X-Forwarded-Host', request.headers.get('host')); headers.set('X-Forwarded-Proto', request.nextUrl.protocol);

API Routes

Authentication Endpoints

/api/auth/signin

Custom signin endpoint that overrides the default NextAuth.js signin:

// GET /api/auth/signin?callbackUrl=/dashboard // Custom implementation that directly redirects to OIDC provider // Bypasses NextAuth.js default signin page

This custom route uses the configured OIDC_PROVIDER_ID to redirect users directly to the OIDC provider, preserving the callback URL for post-authentication navigation.

/api/auth/[...nextauth]

NextAuth.js catch-all route that handles all authentication endpoints:

// Handles all auth-related requests including: // - /api/auth/signin // - /api/auth/callback/{provider} // - /api/auth/signout // - /api/auth/session // - /api/auth/providers // - /api/auth/csrf

This single route exports the GET and POST handlers from the main auth configuration and manages the complete OIDC flow including authorization code exchange and session creation.

/api/auth/federated-signout

Implements federated logout with the OIDC provider:

// GET /api/auth/federated-signout // Redirects to OIDC provider's end_session_endpoint // Includes id_token_hint and post_logout_redirect_uri

This endpoint performs a complete logout by redirecting to the OIDC provider’s end_session_endpoint to terminate the session on both the dashboard and the identity provider.

⚠️ OIDC Provider Compatibility Warning: Some OIDC providers may not properly handle the end_session_endpoint call or may not support all the required parameters (id_token_hint, post_logout_redirect_uri, client_id). This can result in sessions getting stuck on the provider side, causing issues with proper signout. See Federated Logout Issues in the troubleshooting section for more details.

Authentication Flow

The authentication flow varies based on the AUTH_MODE setting:

SSO Mode (AUTH_MODE=sso)

  1. Initial Access: User accesses protected route
  2. Middleware Check: Middleware validates session
  3. Redirect to OIDC: If unauthenticated, redirect to /api/auth/signin
  4. OIDC Provider: User authenticates with external provider
  5. Callback Processing: OIDC callback creates NextAuth session
  6. Session Storage: Session stored securely with HTTP-only cookies
  7. API Token Injection: Middleware adds Bearer token to backend API calls

Basic Mode (AUTH_MODE=basic)

  1. API Key Required: All requests must include HTTP Basic Authentication headers
  2. No Dashboard Authentication: OIDC authentication is disabled
  3. Service-to-Service Only: Designed for headless/API-only deployments

Hybrid Mode (AUTH_MODE=hybrid)

  1. Dashboard Users: Authenticate via OIDC/JWT (same as SSO mode)
  2. Service Access: Use HTTP Basic Authentication with API keys
  3. Flexible Access: Both authentication methods work on all API endpoints
  4. Recommended: Best for production with both human and service users

Open Mode (AUTH_MODE=open)

  1. Initial Access: User accesses any route
  2. No Authentication: Middleware allows all requests to pass through
  3. Direct API Access: API calls are proxied directly without authentication headers

Note: In open mode, no authentication tokens are added to backend API calls. The backend must be configured to handle unauthenticated requests appropriately.

API Key Authentication

ARK supports HTTP Basic Authentication using API keys for service-to-service communication. This authentication method is ideal for programmatic access, CI/CD systems, and headless services.

API Key Namespace Scoping

API keys in ARK are namespace-scoped for multi-tenant security:

  • Each namespace maintains its own set of API keys
  • API keys are stored as Kubernetes secrets with type ark.mckinsey.com/api-key
  • Service accounts only have permissions within their deployment namespace
  • True tenant isolation: Tenant A cannot access Tenant B’s API keys

This design ensures that in multi-tenant deployments, each tenant’s API keys remain private and isolated from other tenants. When you create an API key, it is stored in the current namespace context (or the namespace you specify explicitly).

Creating API Keys

API keys can be created through the ARK Dashboard (when AUTH_MODE=hybrid or AUTH_MODE=sso) or directly via API endpoints.

Via Dashboard

  1. Navigate to Service API Keys in the dashboard sidebar
  2. Click Add API Key
  3. Provide a descriptive name and optional expiration date
  4. Copy the generated public and secret keys immediately (secret is shown only once)

Via API Endpoint

# Create in current namespace curl -X POST http://localhost:8000/v1/api-keys \ -H "Content-Type: application/json" \ -d '{ "name": "My Service Key", "expires_at": "2024-12-31T23:59:59Z" }'

Note: The API key will be stored in the current namespace only. You must authenticate against the ARK API instance in the same namespace where the API key was created.

Using API Keys

API keys use HTTP Basic Authentication where:

  • Username: Public key (starts with pk-ark-)
  • Password: Secret key (starts with sk-ark-)
import requests from requests.auth import HTTPBasicAuth # API credentials public_key = "pk-ark-abcd1234efgh5678" secret_key = "sk-ark-wxyz9876uvst5432" # Make authenticated request response = requests.get( "http://localhost:8000/v1/namespaces/default/agents", auth=HTTPBasicAuth(public_key, secret_key) )

API Key Management

List API Keys

# List in current namespace GET /v1/api-keys

Create API Key

# Create in current namespace POST /v1/api-keys Content-Type: application/json { "name": "Service Key Name", "expires_at": "2024-12-31T23:59:59Z" // Optional }

Revoke API Key

# Revoke in current namespace DELETE /v1/api-keys/{public_key}

Security Considerations

  • Secret Key Storage: Secret keys are hashed using bcrypt before storage in Kubernetes secrets
  • Single Display: Secret keys are shown only once during creation - store them securely
  • Namespace Isolation: API keys are namespace-scoped for multi-tenant security
  • Tenant Isolation: Each tenant’s API keys are isolated (cannot access other tenants’ keys)
  • Kubernetes RBAC: Service accounts only have permissions within their deployment namespace
  • Expiration: API keys can have optional expiration dates for enhanced security
  • Soft Delete: Revoked API keys are marked inactive but retained for audit trails
  • Per-Namespace Storage: API keys are stored as Kubernetes secrets in their respective namespaces for true tenant isolation

Security Features

Token Security

  • HTTP-Only Cookies: Session tokens never exposed to client JavaScript
  • Secure Transmission: All auth communication over HTTPS in production
  • Token Refresh: Automatic access token refresh before expiration
  • Backend Isolation: Access tokens only sent to backend, never exposed to frontend

Session Management

  • Automatic Expiration: NextAuth issues encrypted JWT tokens with a 30-minute TTL. Sessions are refreshed during active use but expire after 30 minutes of inactivity
  • Window Focus Refresh: JWT refreshed when user returns to tab
  • Interval Refresh: Periodic refresh of OIDC access and refresh tokens from the identity provider (every 10 minutes)
  • Error Handling: Automatic redirect to signin on token refresh failure

Route Protection

  • Middleware Guards: All routes protected by authentication middleware
  • Callback URL Preservation: Original destination preserved during auth flow
  • Public Route Exclusions: Auth routes and static assets excluded from protection

High Availability

  • Session state stored in JWT cookies (stateless)
  • No session storage requirements
  • Supports horizontal scaling

Troubleshooting

Common Issues

Authentication Loops

  • Verify AUTH_URL matches external domain
  • Check OIDC provider redirect URI configuration
  • Ensure BASE_URL is set correctly for reverse proxy deployments

Token Refresh Failures

  • Verify OIDC provider supports refresh tokens
  • Check OIDC_CLIENT_SECRET configuration
  • Monitor browser console for refresh errors

Federated Logout Issues

Federated logout can fail when OIDC providers don’t properly handle the logout process, causing sessions to remain active on the identity provider side.

Symptoms
  • Users appear signed out from the dashboard but remain signed in to the OIDC provider
  • Subsequent signin attempts bypass the login screen due to existing provider session
  • Users cannot fully sign out until provider session expires
Understanding OpenID Connect RP-Initiated Logout

OpenID Connect RP-Initiated Logout is a specification that enables complete logout from both the application (ARK Dashboard) and the identity provider.

Standard vs Federated Logout:

Client Side Logout OnlyFederated Logout
1. User clicks “Sign Out”1. User clicks “Sign Out”
2. Dashboard clears its session2. Dashboard redirects to provider’s logout endpoint
3. User appears logged out3. Provider terminates its session
4. Provider session remains active4. Provider redirects back to dashboard
5. Next signin bypasses login screen5. Complete logout from both systems
Technical Requirements

For federated logout to work properly, the identity provider must:

  1. Expose End Session Endpoint

    { "issuer": "https://provider.example.com", "end_session_endpoint": "https://provider.example.com/oidc/logout" }
  2. Support Required Parameters

    • id_token_hint : Helps identify which session to terminate
    • post_logout_redirect_uri : Where to redirect after logout
    • client_id: Application identifier
  3. Example Logout URL

    https://provider.example.com/oidc/logout? id_token_hint=eyJhbGciOiJSUzI1NiIs...& post_logout_redirect_uri=https://ark-dashboard.com/signout& client_id=ark-dashboard-client
Common Causes
  • Provider doesn’t support the end_session_endpoint
  • Provider doesn’t handle id_token_hint parameter correctly
  • Provider ignores post_logout_redirect_uri parameter
  • Incomplete OpenID Connect logout implementation
Solutions
  1. Verify Provider Support Check that your OIDC provider fully supports OpenID Connect RP-Initiated Logout 

  2. Test End Session Endpoint

    curl -v "https://your-provider.com/end_session?id_token_hint=TOKEN&post_logout_redirect_uri=https://your-domain.com/signout"
  3. Provider Configuration Some providers require explicit configuration to enable logout functionality. Check provider-specific documentation for logout setup requirements.

Debug Logging

Enable NextAuth debug logging:

AUTH_DEBUG=true

Monitor authentication flow in browser developer tools and server logs.

Example Configurations

BASE_URL="https://your-domain.com" AUTH_URL="https://your-domain.com/api/auth" AUTH_SECRET="your-generated-secret-key" AUTH_MODE="hybrid" # OIDC configuration for dashboard users OIDC_PROVIDER_ID="your-provider" OIDC_PROVIDER_NAME="Your Provider" OIDC_ISSUER_URL="https://your-oidc-provider.com" OIDC_CLIENT_ID="your-client-id" OIDC_CLIENT_SECRET="your-client-secret"
Last updated on