Saltar al contenido principal

AWS Identity Center / SSO admin platform access

Status: Phase 1 (operator-attributed flow) shipped. Phase 2 (service-attributed Entra -> AWS WIF) plumbed but the matching Terraform module is intentionally empty until production rollout.

In plain English: before AlphaSwarm staff can create, change, or delete anything in the company's AWS cloud account, they must prove who they are through a short-lived, audited sign-in — there are no long-lived cloud passwords stored anywhere. This page explains the two sign-in paths: one for a human operator at a keyboard, and one for unattended background services.

The AlphaSwarm hosted platform runs on AWS. AlphaSwarm staff drive provisioning, deployment, management, and destruction of platform resources from the alphaswarm_admin service via two complementary AWS authentication paths:

  • Operator-attributed: an AlphaSwarm staff operator signs in to AWS Identity Center via the OIDC device flow. The resulting short-lived STS credentials (~1h validity) are cached in AwsSsoCredentialStore (priority 4). TerraformRuntime and the AWS InfrastructureProvider pick those credentials up automatically on the next workspace apply / destroy / workload op. Each destructive run is audited against the operator's sub claim.
  • Service-attributed (Phase 2): for unattended runs (Celery beat, watchdog, autonomous agents) the admin service federates from its Entra Agent Identity into an AWS IAM role via OIDC web identity (sts:AssumeRoleWithWebIdentity). Same resolver chain, different priority-4 store keyed by aws_wif:default. Both stores coexist: their CredentialKey.purpose values are disjoint (aws_sso vs aws_wif).

The boundary contract: every existing AlphaSwarm rule holds. CredentialResolver (rule 26) is the only credential surface, TerraformRuntime (rule 42) is the only Terraform driver, WorkloadRuntime (rule 45) is the only workload op driver, every mutating admin route is step-up MFA gated (rule 52), and no token ever appears in a transcript or audit ledger (alphaswarm-management-engine.mdc).

End-to-end flow​

Five admin BFF endpoints (mirror of five control-plane endpoints):

  1. POST /admin/settings/credentials/sso/device-authorize -- starts sso-oidc.register_client + start_device_authorization. Returns the user code + verification URI for the operator to complete in a browser.
  2. POST /admin/settings/credentials/sso/poll-token -- polls sso-oidc.create_token until the operator approves the device. AWS' authorization_pending / slow_down / expired_token / access_denied codes map onto HTTP 202 / 429 / 401 / 401 so the wizard's poll loop can react.
  3. POST /admin/settings/credentials/sso/accounts -- lists accounts the SSO access token can reach via sso.list_accounts.
  4. POST /admin/settings/credentials/sso/permission-sets -- lists permission set role names assigned to a chosen account via sso.list_account_roles.
  5. POST /admin/settings/credentials/sso/get-role-credentials -- exchanges the SSO access token for short-lived STS credentials via sso.get_role_credentials. Audit-first: a WorkloadRun(action=MINT_CLOUD_CREDENTIAL) row lands in pending BEFORE the boto3 call, then succeeded / failed AFTER.

Resolver chain after Phase 1​

The three priority-4 stores coexist because their keys are disjoint by purpose: aws_sso vs aws_wif vs broker. The resolver walks all stores in priority order; each store returns None for keys it does not own, so dispatch is by (purpose, service-prefix) rather than priority.

Workspace pinning​

Each Terraform workspace can carry an optional (aws_account_id, aws_permission_set_name, aws_region) tuple in terraform_workspaces (Alembic 0089, additive nullable columns). When set, the TerraformRuntime._resolve_aws_creds hook reads the operator's matching SSO session via:

CredentialKey(
service=f"aws_sso:{account_id}:{permission_set_name}:{operator_sub}",
purpose="aws_sso",
)

and injects AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN / AWS_REGION into the env dict that run_command() passes to subprocess.run. The hook is fully additive -- workspaces without an AWS pin behave exactly as they did before, falling through to ambient env (IRSA, AWS_PROFILE, etc.).

Operator runbook​

Brief; the full step-by-step lives at alphaswarm_admin/docs/aws-sso-runbook.md.

