Skip to content

Commit 51e8a84

Browse files
update actionCodeSetting with linkDomain field (#2676)
1 parent 745c22e commit 51e8a84

File tree

7 files changed

+62
-3
lines changed

7 files changed

+62
-3
lines changed

etc/firebase-admin.auth.api.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ export interface ActionCodeSettings {
1515
installApp?: boolean;
1616
minimumVersion?: string;
1717
};
18+
// @deprecated
1819
dynamicLinkDomain?: string;
1920
handleCodeInApp?: boolean;
2021
iOS?: {
2122
bundleId: string;
2223
};
24+
linkDomain?: string;
2325
url: string;
2426
}
2527

@@ -223,6 +225,11 @@ export class AuthClientErrorCode {
223225
message: string;
224226
};
225227
// (undocumented)
228+
static INVALID_HOSTING_LINK_DOMAIN: {
229+
code: string;
230+
message: string;
231+
};
232+
// (undocumented)
226233
static INVALID_ID_TOKEN: {
227234
code: string;
228235
message: string;

src/auth/action-code-settings-builder.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,27 @@ export interface ActionCodeSettings {
9494
* configured per project. This field provides the ability to explicitly choose
9595
* configured per project. This fields provides the ability explicitly choose
9696
* one. If none is provided, the oldest domain is used by default.
97+
* @deprecated use linkDomain instead
9798
*/
9899
dynamicLinkDomain?: string;
100+
101+
/**
102+
* Defines the custom Firebase Hosting domain to use when the link is to be opened
103+
* via a specified mobile app,
104+
* This is a replacement of Firebase Dynamic Link.
105+
* If none is provided,
106+
* a default hosting domain will be used (for example, `example.firebaseapp.com`)
107+
*/
108+
109+
linkDomain?: string;
99110
}
100111

101112
/** Defines the email action code server request. */
102113
interface EmailActionCodeRequest {
103114
continueUrl?: string;
104115
canHandleCodeInApp?: boolean;
105116
dynamicLinkDomain?: string;
117+
linkDomain?: string;
106118
androidPackageName?: string;
107119
androidMinimumVersion: string;
108120
androidInstallApp?: boolean;
@@ -123,6 +135,7 @@ export class ActionCodeSettingsBuilder {
123135
private ibi?: string;
124136
private canHandleCodeInApp?: boolean;
125137
private dynamicLinkDomain?: string;
138+
private linkDomain?: string;
126139

127140
/**
128141
* ActionCodeSettingsBuilder constructor.
@@ -166,6 +179,14 @@ export class ActionCodeSettingsBuilder {
166179
}
167180
this.dynamicLinkDomain = actionCodeSettings.dynamicLinkDomain;
168181

182+
if (typeof actionCodeSettings.linkDomain !== 'undefined' &&
183+
!validator.isNonEmptyString(actionCodeSettings.linkDomain)) {
184+
throw new FirebaseAuthError(
185+
AuthClientErrorCode.INVALID_HOSTING_LINK_DOMAIN,
186+
);
187+
}
188+
this.linkDomain = actionCodeSettings.linkDomain;
189+
169190
if (typeof actionCodeSettings.iOS !== 'undefined') {
170191
if (!validator.isNonNullObject(actionCodeSettings.iOS)) {
171192
throw new FirebaseAuthError(
@@ -230,6 +251,7 @@ export class ActionCodeSettingsBuilder {
230251
continueUrl: this.continueUrl,
231252
canHandleCodeInApp: this.canHandleCodeInApp,
232253
dynamicLinkDomain: this.dynamicLinkDomain,
254+
linkDomain: this.linkDomain,
233255
androidPackageName: this.apn,
234256
androidMinimumVersion: this.amv,
235257
androidInstallApp: this.installApp,

src/auth/base-auth.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ export abstract class BaseAuth {
749749
* minimumVersion: '12'
750750
* },
751751
* handleCodeInApp: true,
752-
* dynamicLinkDomain: 'custom.page.link'
752+
* linkDomain: 'project-id.firebaseapp.com'
753753
* };
754754
* admin.auth()
755755
* .generatePasswordResetLink('[email protected]', actionCodeSettings)
@@ -802,7 +802,7 @@ export abstract class BaseAuth {
802802
* minimumVersion: '12'
803803
* },
804804
* handleCodeInApp: true,
805-
* dynamicLinkDomain: 'custom.page.link'
805+
* linkDomain: 'project-id.firebaseapp.com'
806806
* };
807807
* admin.auth()
808808
* .generateEmailVerificationLink('[email protected]', actionCodeSettings)
@@ -883,7 +883,7 @@ export abstract class BaseAuth {
883883
* minimumVersion: '12'
884884
* },
885885
* handleCodeInApp: true,
886-
* dynamicLinkDomain: 'custom.page.link'
886+
* linkDomain: 'project-id.firebaseapp.com'
887887
* };
888888
* admin.auth()
889889
* .generateEmailVerificationLink('[email protected]', actionCodeSettings)

src/utils/error.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,11 @@ export class AuthClientErrorCode {
471471
message: 'The provided dynamic link domain is not configured or authorized ' +
472472
'for the current project.',
473473
};
474+
public static INVALID_HOSTING_LINK_DOMAIN = {
475+
code: 'invalid-hosting-link-domain',
476+
message: 'The provided hosting link domain is not configured in Firebase ' +
477+
'Hosting or is not owned by the current project.',
478+
};
474479
public static INVALID_EMAIL_VERIFIED = {
475480
code: 'invalid-email-verified',
476481
message: 'The emailVerified field must be a boolean.',
@@ -933,6 +938,8 @@ const AUTH_SERVER_TO_CLIENT_CODE: ServerToClientCode = {
933938
INVALID_CONTINUE_URI: 'INVALID_CONTINUE_URI',
934939
// Dynamic link domain in provided ActionCodeSettings is not authorized.
935940
INVALID_DYNAMIC_LINK_DOMAIN: 'INVALID_DYNAMIC_LINK_DOMAIN',
941+
// Hosting link domain in provided ActionCodeSettings is not owned by the current project.
942+
INVALID_HOSTING_LINK_DOMAIN: 'INVALID_HOSTING_LINK_DOMAIN',
936943
// uploadAccount provides an email that already exists.
937944
DUPLICATE_EMAIL: 'EMAIL_ALREADY_EXISTS',
938945
// uploadAccount provides a localId that already exists.

test/unit/auth/action-code-settings-builder.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ chai.use(sinonChai);
2828
chai.use(chaiAsPromised);
2929

3030
const expect = chai.expect;
31+
const TEST_LINK_DOMAIN = 'project-id.firebaseapp.com';
3132

3233
describe('ActionCodeSettingsBuilder', () => {
3334
describe('constructor', () => {
@@ -43,7 +44,10 @@ describe('ActionCodeSettingsBuilder', () => {
4344
installApp: true,
4445
minimumVersion: '6',
4546
},
47+
// not removing this test since we are going to accept both dynamicLinkDomain
48+
// and linkDomain for the onboarding phase.
4649
dynamicLinkDomain: 'custom.page.link',
50+
linkDomain: TEST_LINK_DOMAIN,
4751
})).not.to.throw;
4852
});
4953

@@ -69,6 +73,7 @@ describe('ActionCodeSettingsBuilder', () => {
6973
minimumVersion: '6',
7074
},
7175
dynamicLinkDomain: 'custom.page.link',
76+
linkDomain: TEST_LINK_DOMAIN,
7277
} as any);
7378
}).to.throw(AuthClientErrorCode.MISSING_CONTINUE_URI.message);
7479
});
@@ -109,6 +114,20 @@ describe('ActionCodeSettingsBuilder', () => {
109114
});
110115
});
111116

117+
const invalidHostingDomains = [null, NaN, 0, 1, true, false, '',
118+
[TEST_LINK_DOMAIN], [], {}, { a: 1 }, _.noop];
119+
invalidHostingDomains.forEach((domain) => {
120+
it('should throw on invalid linkDomain:' + JSON.stringify(domain), () => {
121+
expect(() => {
122+
return new ActionCodeSettingsBuilder({
123+
url: 'https://www.example.com/path/file?a=1&b=2',
124+
handleCodeInApp: true,
125+
linkDomain: domain,
126+
} as any);
127+
}).to.throw(AuthClientErrorCode.INVALID_HOSTING_LINK_DOMAIN.message);
128+
});
129+
});
130+
112131
const invalidIOSSettings = [null, NaN, 0, 1, true, false, '', 'a', [], [1, 'a'], _.noop];
113132
invalidIOSSettings.forEach((settings) => {
114133
it('should throw on invalid iOS object:' + JSON.stringify(settings), () => {
@@ -228,11 +247,13 @@ describe('ActionCodeSettingsBuilder', () => {
228247
minimumVersion: '6',
229248
},
230249
dynamicLinkDomain: 'custom.page.link',
250+
linkDomain: TEST_LINK_DOMAIN,
231251
});
232252
const expectedRequest = {
233253
continueUrl: 'https://www.example.com/path/file?a=1&b=2',
234254
canHandleCodeInApp: true,
235255
dynamicLinkDomain: 'custom.page.link',
256+
linkDomain: TEST_LINK_DOMAIN,
236257
androidPackageName: 'com.example.android',
237258
androidMinimumVersion: '6',
238259
androidInstallApp: true,

test/unit/auth/auth-api-request.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3078,6 +3078,7 @@ AUTH_REQUEST_HANDLER_TESTS.forEach((handler) => {
30783078
minimumVersion: '6',
30793079
},
30803080
dynamicLinkDomain: 'custom.page.link',
3081+
linkDomain: 'project-id.firebaseapp.com',
30813082
};
30823083
const expectedActionCodeSettingsRequest = new ActionCodeSettingsBuilder(actionCodeSettings).buildRequest();
30833084
const expectedLink = 'https://custom.page.link?link=' +

test/unit/auth/auth.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2887,6 +2887,7 @@ AUTH_CONFIGS.forEach((testConfig) => {
28872887
minimumVersion: '6',
28882888
},
28892889
dynamicLinkDomain: 'custom.page.link',
2890+
linkDomain: 'project-id.firebaseapp.com',
28902891
};
28912892
const expectedLink = 'https://custom.page.link?link=' +
28922893
encodeURIComponent('https://projectId.firebaseapp.com/__/auth/action?oobCode=CODE') +

0 commit comments

Comments
 (0)