Skip to content

Commit 9e7a58b

Browse files
chore: Add integration tests for changes related to FDL deprecation (#2749)
* Add integration tests for changes related to FDL deprecation --------- Co-authored-by: Liubin Jiang <[email protected]>
1 parent 4e4893a commit 9e7a58b

File tree

1 file changed

+239
-2
lines changed

1 file changed

+239
-2
lines changed

test/integration/auth.spec.ts

Lines changed: 239 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
AuthProviderConfig, CreateTenantRequest, DeleteUsersResult, PhoneMultiFactorInfo,
3333
TenantAwareAuth, UpdatePhoneMultiFactorInfoRequest, UpdateTenantRequest, UserImportOptions,
3434
UserImportRecord, UserRecord, getAuth, UpdateProjectConfigRequest, UserMetadata, MultiFactorConfig,
35-
PasswordPolicyConfig, SmsRegionConfig,
35+
PasswordPolicyConfig, SmsRegionConfig, ActionCodeSettings,
3636
} from '../../lib/auth/index';
3737
import * as sinon from 'sinon';
3838
import * as sinonChai from 'sinon-chai';
@@ -75,10 +75,25 @@ const mockUserData = {
7575
photoURL: 'http://www.example.com/' + newUserUid + '/photo.png',
7676
disabled: false,
7777
};
78-
const actionCodeSettings = {
78+
const actionCodeSettings : ActionCodeSettings = {
7979
url: 'http://localhost/?a=1&b=2#c=3',
8080
handleCodeInApp: false,
8181
};
82+
const actionCodeSettingsWithCustomDomain: ActionCodeSettings = {
83+
url: 'http://localhost/?a=1&b=2#c=3',
84+
handleCodeInApp: true,
85+
linkDomain: 'kobayashimaru.testdomaindonotuse.com',
86+
iOS: {
87+
bundleId: 'testBundleId',
88+
},
89+
}
90+
const actionCodeSettingsForFdlLinks: ActionCodeSettings = {
91+
url: 'http://localhost/?a=1&b=2#c=3',
92+
handleCodeInApp: true,
93+
iOS: {
94+
bundleId: 'testBundleId',
95+
},
96+
}
8297
let deleteQueue = Promise.resolve();
8398

8499
interface UserImportTest {
@@ -1105,6 +1120,13 @@ describe('admin.auth', () => {
11051120

11061121
// Create the test user before running this suite of tests.
11071122
before(() => {
1123+
// Update project config to have HOSTING_DOMAIN as mobileLinksConfig after each test
1124+
const updateMobileLinksRequest: UpdateProjectConfigRequest = {
1125+
mobileLinksConfig: {
1126+
domain: 'HOSTING_DOMAIN',
1127+
},
1128+
};
1129+
getAuth().projectConfigManager().updateProjectConfig(updateMobileLinksRequest);
11081130
return getAuth().createUser(userData);
11091131
});
11101132

@@ -1199,6 +1221,162 @@ describe('admin.auth', () => {
11991221
expect(result.user!.emailVerified).to.be.true;
12001222
});
12011223
});
1224+
1225+
it('generateSignInWithEmailLink() with custom linkDomain should return error in case of invalid hosting domain',
1226+
function () {
1227+
if (authEmulatorHost) {
1228+
return this.skip(); // Not yet supported in Auth Emulator.
1229+
}
1230+
const actionCodeSettingsWithInvalidLinkDomain = deepCopy(actionCodeSettings);
1231+
actionCodeSettingsWithInvalidLinkDomain.linkDomain = 'invaliddomain.firebaseapp.com';
1232+
return getAuth().generateSignInWithEmailLink(email, actionCodeSettingsWithInvalidLinkDomain)
1233+
.catch((error) => {
1234+
expect(error.code).to.equal('auth/invalid-hosting-link-domain');
1235+
});
1236+
});
1237+
1238+
it('generatePasswordResetLink() should return a password reset link with custom domain', function () {
1239+
if (authEmulatorHost) {
1240+
return this.skip(); // Not yet supported in Auth Emulator.
1241+
}
1242+
// Ensure old password set on created user.
1243+
return getAuth().updateUser(uid, { password: 'password' })
1244+
.then(() => {
1245+
return getAuth().generatePasswordResetLink(email, actionCodeSettingsWithCustomDomain);
1246+
})
1247+
.then((link) => {
1248+
const code = getActionCode(link);
1249+
expect(getContinueUrlForInAppRequest(link)).equal(actionCodeSettings.url);
1250+
expect(getHostName(link)).equal(actionCodeSettingsWithCustomDomain.linkDomain);
1251+
return clientAuth().confirmPasswordReset(code, newPassword);
1252+
})
1253+
.then(() => {
1254+
return clientAuth().signInWithEmailAndPassword(email, newPassword);
1255+
})
1256+
.then((result) => {
1257+
expect(result.user).to.exist;
1258+
expect(result.user!.email).to.equal(email);
1259+
// Password reset also verifies the user's email.
1260+
expect(result.user!.emailVerified).to.be.true;
1261+
});
1262+
});
1263+
1264+
it('generateEmailVerificationLink() should return a verification link with custom domain', function () {
1265+
if (authEmulatorHost) {
1266+
return this.skip(); // Not yet supported in Auth Emulator.
1267+
}
1268+
// Ensure the user's email is unverified.
1269+
return getAuth().updateUser(uid, { password: '123456', emailVerified: false })
1270+
.then((userRecord) => {
1271+
expect(userRecord.emailVerified).to.be.false;
1272+
return getAuth().generateEmailVerificationLink(email, actionCodeSettingsWithCustomDomain);
1273+
})
1274+
.then((link) => {
1275+
const code = getActionCode(link);
1276+
expect(getContinueUrlForInAppRequest(link)).equal(actionCodeSettings.url);
1277+
expect(getHostName(link)).equal(actionCodeSettingsWithCustomDomain.linkDomain);
1278+
return clientAuth().applyActionCode(code);
1279+
})
1280+
.then(() => {
1281+
return clientAuth().signInWithEmailAndPassword(email, userData.password);
1282+
})
1283+
.then((result) => {
1284+
expect(result.user).to.exist;
1285+
expect(result.user!.email).to.equal(email);
1286+
expect(result.user!.emailVerified).to.be.true;
1287+
});
1288+
});
1289+
1290+
it('generateSignInWithEmailLink() should return a sign-in link with custom domain', function () {
1291+
if (authEmulatorHost) {
1292+
return this.skip(); // Not yet supported in Auth Emulator.
1293+
}
1294+
return getAuth().generateSignInWithEmailLink(email, actionCodeSettingsWithCustomDomain)
1295+
.then((link) => {
1296+
expect(getContinueUrlForInAppRequest(link)).equal(actionCodeSettingsWithCustomDomain.url);
1297+
expect(getHostName(link)).equal(actionCodeSettingsWithCustomDomain.linkDomain);
1298+
return clientAuth().signInWithEmailLink(email, link);
1299+
})
1300+
.then((result) => {
1301+
expect(result.user).to.exist;
1302+
expect(result.user!.email).to.equal(email);
1303+
expect(result.user!.emailVerified).to.be.true;
1304+
});
1305+
});
1306+
1307+
it('generateVerifyAndChangeEmailLink() should return a verification link with custom domain', function () {
1308+
if (authEmulatorHost) {
1309+
return this.skip(); // Not yet supported in Auth Emulator.
1310+
}
1311+
// Ensure the user's email is verified.
1312+
return getAuth().updateUser(uid, { password: '123456', emailVerified: true })
1313+
.then((userRecord) => {
1314+
expect(userRecord.emailVerified).to.be.true;
1315+
return getAuth().generateVerifyAndChangeEmailLink(email, newEmail, actionCodeSettingsWithCustomDomain);
1316+
})
1317+
.then((link) => {
1318+
const code = getActionCode(link);
1319+
expect(getContinueUrlForInAppRequest(link)).equal(actionCodeSettings.url);
1320+
expect(getHostName(link)).equal(actionCodeSettingsWithCustomDomain.linkDomain);
1321+
return clientAuth().applyActionCode(code);
1322+
})
1323+
.then(() => {
1324+
return clientAuth().signInWithEmailAndPassword(newEmail, 'password');
1325+
})
1326+
.then((result) => {
1327+
expect(result.user).to.exist;
1328+
expect(result.user!.email).to.equal(newEmail);
1329+
expect(result.user!.emailVerified).to.be.true;
1330+
});
1331+
});
1332+
1333+
it('generateSignInWithEmailLink() should return a FDL sign-in'
1334+
+ 'link with mobileLinksConfig set to FIREBASE_DYNAMIC_LINK_DOMAIN', function () {
1335+
if (authEmulatorHost) {
1336+
return this.skip(); // Not yet supported in Auth Emulator.
1337+
}
1338+
1339+
const updateMobileLinksRequest: UpdateProjectConfigRequest = {
1340+
mobileLinksConfig: {
1341+
domain: 'FIREBASE_DYNAMIC_LINK_DOMAIN',
1342+
}
1343+
};
1344+
return getAuth().projectConfigManager().updateProjectConfig(updateMobileLinksRequest)
1345+
.then((projectConfig) => {
1346+
expect(projectConfig?.mobileLinksConfig?.domain).equal('FIREBASE_DYNAMIC_LINK_DOMAIN');
1347+
return getAuth().generateSignInWithEmailLink(email, actionCodeSettingsForFdlLinks);
1348+
}).then((link) => {
1349+
expectFDLLink(link);
1350+
return clientAuth().signInWithEmailLink(email, link);
1351+
}).then((result) => {
1352+
expect(result.user).to.exist;
1353+
expect(result.user!.email).to.equal(email);
1354+
expect(result.user!.emailVerified).to.be.true;
1355+
});
1356+
});
1357+
1358+
it('generateSignInWithEmailLink() should return a FDL sign-in link with empty mobileLinksConfig', function () {
1359+
if (authEmulatorHost) {
1360+
return this.skip(); // Not yet supported in Auth Emulator.
1361+
}
1362+
1363+
const updateMobileLinksRequest: UpdateProjectConfigRequest = {
1364+
mobileLinksConfig: {
1365+
}
1366+
};
1367+
return getAuth().projectConfigManager().updateProjectConfig(updateMobileLinksRequest)
1368+
.then((projectConfig) => {
1369+
expect(projectConfig.mobileLinksConfig).is.empty;
1370+
return getAuth().generateSignInWithEmailLink(email, actionCodeSettingsForFdlLinks);
1371+
}).then((link) => {
1372+
expectFDLLink(link);
1373+
return clientAuth().signInWithEmailLink(email, link);
1374+
}).then((result) => {
1375+
expect(result.user).to.exist;
1376+
expect(result.user!.email).to.equal(email);
1377+
expect(result.user!.emailVerified).to.be.true;
1378+
});
1379+
});
12021380
});
12031381

