Authentication & Authorization

Authentication Methods

OAuth 2.0 for Human Users

┌──────────────────────────────────────────────────────────────────────────────┐
│                    OAUTH 2.0 AUTHORIZATION CODE FLOW                          │
│                                                                              │
│  ┌──────────┐                          ┌──────────────┐                      │
│  │  Browser │                          │   Authority   │                      │
│  └────┬─────┘                          └──────┬───────┘                      │
│       │                                       │                               │
│       │ 1. Login request                      │                               │
│       │ ────────────────────────────────────► │                               │
│       │                                       │                               │
│       │ 2. Redirect to IdP                    │                               │
│       │ ◄──────────────────────────────────── │                               │
│       │                                       │                               │
│       │ 3. User authenticates at IdP          │                               │
│       │ ─────────────────────────────────►    │                               │
│       │                                       │                               │
│       │ 4. IdP callback with code             │                               │
│       │ ◄──────────────────────────────────── │                               │
│       │                                       │                               │
│       │ 5. Exchange code for tokens           │                               │
│       │ ────────────────────────────────────► │                               │
│       │                                       │                               │
│       │ 6. Access token + refresh token       │                               │
│       │ ◄──────────────────────────────────── │                               │
│       │                                       │                               │
└──────────────────────────────────────────────────────────────────────────────┘

mTLS for Agents

Agents authenticate using mutual TLS with certificates issued by Stella’s internal CA.

Registration Flow:

  1. Admin generates one-time registration token
  2. Agent starts with registration token
  3. Agent submits CSR (Certificate Signing Request)
  4. Authority issues certificate signed by Stella CA
  5. Agent uses certificate for all subsequent requests

API Keys for Service-to-Service

External services can use API keys for programmatic access:

JWT Token Structure

Access Token Claims

interface AccessTokenClaims {
  // Standard claims
  iss: string;          // "https://authority.stella.local"
  sub: string;          // User ID
  aud: string[];        // ["stella-api"]
  exp: number;          // Expiration timestamp
  iat: number;          // Issued at timestamp
  jti: string;          // Unique token ID

  // Custom claims
  tenant_id: string;
  roles: string[];
  permissions: Permission[];
  email?: string;
  name?: string;
}

Token Lifetimes

Token TypeLifetimeRefresh
Access Token15 minutesVia refresh token
Refresh Token7 daysRotated on use
Agent Token1 hourVia mTLS connection
API KeyConfigurableNot refreshed

Authorization Model

Resource Types

type ResourceType =
  | "environment"
  | "release"
  | "promotion"
  | "target"
  | "agent"
  | "workflow"
  | "plugin"
  | "integration"
  | "evidence";

Action Types

type ActionType =
  | "create"
  | "read"
  | "update"
  | "delete"
  | "execute"
  | "approve"
  | "deploy"
  | "rollback";

Permission Structure

interface Permission {
  resource: ResourceType;
  action: ActionType;
  scope?: PermissionScope;
  conditions?: Condition[];
}

type PermissionScope =
  | "*"                              // All resources
  | { environmentId: UUID }          // Specific environment
  | { labels: Record<string, string> }; // Label-based

Built-in Roles

RoleDescriptionKey Permissions
adminFull accessAll permissions
release_managerManage releases and promotionsCreate releases, request promotions
deployerExecute deploymentsApprove promotions (where allowed), view releases
approverApprove promotionsApprove promotions (SoD respected)
viewerRead-only accessRead all resources
agentAgent service accountExecute deployment tasks

Role Definitions

const roles = {
  admin: {
    permissions: [
      { resource: "*", action: "*" }
    ]
  },
  release_manager: {
    permissions: [
      { resource: "release", action: "create" },
      { resource: "release", action: "read" },
      { resource: "release", action: "update" },
      { resource: "promotion", action: "create" },
      { resource: "promotion", action: "read" },
      { resource: "environment", action: "read" },
      { resource: "workflow", action: "read" },
      { resource: "workflow", action: "execute" }
    ]
  },
  deployer: {
    permissions: [
      { resource: "release", action: "read" },
      { resource: "promotion", action: "read" },
      { resource: "promotion", action: "approve" },
      { resource: "environment", action: "read" },
      { resource: "target", action: "read" },
      { resource: "agent", action: "read" }
    ]
  },
  approver: {
    permissions: [
      { resource: "promotion", action: "read" },
      { resource: "promotion", action: "approve" },
      { resource: "release", action: "read" },
      { resource: "environment", action: "read" }
    ]
  },
  viewer: {
    permissions: [
      { resource: "*", action: "read" }
    ]
  }
};

Environment-Scoped Permissions

Permissions can be scoped to specific environments:

// User can approve promotions only to staging
{
  resource: "promotion",
  action: "approve",
  scope: { environmentId: "staging-env-id" }
}

// User can deploy only to targets with specific labels
{
  resource: "target",
  action: "deploy",
  scope: { labels: { "tier": "frontend" } }
}

Separation of Duties (SoD)

When SoD is enabled for an environment:

interface ApprovalValidation {
  promotionId: UUID;
  approverId: UUID;
  requesterId: UUID;
  sodRequired: boolean;
  sodSatisfied: boolean;
  validationResult: "valid" | "self_approval_denied" | "sod_violation";
}

Permission Checking Algorithm

async function checkPermission(
  userId: UUID,
  resource: ResourceType,
  action: ActionType,
  resourceId?: UUID
): Promise<boolean> {
  // 1. Get user's roles and direct permissions
  const userRoles = await getUserRoles(userId);
  const userPermissions = await getUserPermissions(userId);

  // 2. Expand role permissions
  const rolePermissions = userRoles.flatMap(r => roles[r].permissions);
  const allPermissions = [...rolePermissions, ...userPermissions];

  // 3. Check for matching permission
  for (const perm of allPermissions) {
    if (matchesResource(perm.resource, resource) &&
        matchesAction(perm.action, action) &&
        matchesScope(perm.scope, resourceId) &&
        evaluateConditions(perm.conditions)) {
      return true;
    }
  }

  return false;
}

function matchesResource(pattern: string, resource: string): boolean {
  return pattern === "*" || pattern === resource;
}

function matchesAction(pattern: string, action: string): boolean {
  return pattern === "*" || pattern === action;
}

API Authorization Headers

All API requests require:

Authorization: Bearer <access_token>

For agent requests (over mTLS):

X-Agent-Id: <agent_id>
Authorization: Bearer <agent_token>

Permission Denied Response

{
  "success": false,
  "error": {
    "code": "PERMISSION_DENIED",
    "message": "User does not have permission to approve promotions to production",
    "details": {
      "resource": "promotion",
      "action": "approve",
      "scope": { "environmentId": "prod-env-id" },
      "requiredRoles": ["admin", "approver"],
      "userRoles": ["viewer"]
    }
  }
}

Deployment Share Grants (resource-scoped delegation)

Full model, endpoints, and tables: architecture — Deployment Share Grants. Decision record: ADR-027.

A deployment share grant lets an operator delegate a single deployment’s read or operate/deploy capability to a specific authenticated person, time-boxed, revocable, and audited. It is a ReleaseOrchestrator-local authorization mechanism, deliberately not a new global scope and not an Authority/identity-envelope change.

Key authorization properties (all server-enforced in DeploymentShareGrantService):

References