# 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: - Keys are tenant-scoped - Keys can have restricted permissions - Keys can have expiration dates - Key usage is audited ## JWT Token Structure ### Access Token Claims ```typescript 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 Type | Lifetime | Refresh | |------------|----------|---------| | Access Token | 15 minutes | Via refresh token | | Refresh Token | 7 days | Rotated on use | | Agent Token | 1 hour | Via mTLS connection | | API Key | Configurable | Not refreshed | ## Authorization Model ### Resource Types ```typescript type ResourceType = | "environment" | "release" | "promotion" | "target" | "agent" | "workflow" | "plugin" | "integration" | "evidence"; ``` ### Action Types ```typescript type ActionType = | "create" | "read" | "update" | "delete" | "execute" | "approve" | "deploy" | "rollback"; ``` ### Permission Structure ```typescript interface Permission { resource: ResourceType; action: ActionType; scope?: PermissionScope; conditions?: Condition[]; } type PermissionScope = | "*" // All resources | { environmentId: UUID } // Specific environment | { labels: Record }; // Label-based ``` ### Built-in Roles | Role | Description | Key Permissions | |------|-------------|-----------------| | `admin` | Full access | All permissions | | `release_manager` | Manage releases and promotions | Create releases, request promotions | | `deployer` | Execute deployments | Approve promotions (where allowed), view releases | | `approver` | Approve promotions | Approve promotions (SoD respected) | | `viewer` | Read-only access | Read all resources | | `agent` | Agent service account | Execute deployment tasks | ### Role Definitions ```typescript 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: ```typescript // 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: - The user who requested a promotion cannot approve it - The user who created a release cannot be the sole approver - Approval records include SoD verification status ```typescript interface ApprovalValidation { promotionId: UUID; approverId: UUID; requesterId: UUID; sodRequired: boolean; sodSatisfied: boolean; validationResult: "valid" | "self_approval_denied" | "sod_violation"; } ``` ## Permission Checking Algorithm ```typescript async function checkPermission( userId: UUID, resource: ResourceType, action: ActionType, resourceId?: UUID ): Promise { // 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: ```http Authorization: Bearer ``` For agent requests (over mTLS): ```http X-Agent-Id: Authorization: Bearer ``` ## Permission Denied Response ```json { "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"] } } } ``` ## References - [Security Overview](overview.md) - [Agent Security](agent-security.md) - [Authority Module](../../../authority/architecture.md)