12041382
describe('Project config management operations', () => {
@@ -1285,6 +1463,9 @@ describe('admin.auth', () => {
12851463
},
12861464
emailPrivacyConfig: {
12871465
enableImprovedEmailPrivacy: true,
1466+
},
1467+
mobileLinksConfig: {
1468+
domain: 'HOSTING_DOMAIN',
12881469
}
12891470
};
12901471
const projectConfigOption2: UpdateProjectConfigRequest = {
@@ -1318,6 +1499,9 @@ describe('admin.auth', () => {
13181499
emailPrivacyConfig: {
13191500
enableImprovedEmailPrivacy: true,
13201501
},
1502+
mobileLinksConfig: {
1503+
domain: 'HOSTING_DOMAIN',
1504+
},
13211505
};
13221506
const expectedProjectConfig2: any = {
13231507
smsRegionConfig: smsRegionAllowlistOnlyConfig,
@@ -1333,6 +1517,9 @@ describe('admin.auth', () => {
13331517
],
13341518
},
13351519
emailPrivacyConfig: {},
1520+
mobileLinksConfig: {
1521+
domain: 'HOSTING_DOMAIN',
1522+
},
13361523
};
13371524
const expectedProjectConfigSmsEnabledTotpDisabled: any = {
13381525
smsRegionConfig: smsRegionAllowlistOnlyConfig,
@@ -1348,6 +1535,9 @@ describe('admin.auth', () => {
13481535
],
13491536
},
13501537
emailPrivacyConfig: {},
1538+
mobileLinksConfig: {
1539+
domain: 'HOSTING_DOMAIN',
1540+
},
13511541
};
13521542

13531543
it('updateProjectConfig() should resolve with the updated project config', () => {
@@ -3194,6 +3384,53 @@ function getContinueUrl(link: string): string {
31943384
return continueUrl!;
31953385
}
31963386

3387+
/**
3388+
* Returns the host name corresponding to the link.
3389+
*
3390+
* @param link The link to parse for hostname
3391+
* @returns Hostname in the link
3392+
*/
3393+
function getHostName(link: string): string {
3394+
const parsedUrl = new url.URL(link);
3395+
return parsedUrl.hostname;
3396+
}
3397+
3398+
/**
3399+
* Returns continue URL for handling in app requests.
3400+
* URL will be of the form, http://abc/__/auth/link?link=<action link url>
3401+
* Coninue URL will be part of action link url
3402+
*
3403+
* @param link The link to parse for continue url
3404+
* @returns
3405+
*/
3406+
function getContinueUrlForInAppRequest(link: string): string {
3407+
// Extract action url from link param
3408+
const parsedUrl = new url.URL(link);
3409+
const linkParam = parsedUrl.searchParams.get('link') ?? '';
3410+
expect(linkParam).is.not.empty;
3411+
3412+
// Extract continueUrl param from action url
3413+
const actionUrl = new url.URL(linkParam);
3414+
const continueUrl = actionUrl.searchParams.get('continueUrl');
3415+
expect(continueUrl).to.exist;
3416+
return continueUrl!;
3417+
}
3418+
3419+
/**
3420+
* Verify if the generated link is generated by FDL
3421+
* We leverage the params created by FDL to test whether a given link is FDL
3422+
*
3423+
* @param link Link to check whether it is FDL
3424+
*/
3425+
function expectFDLLink(link: string): void {
3426+
const parsedUrl = new url.URL(link);
3427+
// For ios, FDL creates a fallback url with param ifl
3428+
// We leverage that to test whether a given link is FDL link
3429+
// Note: This param does not exist when the link is generated for HOSTING_DOMAIN
3430+
const iflParam = parsedUrl.searchParams.get('ifl');
3431+
expect(iflParam).is.not.null;
3432+
}
3433+
31973434
/**
31983435
* Returns the tenant ID corresponding to the link.
31993436
*

0 commit comments

Comments
 (0)