From 99093cb950bab099c02ee300e54b1fa45a02d58c Mon Sep 17 00:00:00 2001 From: Meng Cao Date: Sun, 23 Nov 2025 16:20:59 -0500 Subject: [PATCH 1/2] feat(auth): support custom domain prefix for cognito domain in user pool This PR adds support for custom domain prefix for cognito domain in user pool. Instead of showing a cryptic domain name to customers when they login with an external identity provider such as Google, this allows the Amplify customer to provide a domain prefix associated with their own brand. Although this doesn't resolve https://github.com/aws-amplify/amplify-backend/issues/2350, it can serve as a stop gap solution to make the login domain more trust-inspiring. --- package-lock.json | 22 +++---- packages/backend-auth/src/factory.ts | 5 +- .../src/translate_auth_props.test.ts | 66 +++++++++++++++++++ .../backend-auth/src/translate_auth_props.ts | 28 +++++++- packages/backend-auth/src/types.ts | 7 +- 5 files changed, 104 insertions(+), 24 deletions(-) diff --git a/package-lock.json b/package-lock.json index 667e60ca9d0..6a051a023ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49307,7 +49307,7 @@ }, "packages/auth-construct": { "name": "@aws-amplify/auth-construct", - "version": "1.8.2", + "version": "1.9.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.7.1", @@ -49322,17 +49322,17 @@ }, "packages/backend": { "name": "@aws-amplify/backend", - "version": "1.17.0", + "version": "1.18.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/backend-auth": "^1.7.2", + "@aws-amplify/backend-auth": "^1.8.0", "@aws-amplify/backend-data": "^1.6.2", - "@aws-amplify/backend-function": "^1.15.0", + "@aws-amplify/backend-function": "^1.15.1", "@aws-amplify/backend-output-schemas": "^1.7.1", "@aws-amplify/backend-output-storage": "^1.3.2", "@aws-amplify/backend-secret": "^1.4.1", "@aws-amplify/backend-storage": "^1.4.2", - "@aws-amplify/client-config": "^1.8.1", + "@aws-amplify/client-config": "^1.9.0", "@aws-amplify/data-schema": "^1.13.4", "@aws-amplify/platform-core": "^1.10.1", "@aws-amplify/plugin-types": "^1.11.1", @@ -49367,10 +49367,10 @@ }, "packages/backend-auth": { "name": "@aws-amplify/backend-auth", - "version": "1.7.2", + "version": "1.8.0", "license": "Apache-2.0", "dependencies": { - "@aws-amplify/auth-construct": "^1.8.2", + "@aws-amplify/auth-construct": "^1.9.0", "@aws-amplify/backend-output-schemas": "^1.7.1", "@aws-amplify/backend-output-storage": "^1.3.2", "@aws-amplify/plugin-types": "^1.11.1" @@ -49458,7 +49458,7 @@ }, "packages/backend-function": { "name": "@aws-amplify/backend-function", - "version": "1.15.0", + "version": "1.15.1", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.7.1", @@ -49697,7 +49697,7 @@ }, "packages/client-config": { "name": "@aws-amplify/client-config", - "version": "1.8.1", + "version": "1.9.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-output-schemas": "^1.7.1", @@ -52104,12 +52104,12 @@ }, "packages/seed": { "name": "@aws-amplify/seed", - "version": "1.0.2", + "version": "1.1.0", "license": "Apache-2.0", "dependencies": { "@aws-amplify/backend-secret": "^1.4.1", "@aws-amplify/cli-core": "^2.2.2", - "@aws-amplify/client-config": "^1.8.1", + "@aws-amplify/client-config": "^1.9.0", "@aws-amplify/platform-core": "^1.10.1", "@aws-amplify/plugin-types": "^1.11.1", "@aws-sdk/client-cognito-identity-provider": "^3.750.0", diff --git a/packages/backend-auth/src/factory.ts b/packages/backend-auth/src/factory.ts index 3d632e82151..671422695fa 100644 --- a/packages/backend-auth/src/factory.ts +++ b/packages/backend-auth/src/factory.ts @@ -161,6 +161,7 @@ class AmplifyAuthGenerator implements ConstructContainerEntryGenerator { loginWith: translateToAuthConstructLoginWith( this.props.loginWith, backendSecretResolver, + stableBackendIdentifiers, ), senders: translateToAuthConstructSenders( this.props.senders, @@ -168,10 +169,6 @@ class AmplifyAuthGenerator implements ConstructContainerEntryGenerator { ), outputStorageStrategy: this.getInstanceProps.outputStorageStrategy, }; - if (authProps.loginWith.externalProviders) { - authProps.loginWith.externalProviders.domainPrefix = - stableBackendIdentifiers.getStableBackendHash(); - } let authConstruct: AmplifyAuth; try { diff --git a/packages/backend-auth/src/translate_auth_props.test.ts b/packages/backend-auth/src/translate_auth_props.test.ts index 304af562c09..7495771b717 100644 --- a/packages/backend-auth/src/translate_auth_props.test.ts +++ b/packages/backend-auth/src/translate_auth_props.test.ts @@ -3,6 +3,7 @@ import { BackendSecret, BackendSecretResolver, ResolvePathResult, + StableBackendIdentifiers, } from '@aws-amplify/plugin-types'; import { describe, it } from 'node:test'; import { AuthLoginWithFactoryProps } from './types.js'; @@ -31,6 +32,8 @@ const appleKeyId = 'appleKeyId'; const applePrivateKey = 'applePrivateKey'; const callbackUrls = ['a', 'b']; const logoutUrls = ['a', 'b']; +const stableBackendHash = 'testStableBackendHash'; +const domainPrefix = 'testDomainPrefix'; const testBackendIdentifier: BackendIdentifier = { namespace: 'testBackendId', @@ -68,8 +71,15 @@ class TestBackendSecretResolver implements BackendSecretResolver { }; } +class TestStableBackendIdentifiers implements StableBackendIdentifiers { + getStableBackendHash = (): string => { + return stableBackendHash; + }; +} + void describe('translateToAuthConstructLoginWith', () => { const backendResolver = new TestBackendSecretResolver(); + const stableBackendIdentifiers = new TestStableBackendIdentifiers(); void it('translates with external providers', () => { const loginWith: AuthLoginWithFactoryProps = { @@ -108,6 +118,7 @@ void describe('translateToAuthConstructLoginWith', () => { const translated = translateToAuthConstructLoginWith( loginWith, backendResolver, + stableBackendIdentifiers, ); const expected: AuthProps['loginWith'] = { @@ -140,6 +151,7 @@ void describe('translateToAuthConstructLoginWith', () => { }, callbackUrls: callbackUrls, logoutUrls: logoutUrls, + domainPrefix: stableBackendHash, }, }; assert.deepStrictEqual(translated, expected); @@ -157,6 +169,7 @@ void describe('translateToAuthConstructLoginWith', () => { const translated = translateToAuthConstructLoginWith( loginWith, backendResolver, + stableBackendIdentifiers, ); const expected: AuthProps['loginWith'] = { @@ -164,8 +177,60 @@ void describe('translateToAuthConstructLoginWith', () => { externalProviders: { callbackUrls: callbackUrls, logoutUrls: logoutUrls, + domainPrefix: stableBackendHash, + }, + }; + assert.deepStrictEqual(translated, expected); + }); + + void it('translates without custom domain prefix', () => { + const loginWith: AuthLoginWithFactoryProps = { + externalProviders: { + callbackUrls: callbackUrls, + logoutUrls: logoutUrls, + }, + }; + + const translated = translateToAuthConstructLoginWith( + loginWith, + backendResolver, + stableBackendIdentifiers, + ); + + const expected: AuthProps['loginWith'] = { + externalProviders: { + callbackUrls, + logoutUrls, + domainPrefix: stableBackendHash, }, }; + + assert.deepStrictEqual(translated, expected); + }); + + void it('translates with custom domain prefix', () => { + const loginWith: AuthLoginWithFactoryProps = { + externalProviders: { + callbackUrls: callbackUrls, + logoutUrls: logoutUrls, + domainPrefix: domainPrefix, + }, + }; + + const translated = translateToAuthConstructLoginWith( + loginWith, + backendResolver, + stableBackendIdentifiers, + ); + + const expected: AuthProps['loginWith'] = { + externalProviders: { + callbackUrls, + logoutUrls, + domainPrefix: domainPrefix, + }, + }; + assert.deepStrictEqual(translated, expected); }); @@ -177,6 +242,7 @@ void describe('translateToAuthConstructLoginWith', () => { const translated = translateToAuthConstructLoginWith( loginWith, backendResolver, + stableBackendIdentifiers, ); const expected: AuthProps['loginWith'] = { diff --git a/packages/backend-auth/src/translate_auth_props.ts b/packages/backend-auth/src/translate_auth_props.ts index 88898cfadc0..47cbd4df3dc 100644 --- a/packages/backend-auth/src/translate_auth_props.ts +++ b/packages/backend-auth/src/translate_auth_props.ts @@ -9,6 +9,7 @@ import { import { BackendSecretResolver, ConstructFactoryGetInstanceProps, + StableBackendIdentifiers, } from '@aws-amplify/plugin-types'; import { AmazonProviderFactoryProps, @@ -26,10 +27,13 @@ import { AmplifyAuthProps } from './factory.js'; * Translate an Auth factory's loginWith to its Auth construct counterpart. Backend secret fields will be resolved * to an CFN token and map to the required construct type. Note that not all construct secret fields are of sdk * SecretValue type, many are (wrongly) of type string as well. + * + * The domain prefix will be used if specified, otherwise a default domain prefix will be generated. */ export const translateToAuthConstructLoginWith = ( authFactoryLoginWith: AuthLoginWithFactoryProps, backendSecretResolver: BackendSecretResolver, + stableBackendIdentifiers: StableBackendIdentifiers, ): AuthProps['loginWith'] => { const result: AuthProps['loginWith'] = authFactoryLoginWith as AuthProps['loginWith']; @@ -82,6 +86,14 @@ export const translateToAuthConstructLoginWith = ( result.externalProviders.google = googleProps; } + const domainPrefix = translateDomainPrefix( + stableBackendIdentifiers, + externalProviders.domainPrefix, + ); + if (domainPrefix) { + result.externalProviders.domainPrefix = domainPrefix; + } + return result; }; /** @@ -159,14 +171,14 @@ const translateAmazonProps = ( const translateAppleProps = ( backendSecretResolver: BackendSecretResolver, - amazonProviderProps?: AppleProviderFactoryProps, + appleProviderProps?: AppleProviderFactoryProps, ): AppleProviderProps | undefined => { - if (!amazonProviderProps) { + if (!appleProviderProps) { return undefined; } const { clientId, teamId, keyId, privateKey, ...noSecretProps } = - amazonProviderProps; + appleProviderProps; return { ...noSecretProps, clientId: backendSecretResolver.resolveSecret(clientId).unsafeUnwrap(), @@ -236,3 +248,13 @@ const translateGoogleProps = ( clientSecret: backendSecretResolver.resolveSecret(clientSecretValue), }; }; + +const translateDomainPrefix = ( + stableBackendIdentifiers: StableBackendIdentifiers, + domainPrefix?: string, +): string | undefined => { + if (!domainPrefix) { + return stableBackendIdentifiers.getStableBackendHash(); + } + return domainPrefix; +}; diff --git a/packages/backend-auth/src/types.ts b/packages/backend-auth/src/types.ts index 852f39a196e..35c85a98717 100644 --- a/packages/backend-auth/src/types.ts +++ b/packages/backend-auth/src/types.ts @@ -142,12 +142,7 @@ export type OidcProviderFactoryProps = Omit< */ export type ExternalProviderGeneralFactoryProps = Omit< ExternalProviderOptions, - | 'signInWithApple' - | 'loginWithAmazon' - | 'facebook' - | 'oidc' - | 'google' - | 'domainPrefix' + 'signInWithApple' | 'loginWithAmazon' | 'facebook' | 'oidc' | 'google' >; /** From 82c00c4ef71bddca94646a095428b0f6fb223a20 Mon Sep 17 00:00:00 2001 From: Meng Cao Date: Sun, 23 Nov 2025 22:11:55 -0500 Subject: [PATCH 2/2] chore: add changeset --- .changeset/stupid-times-knock.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/stupid-times-knock.md diff --git a/.changeset/stupid-times-knock.md b/.changeset/stupid-times-knock.md new file mode 100644 index 00000000000..395edcc30c5 --- /dev/null +++ b/.changeset/stupid-times-knock.md @@ -0,0 +1,5 @@ +--- +'@aws-amplify/backend-auth': patch +--- + +feat(auth): support custom domain prefix for cognito domain in user pool