diff --git a/.changeset/proud-brooms-live.md b/.changeset/proud-brooms-live.md new file mode 100644 index 0000000000..bbbfd72644 --- /dev/null +++ b/.changeset/proud-brooms-live.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/auth-construct-alpha': patch +--- + +OIDC attributeRequestMethod no longer requires importing OidcAttributeRequestMethod. diff --git a/.eslint_dictionary.json b/.eslint_dictionary.json index cc9fa4b7d4..c8be0f945f 100644 --- a/.eslint_dictionary.json +++ b/.eslint_dictionary.json @@ -63,6 +63,7 @@ "javascript", "jsdoc", "jsons", + "jwks", "keyof", "lang", "linux", diff --git a/packages/auth-construct/API.md b/packages/auth-construct/API.md index 4a20e65565..62b4e620e7 100644 --- a/packages/auth-construct/API.md +++ b/packages/auth-construct/API.md @@ -89,7 +89,9 @@ export type MFASettings = { }; // @public -export type OidcProviderProps = Omit; +export type OidcProviderProps = Omit & { + readonly attributeRequestMethod?: 'GET' | 'POST'; +}; // @public export type PhoneNumberLogin = true | { diff --git a/packages/auth-construct/src/construct.test.ts b/packages/auth-construct/src/construct.test.ts index 5618e6f6ce..136b8900a3 100644 --- a/packages/auth-construct/src/construct.test.ts +++ b/packages/auth-construct/src/construct.test.ts @@ -1230,6 +1230,13 @@ void describe('Auth construct', () => { void it('supports oidc and email', () => { const app = new App(); const stack = new Stack(app); + const authorizationURL = 'http://localhost:3000/authorization'; + const jwksURI = 'https://localhost:3000/jwksuri'; + const tokensURL = 'http://localhost:3000/token'; + const userInfoURL = 'http://localhost:3000/userinfo'; + const mockIdentifiers = ['one', 'two']; + const mockScopes = ['scope1', 'scope2']; + const attributeRequestMethod = 'POST'; new AmplifyAuth(stack, 'test', { loginWith: { email: true, @@ -1239,6 +1246,20 @@ void describe('Auth construct', () => { clientSecret: oidcClientSecret, issuerUrl: oidcIssuerUrl, name: oidcProviderName, + attributeMapping: { + email: { + attributeName: 'email', + }, + }, + attributeRequestMethod: attributeRequestMethod, + endpoints: { + authorization: authorizationURL, + jwksUri: jwksURI, + token: tokensURL, + userInfo: userInfoURL, + }, + identifiers: mockIdentifiers, + scopes: mockScopes, }, callbackUrls: ['https://redirect.com'], logoutUrls: ['https://logout.com'], @@ -1250,10 +1271,106 @@ void describe('Auth construct', () => { UsernameAttributes: ['email'], AutoVerifiedAttributes: ['email'], }); - template.hasResourceProperties( - 'AWS::Cognito::UserPoolIdentityProvider', - ExpectedOidcIDPProperties - ); + template.hasResourceProperties('AWS::Cognito::UserPoolIdentityProvider', { + AttributeMapping: { + email: 'email', + }, + IdpIdentifiers: mockIdentifiers, + ProviderDetails: { + attributes_request_method: attributeRequestMethod, + attributes_url: userInfoURL, + authorize_scopes: mockScopes.join(' '), + authorize_url: authorizationURL, + client_id: oidcClientId, + client_secret: oidcClientSecret, + jwks_uri: jwksURI, + oidc_issuer: oidcIssuerUrl, + token_url: tokensURL, + }, + ProviderName: oidcProviderName, + ProviderType: 'OIDC', + }); + template.hasResourceProperties('AWS::Cognito::IdentityPool', { + OpenIdConnectProviderARNs: [ + Match.objectEquals({ + 'Fn::Join': [ + '', + [ + 'arn:aws:iam:', + { Ref: 'AWS::Region' }, + ':', + { Ref: 'AWS::AccountId' }, + ':oidc-provider/cognito-idp.', + { Ref: 'AWS::Region' }, + '.amazonaws.com/', + { Ref: 'testOidcIDP12B3582F' }, + ], + ], + }), + ], + }); + }); + void it('oidc defaults to GET for oidc method', () => { + const app = new App(); + const stack = new Stack(app); + const authorizationURL = 'http://localhost:3000/authorization'; + const jwksURI = 'https://localhost:3000/jwksuri'; + const tokensURL = 'http://localhost:3000/token'; + const userInfoURL = 'http://localhost:3000/userinfo'; + const mockIdentifiers = ['one', 'two']; + const mockScopes = ['scope1', 'scope2']; + new AmplifyAuth(stack, 'test', { + loginWith: { + email: true, + externalProviders: { + oidc: { + clientId: oidcClientId, + clientSecret: oidcClientSecret, + issuerUrl: oidcIssuerUrl, + name: oidcProviderName, + attributeMapping: { + email: { + attributeName: 'email', + }, + }, + endpoints: { + authorization: authorizationURL, + jwksUri: jwksURI, + token: tokensURL, + userInfo: userInfoURL, + }, + identifiers: mockIdentifiers, + scopes: mockScopes, + }, + callbackUrls: ['https://redirect.com'], + logoutUrls: ['https://logout.com'], + }, + }, + }); + const template = Template.fromStack(stack); + template.hasResourceProperties('AWS::Cognito::UserPool', { + UsernameAttributes: ['email'], + AutoVerifiedAttributes: ['email'], + }); + template.hasResourceProperties('AWS::Cognito::UserPoolIdentityProvider', { + AttributeMapping: { + email: 'email', + }, + IdpIdentifiers: mockIdentifiers, + ProviderDetails: { + attributes_request_method: 'GET', + attributes_url: userInfoURL, + authorize_scopes: mockScopes.join(' '), + authorize_url: authorizationURL, + client_id: oidcClientId, + client_secret: oidcClientSecret, + jwks_uri: jwksURI, + oidc_issuer: oidcIssuerUrl, + token_url: tokensURL, + }, + ProviderName: oidcProviderName, + ProviderType: 'OIDC', + }); template.hasResourceProperties('AWS::Cognito::IdentityPool', { OpenIdConnectProviderARNs: [ Match.objectEquals({ diff --git a/packages/auth-construct/src/construct.ts b/packages/auth-construct/src/construct.ts index a3a69637c4..0ef9e87054 100644 --- a/packages/auth-construct/src/construct.ts +++ b/packages/auth-construct/src/construct.ts @@ -11,6 +11,7 @@ import { CfnUserPoolClient, Mfa, OAuthScope, + OidcAttributeRequestMethod, UserPool, UserPoolClient, UserPoolIdentityProviderAmazon, @@ -696,14 +697,29 @@ export class AmplifyAuth result.providersList.push('APPLE'); } if (external.oidc) { + const oidc = external.oidc; + const requestMethod = + oidc.attributeRequestMethod === undefined + ? 'GET' // default if not defined + : oidc.attributeRequestMethod; result.oidc = new cognito.UserPoolIdentityProviderOidc( this, `${this.name}OidcIDP`, { userPool, - ...external.oidc, + attributeRequestMethod: + requestMethod === 'GET' + ? OidcAttributeRequestMethod.GET + : OidcAttributeRequestMethod.POST, + clientId: oidc.clientId, + clientSecret: oidc.clientSecret, + endpoints: oidc.endpoints, + identifiers: oidc.identifiers, + issuerUrl: oidc.issuerUrl, + name: oidc.name, + scopes: oidc.scopes, attributeMapping: - external.oidc.attributeMapping ?? shouldMapEmailAttributes + oidc.attributeMapping ?? shouldMapEmailAttributes ? { email: { attributeName: 'email', diff --git a/packages/auth-construct/src/types.ts b/packages/auth-construct/src/types.ts index 3291c88f09..107821b67a 100644 --- a/packages/auth-construct/src/types.ts +++ b/packages/auth-construct/src/types.ts @@ -139,8 +139,19 @@ export type FacebookProviderProps = Omit< */ export type OidcProviderProps = Omit< cognito.UserPoolIdentityProviderOidcProps, - 'userPool' ->; + 'userPool' | 'attributeRequestMethod' +> & { + /** + * The method to use to request attributes + * @default 'GET' + * + * For details about each option, see below. + * + * 'GET' - use GET + * 'POST' - use POST + */ + readonly attributeRequestMethod?: 'GET' | 'POST'; +}; /** * SAML provider.