Skip to content

fix(cli): fix OAuth provider scopes and attribute mappings in generate#14777

Draft
9pace wants to merge 2 commits intogen2-migrationfrom
fix/oauth-generate-scopes-attributes
Draft

fix(cli): fix OAuth provider scopes and attribute mappings in generate#14777
9pace wants to merge 2 commits intogen2-migrationfrom
fix/oauth-generate-scopes-attributes

Conversation

@9pace
Copy link
Copy Markdown

@9pace 9pace commented Apr 13, 2026

Summary

  • Fix scope field name typo: authorized_scopesauthorize_scopes (Cognito's actual field name)
  • Remove incorrect VALID_SCOPES filter that mangled provider scopes (e.g. Facebook's public_profileprofile)
  • Route non-standard attribute mapping keys (e.g. usernamesub/id) into custom sub-object instead of dropping them

Before (generated resource.ts)

google: {
  clientId: secret('GOOGLE_CLIENT_ID'),
  clientSecret: secret('GOOGLE_CLIENT_SECRET'),
  attributeMapping: {
    email: 'email',
  },
},
facebook: {
  clientId: secret('FACEBOOK_CLIENT_ID'),
  clientSecret: secret('FACEBOOK_CLIENT_SECRET'),
  attributeMapping: {
    email: 'email',
  },
},

Scopes missing entirely. usernamesub/id mapping dropped.

After (generated resource.ts)

google: {
  clientId: secret('GOOGLE_CLIENT_ID'),
  clientSecret: secret('GOOGLE_CLIENT_SECRET'),
  scopes: ['openid', 'email', 'profile'],
  attributeMapping: {
    email: 'email',
    custom: {
      username: 'sub',
    },
  },
},
facebook: {
  clientId: secret('FACEBOOK_CLIENT_ID'),
  clientSecret: secret('FACEBOOK_CLIENT_SECRET'),
  scopes: ['email', 'public_profile'],
  attributeMapping: {
    email: 'email',
    custom: {
      username: 'id',
    },
  },
},

Caveats

  • Provider scopes are no longer validated — the old validation was against the wrong namespace (Cognito OAuth scopes vs provider scopes), but there is now no validation at all
  • OIDC/SAML providers flatten standard + custom attributes back into a single Record since their rendering path doesn't support the custom sub-object

Test plan

  • Verified end-to-end with a test app using Google and Facebook social login: Gen2 deployed with correct scopes and attribute mappings without manual post-generate edits to OAuth config
  • Run generate on existing migration test apps with social providers
  • Verify OIDC/SAML provider rendering is unchanged

Three bugs in auth.renderer.ts caused generate to produce broken OAuth
config for social identity providers (Google, Facebook):

1. Scope field name typo: deriveProviderSpecificScopes() looked for
   'authorized_scopes' but Cognito returns 'authorize_scopes'. Scopes
   were never collected. Added the correct field name to the lookup.

2. Scope mangling: provider scopes were filtered against VALID_SCOPES
   (Cognito OAuth scopes), which is the wrong namespace. Facebook's
   'public_profile' was mapped to 'profile'. Removed the filter —
   provider scopes are now passed through as-is.

3. Attribute mapping: filterAttributeMapping() dropped keys not in
   MAPPED_USER_ATTRIBUTE_NAME (e.g. 'username' -> 'sub' for Google,
   'username' -> 'id' for Facebook). Changed to route unknown keys
   into a 'custom' sub-object matching Gen2's AttributeMapping CDK
   interface.

Caveats:
- Provider scopes are no longer validated. The old validation was
  incorrect (wrong namespace), but there is now no validation at all —
  whatever the provider returns is passed through verbatim.
- OIDC/SAML providers flatten standard + custom back into a single
  Record since their rendering path doesn't support the custom
  sub-object. If a custom key collides with a mapped standard key,
  the custom value wins.

Verified end-to-end with a test app using Google and Facebook social
login. Gen2 deployed with correct scopes and attribute mappings
without manual post-generate edits to OAuth config.
@9pace 9pace force-pushed the fix/oauth-generate-scopes-attributes branch from 1ffface to 66e090b Compare April 13, 2026 21:08
Two bugs in cfn-output-resolver.ts caused incorrect template resolution
during the refactor step:

1. Phase 1 GetAtt resolution passed already-resolved ARN values through
   buildArn(), producing nested ARNs (e.g. arn:.../userpool/arn:...).
   Added a guard to short-circuit when the output value is already an ARN.

2. Phase 2 fallback resolution used PhysicalResourceId for Custom::
   resources, but custom resource GetAtt attributes come from the backing
   Lambda's response Data — not the physical ID. These are now skipped
   so CloudFormation evaluates them at deploy time.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant