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).TerraformRuntimeand the AWSInfrastructureProviderpick those credentials up automatically on the next workspace apply / destroy / workload op. Each destructive run is audited against the operator'ssubclaim. - 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 byaws_wif:default. Both stores coexist: theirCredentialKey.purposevalues are disjoint (aws_ssovsaws_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):
POST /admin/settings/credentials/sso/device-authorize-- startssso-oidc.register_client+start_device_authorization. Returns the user code + verification URI for the operator to complete in a browser.POST /admin/settings/credentials/sso/poll-token-- pollssso-oidc.create_tokenuntil the operator approves the device. AWS'authorization_pending/slow_down/expired_token/access_deniedcodes map onto HTTP 202 / 429 / 401 / 401 so the wizard's poll loop can react.POST /admin/settings/credentials/sso/accounts-- lists accounts the SSO access token can reach viasso.list_accounts.POST /admin/settings/credentials/sso/permission-sets-- lists permission set role names assigned to a chosen account viasso.list_account_roles.POST /admin/settings/credentials/sso/get-role-credentials-- exchanges the SSO access token for short-lived STS credentials viasso.get_role_credentials. Audit-first: aWorkloadRun(action=MINT_CLOUD_CREDENTIAL)row lands inpendingBEFORE the boto3 call, thensucceeded/failedAFTER.
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:
- The Phase 2 Terraform module
aws_iam_identity_centerprovisions the Entra OIDC provider on AWS plus analphaswarm-admin-serviceIAM role whose trust policy condition pins the audience + the admin app'ssubclaim. - The
EntraAwsWifStoreresolver entry mints an OIDC token viaMsalEntraProvider.m2m_token(audience="api://AzureADTokenExchange")then exchanges it viasts:AssumeRoleWithWebIdentityfor STS creds. Cached in-memory with TTL = STS expiry minus 60s safety margin. - The
refresh_aws_wif_tokensCelery 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:AssumeRoleonboarding flow. That is a separate auth method on the samecloud_awsprovider (theiam_role_external_idbranch); 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
alphaswarm_admin/docs/aws-sso-runbook.md-- step-by-step operator runbook.- credentials.md -- the
CredentialResolverchain. - identity.md -- inbound
IdentityProviderchain. - management-engine.md -- audit-first + step-up posture for every mutating admin route.
- admin-agent-identity.md -- the Entra Agent Identity story consumed by Phase 2's WIF path.