# Sign in via the CLI (opens the browser, polls, picks account, mints)
alphaswarm-cli aws sso-login \
--start-url https://alphaswarm.awsapps.com/start \
--region us-east-1

# Or skip the pickers if the platform account + role are stable
alphaswarm-cli aws sso-login \
--start-url https://alphaswarm.awsapps.com/start \
--account-id 111122223333 \
--permission-set AlphaSwarmPlatformOperator

# Inspect the cached session (always redacted)
alphaswarm-cli aws sso-status

# Clear the local pointer (server-side audit row stays)
alphaswarm-cli aws sso-logout

All three commands talk only to the admin BFF (ALPHASWARM_CLI_ADMIN_URL, default http://localhost:8900). The CLI never holds raw STS keys; those land in aws_sso_sessions server-side, envelope-encrypted via vault_transit.encrypt.

Phase 2: service-attributed WIF setup​

For unattended runs (Celery beat, watchdogs, autonomous agents) the admin service federates from its Entra Agent Identity into an AWS IAM role via OIDC web-identity federation. Three pieces:

  1. The Phase 2 Terraform module aws_iam_identity_center provisions the Entra OIDC provider on AWS plus an alphaswarm-admin-service IAM role whose trust policy condition pins the audience + the admin app's sub claim.
  2. The EntraAwsWifStore resolver entry mints an OIDC token via MsalEntraProvider.m2m_token(audience="api://AzureADTokenExchange") then exchanges it via sts:AssumeRoleWithWebIdentity for STS creds. Cached in-memory with TTL = STS expiry minus 60s safety margin.
  3. The refresh_aws_wif_tokens Celery beat task forces a re-mint on the configured interval (default 30 min) so unattended workloads never hit a cold cache.

Operator workflow to enable Phase 2:

# 1. Populate the seed spec from your Identity Center inventory
cat > /tmp/aws-iam-identity-center.tfvars.json <<EOF
{
"enabled": true,
"identity_center_instance_arn": "arn:aws:sso:::instance/ssoins-XXXX",
"identity_store_id": "d-9067XXXXXX",
"permission_sets": [
{ "name": "AlphaSwarmPlatformOperator",
"description": "Full operator access",
"session_duration": "PT8H",
"managed_policies": ["arn:aws:iam::aws:policy/AdministratorAccess"]
}
],
"entra_tenant_id": "<alphaswarm staff tenant id>",
"wif_subjects": ["<admin-service app object id>"],
"wif_role_managed_policies": ["arn:aws:iam::aws:policy/PowerUserAccess"]
}
EOF

# 2. Snapshot the spec into terraform_stack_spec_versions
python alphaswarm/scripts/identity/seed_aws_iam_identity_center.py --apply

# 3. Apply through TerraformRuntime (audit-first, step-up gated)
alphaswarm-cli manage terraform apply \
--workspace-id aws-identity-center \
--spec-version-id <output-from-seed>

# 4. Wire the resulting role ARN into the runtime env
export ALPHASWARM_AWS_WIF_ROLE_ARN="$(terraform output -raw service_role_arn)"
export ALPHASWARM_AWS_WIF_AUDIENCE="api://AzureADTokenExchange"

# 5. Restart the alphaswarm + worker stack so the resolver picks up the new role
alphaswarm-cli deploy restart

After Phase 2 is live, Celery workers and watchdog tasks will see the WIF resolver entry on every cold start and the admin service's unattended terraform applies stop requiring an operator's SSO session to be active. Operator-attributed runs continue to take precedence -- the resolver checks aws_sso:* keys before falling back to aws_wif:default.

What this never replaces​

  • The customer-facing per-org cross-account sts:AssumeRole onboarding flow. That is a separate auth method on the same cloud_aws provider (the iam_role_external_id branch); SSO is for AlphaSwarm staff acting on the platform account, not for customer accounts.
  • The inbound login provider AwsIamIdentityCenterProvider. That governs how AlphaSwarm staff sign in to the AlphaSwarm UI; this work governs outbound AWS access. Orthogonal.
  • The AwsSecretsManagerStore (priority 30). Untouched; still resolves Secrets Manager keys for the deployments that have opted into it.

See also​