Migrate from Cognito

Migrate from AWS Cognito

Cognito migrations are typically painful because AWS makes you stitch together list-users, admin-list-groups-for-user, and the federated-identity sub mapping yourself. This guide covers the full stitch.

What you'll keep, what you'll lose, what you'll gain

  • Keep: email + email_verified, sub (preserved in metadata and as an identity record), name / preferred_username, phone_number, custom attributes (custom:* preserved under user.metadata.custom), groups (each Cognito group becomes an Authio org), MFA enrolled flag.
  • Lose: Cognito-managed passwords (Authio is passwordless), TOTP secrets (only the enrolled flag is preserved), AWS-specific risk-based-authentication state.
  • Gain: A real management API. Webhooks (Cognito has Lambda triggers; Authio has webhooks + a query-able audit stream). The risk engine without per-Identity-Pool quirks. First-class multi-org.

Cognito's three-export pain

Cognito makes you make three different API calls to get the full picture of a user:

  1. list-users gives you the standard attributes, custom attributes, and federated identity sub-IDs in Attributes[].
  2. admin-list-groups-for-user gives you the groups (one call per user).
  3. If you have a Cognito Identity Pool (not a User Pool — they're different products), federated provider mappings live there. The importer accepts an enriched FederatedIdentities[] field on each user.

The CLI snippet below does all three.

Custom attributes

Cognito custom attributes always have the custom: prefix on the wire. The importer strips the prefix and stores them under user.metadata.custom:

Cognito:  {"Name":"custom:tier","Value":"enterprise"}
Authio:   user.metadata.custom = {"tier": "enterprise"}

Sessions

Cognito sessions and refresh tokens are dropped. Users re-authenticate via passkey or magic-link.

Rollback plan

Cognito User Pools are not real-time queryable by external services without trade-offs. The cleanest cutover is to run the importer once just before the switch, then flip your app's auth provider. If you need a phased rollout, run the importer nightly — new Cognito users will appear in Authio on the next run.

Step-by-step: CLI

# 1) Export users from the User Pool.
aws cognito-idp list-users --user-pool-id $POOL --max-results 60 > cognito.json
# (Repeat with --pagination-token and merge "Users" arrays for >60 users.)

# 2) Enrich with groups for each user.
jq -r '.Users[].Username' cognito.json | while read u; do
  aws cognito-idp admin-list-groups-for-user --user-pool-id $POOL --username "$u" \
    | jq --arg u "$u" '{user: $u, groups: .Groups}'
done | jq -s '.' > groups.json

# 3) Merge groups into the cognito.json (jq one-liner; adapt to your shell).
jq --slurpfile g groups.json '.Users |= map(. + {Groups:
  (($g[0] // []) | map(select(.user == $key.Username)) | first.groups // [])})' \
  cognito.json > cognito-merged.json

# 4) Dry-run.
authio import cognito --input ./cognito-merged.json --dry-run | jq '.stats'

# 5) Apply.
authio import cognito --input ./cognito-merged.json

Step-by-step: dashboard wizard

Visit app.authio.com/migrate/cognito.

Live import (paste an API token)

Cognito's live importer isn't wired in this release — the AWS SDK is the only stable client for cognito-idp.ListUsers, and packaging it into the Authio CLI/dashboard is on the next iteration's plate.

Use the file-upload path above for now. When the live importer lands, the dashboard's Connect with API token tab will accept an AWS access key + secret + region + user pool ID.

Post-migration checklist

  • Update your application's Cognito SDK calls to Authio. The drop-in pattern is similar: read the session from an Authio cookie + verify the JWT against Authio's JWKS.
  • Re-create any Cognito Lambda triggers as Authio webhooks (one webhook subscriber per trigger).
  • If you used a Cognito Identity Pool for federated AWS access, keep using it — Authio doesn't replace IAM. The Identity Pool's authenticated-role can swap from Cognito Auth to OIDC pointed at Authio.
  • Send migration emails.
Cognito's sub attribute is the user's UUID. If your app stored Cognito sub as a foreign key in your own database, you'll need to either keep that mapping (the importer preserves sub under source_external_ids) or do a one-time SQL migration to map sub → Authio user_id.