9.7 KiB
9.7 KiB
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:
- Admin generates one-time registration token
- Agent starts with registration token
- Agent submits CSR (Certificate Signing Request)
- Authority issues certificate signed by Stella CA
- 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
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
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
| 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
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:
- 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
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"]
}
}
}