Skip to main content

Authentication

OctoMesh uses OAuth 2.0 and OpenID Connect (OIDC) for authentication. This page explains how authentication works, which flows are supported, and what happens during the login process.

Overview

Supported Flows

Authorization Code with PKCE

Used by web applications like Data Refinery Studio. The browser redirects to the Identity Service login page, the user authenticates, and the browser receives an authorization code that is exchanged for tokens.

  • PKCE (Proof Key for Code Exchange) is required — prevents authorization code interception
  • No client secret needed for public clients (SPAs)
  • Supports offline_access for refresh tokens

Device Code

Used by octo-cli and other devices without a browser. The CLI displays a URL and code, the user opens the URL in any browser, enters the code, and authenticates.

  • The CLI polls /connect/token until the user completes authentication
  • Supports offline_access for long-lived sessions

Client Credentials

Used for service-to-service communication where no user is involved. The client authenticates with its own credentials (client ID + secret) and receives an access token.

  • No user context — the token has no sub claim
  • Client-credentials tokens bypass tenant authorization (no allowed_tenants check)
  • Used for background jobs, inter-service calls, and automated processes

Multi-Tenant Authentication

Authentication in OctoMesh is always scoped to a tenant. The tenant ID determines which user database, identity providers, and roles are used.

Tenant Resolution

Different endpoints resolve the tenant differently:

EndpointTenant Source
REST API (/{tenantId}/v1/...)URL path segment
/connect/authorizeacr_values=tenant:{tenantId} query parameter
/connect/tokenMapped from authorization code or refresh token
/connect/endsessionDecoded from id_token_hint JWT

The Identity Service scopes authentication cookies per tenant by appending the tenant ID to cookie names:

Standard CookieTenant-Scoped Cookie
.AspNetCore.Identity.Application.AspNetCore.Identity.Application.{tenantId}
idsrvidsrv.{tenantId}
idsrv.sessionidsrv.session.{tenantId}

This prevents session leakage between tenants when a user has sessions in multiple tenants on the same browser.

Per-Tenant Identity Provider Schemes

External identity provider schemes are registered with a tenant prefix to prevent conflicts:

{tenantId}:{providerName}

Examples:
octosystem:Google
customer-project:AzureAD

Each tenant independently configures which providers are available and their credentials. Providers are dynamically registered at runtime — adding or updating a provider takes effect immediately without restarting the service.

Access Token Structure

When authentication succeeds, the Identity Service issues a JWT access token containing:

Standard Claims

ClaimDescriptionExample
subUser ID"a1b2c3d4"
preferred_usernameUsername"john.doe"
nameDisplay name"John Doe"
emailEmail address"john@example.com"
given_nameFirst name"John"
family_nameLast name"Doe"

OctoMesh-Specific Claims

ClaimDescriptionExample
tenant_idTenant the user logged into"customer-project"
allowed_tenantsTenants the user may access (repeated claim)"customer-project", "octosystem"
roleEffective roles (direct + group-inherited, repeated claim)"Development", "DashboardViewer"
home_tenant_idFor cross-tenant users: their parent tenant"octosystem"

Effective Roles

The role claims include the union of:

  • Roles directly assigned to the user
  • Roles inherited through group membership (resolved recursively up to 10 levels of nesting)

Roles are resolved at token issuance time and updated on token refresh.

Allowed Tenants

The allowed_tenants claims are resolved from:

  1. The login tenant — always included
  2. The home tenant — for cross-tenant users, their parent tenant
  3. Ancestor tenants — walks up the tenant hierarchy via OctoTenantIdentityProvider parent references (up to 10 levels)
  4. Descendant tenants — BFS traversal down through child tenants where an ExternalTenantUserMapping exists for this user (cascading through multiple levels)

Token Validation by Services

All OctoMesh services validate incoming requests using the same pattern:

Bearer Token Authentication

Services validate JWT tokens against the Identity Service's signing keys. The token must:

  • Be a valid JWT signed by the Identity Service
  • Not be expired
  • Contain the required scopes for the requested operation

Tenant Authorization

After standard JWT validation, the TenantAuthorizationMiddleware enforces tenant access:

  1. Extract {tenantId} from the request URL path
  2. Read the allowed_tenants claims from the validated token
  3. If the route tenant is not in the allowed list → 403 Forbidden
  4. Client-credentials tokens (no sub claim) bypass this check
Request: GET /customer-project/v1/runtime/entities

Token claims:
sub: "a1b2c3d4"
allowed_tenants: ["customer-project", "octosystem"]

→ "customer-project" is in allowed_tenants → Access granted
Request: GET /other-tenant/v1/runtime/entities

Token claims:
sub: "a1b2c3d4"
allowed_tenants: ["customer-project", "octosystem"]

→ "other-tenant" is NOT in allowed_tenants → 403 Forbidden

Authentication Flow: Step by Step

This section traces a complete authentication flow for a browser-based application.

1. User Opens Application

The client application (e.g., Data Refinery Studio) detects no valid session and redirects to the Identity Service:

GET /connect/authorize?
client_id=octo-data-refinery-studio
&response_type=code
&scope=openid profile email role octo_api
&redirect_uri=https://studio.example.com/callback
&code_challenge=...
&code_challenge_method=S256
&acr_values=tenant:customer-project

2. Identity Service Presents Login Page

The Identity Service renders the login page for the customer-project tenant, showing the available identity providers (e.g., "Corporate Azure AD", local login form).

3. User Authenticates

The user either:

  • Enters username/password (validated against the tenant's local user database)
  • Clicks an external provider button (redirected to Google, Azure AD, etc.)
  • Is authenticated via a parent tenant (if an OctoTenantIdentityProvider is configured)

4. First Login Processing

On first login via an external provider, the Identity Service:

  1. Creates a local user record linked to the external identity
  2. Checks email domain group rules and adds the user to matching groups
  3. If the provider has a DefaultGroupRtId, adds the user to that group
  4. Resolves effective roles from group memberships

5. Token Issuance

The Identity Service:

  1. Resolves all effective roles (direct + group-inherited)
  2. Resolves allowed tenants (login tenant + home tenant + child tenants with mappings)
  3. Issues a JWT access token with all claims
  4. Returns the token to the client application

6. API Calls

The client includes the access token in API requests:

GET /customer-project/v1/runtime/entities
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...

The target service validates the token and checks tenant authorization before processing the request.

Initial Setup

The very first time the Identity Service starts, no users exist. The setup endpoint creates the initial administrator:

octo-cli -c Setup -e "admin@example.com" -p "SecurePassword123"

This endpoint:

  1. Verifies no users exist in the system tenant (fails otherwise)
  2. Creates the admin user with the specified credentials
  3. The admin can then log in and configure tenants, clients, and providers

After setup, the system tenant has:

  • The admin user
  • Default clients (octo-cli, Swagger UI)
  • Default identity resources (openid, profile, email, role)
  • Default API scopes and resources
  • Default identity providers (Google, Microsoft — disabled; system tenant only)
  • Default roles and TenantOwners group