veritatisdoloremque
Version:
Monorepo for the Firebase JavaScript SDK
279 lines (251 loc) • 8.53 kB
text/typescript
/**
* @license
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { expect, use } from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import * as sinonChai from 'sinon-chai';
import { ActionCodeOperation, OperationType } from '../../model/public_types';
import { FirebaseError } from '@firebase/util';
import { mockEndpoint } from '../../../test/helpers/api/helper';
import { testAuth, TestAuth } from '../../../test/helpers/mock_auth';
import * as mockFetch from '../../../test/helpers/mock_fetch';
import { Endpoint } from '../../api';
import { APIUserInfo } from '../../api/account_management/account';
import { ServerError } from '../../api/errors';
import { UserCredentialInternal } from '../../model/user';
import {
isSignInWithEmailLink,
sendSignInLinkToEmail,
signInWithEmailLink
} from './email_link';
use(chaiAsPromised);
use(sinonChai);
describe('core/strategies/sendSignInLinkToEmail', () => {
const email = 'foo@bar.com';
let auth: TestAuth;
beforeEach(async () => {
auth = await testAuth();
mockFetch.setUp();
});
afterEach(mockFetch.tearDown);
it('should send a sign in link via email', async () => {
const mock = mockEndpoint(Endpoint.SEND_OOB_CODE, {
email
});
await sendSignInLinkToEmail(auth, email, {
handleCodeInApp: true,
url: 'continue-url'
});
expect(mock.calls[0].request).to.eql({
requestType: ActionCodeOperation.EMAIL_SIGNIN,
email,
canHandleCodeInApp: true,
continueUrl: 'continue-url'
});
});
it('should require handleCodeInApp to be true', async () => {
await expect(
sendSignInLinkToEmail(auth, email, {
handleCodeInApp: false,
url: 'continue-url'
})
).to.be.rejectedWith(FirebaseError, 'auth/argument-error).');
});
it('should surface errors', async () => {
const mock = mockEndpoint(
Endpoint.SEND_OOB_CODE,
{
error: {
code: 400,
message: ServerError.INVALID_EMAIL
}
},
400
);
await expect(
sendSignInLinkToEmail(auth, email, {
handleCodeInApp: true,
url: 'continue-url'
})
).to.be.rejectedWith(
FirebaseError,
'Firebase: The email address is badly formatted. (auth/invalid-email).'
);
expect(mock.calls.length).to.eq(1);
});
context('on iOS', () => {
it('should pass action code parameters', async () => {
const mock = mockEndpoint(Endpoint.SEND_OOB_CODE, {
email
});
await sendSignInLinkToEmail(auth, email, {
handleCodeInApp: true,
iOS: {
bundleId: 'my-bundle'
},
url: 'my-url',
dynamicLinkDomain: 'fdl-domain'
});
expect(mock.calls[0].request).to.eql({
requestType: ActionCodeOperation.EMAIL_SIGNIN,
email,
continueUrl: 'my-url',
dynamicLinkDomain: 'fdl-domain',
canHandleCodeInApp: true,
iosBundleId: 'my-bundle'
});
});
});
context('on Android', () => {
it('should pass action code parameters', async () => {
const mock = mockEndpoint(Endpoint.SEND_OOB_CODE, {
email
});
await sendSignInLinkToEmail(auth, email, {
handleCodeInApp: true,
android: {
installApp: false,
minimumVersion: 'my-version',
packageName: 'my-package'
},
url: 'my-url',
dynamicLinkDomain: 'fdl-domain'
});
expect(mock.calls[0].request).to.eql({
requestType: ActionCodeOperation.EMAIL_SIGNIN,
email,
continueUrl: 'my-url',
dynamicLinkDomain: 'fdl-domain',
canHandleCodeInApp: true,
androidInstallApp: false,
androidMinimumVersionCode: 'my-version',
androidPackageName: 'my-package'
});
});
});
});
describe('core/strategies/isSignInWithEmailLink', () => {
let auth: TestAuth;
beforeEach(async () => {
auth = await testAuth();
});
context('simple links', () => {
it('should recognize sign in links', () => {
const link =
'https://www.example.com/action?mode=signIn&oobCode=oobCode&apiKey=API_KEY';
expect(isSignInWithEmailLink(auth, link)).to.be.true;
});
it('should not recognize other email links', () => {
const link =
'https://www.example.com/action?mode=verifyEmail&oobCode=oobCode&apiKey=API_KEY';
expect(isSignInWithEmailLink(auth, link)).to.be.false;
});
it('should not recognize invalid links', () => {
const link = 'https://www.example.com/action?mode=signIn';
expect(isSignInWithEmailLink(auth, link)).to.be.false;
});
});
context('deep links', () => {
it('should recognize valid links', () => {
const deepLink =
'https://www.example.com/action?mode=signIn&oobCode=oobCode&apiKey=API_KEY';
const link = `https://example.app.goo.gl/?link=${encodeURIComponent(
deepLink
)}`;
expect(isSignInWithEmailLink(auth, link)).to.be.true;
});
it('should recognize valid links with deep_link_id', () => {
const deepLink =
'https://www.example.com/action?mode=signIn&oobCode=oobCode&apiKey=API_KEY';
const link = `somexampleiosurl://google/link?deep_link_id=${encodeURIComponent(
deepLink
)}`;
expect(isSignInWithEmailLink(auth, link)).to.be.true;
});
it('should reject other email links', () => {
const deepLink =
'https://www.example.com/action?mode=verifyEmail&oobCode=oobCode&apiKey=API_KEY';
const link = `https://example.app.goo.gl/?link=${encodeURIComponent(
deepLink
)}`;
expect(isSignInWithEmailLink(auth, link)).to.be.false;
});
it('should reject invalid links', () => {
const deepLink = 'https://www.example.com/action?mode=signIn';
const link = `https://example.app.goo.gl/?link=${encodeURIComponent(
deepLink
)}`;
expect(isSignInWithEmailLink(auth, link)).to.be.false;
});
});
});
describe('core/strategies/email_and_password/signInWithEmailLink', () => {
let auth: TestAuth;
const serverUser: APIUserInfo = {
localId: 'local-id'
};
beforeEach(async () => {
auth = await testAuth();
mockFetch.setUp();
mockEndpoint(Endpoint.SIGN_IN_WITH_EMAIL_LINK, {
idToken: 'id-token',
refreshToken: 'refresh-token',
expiresIn: '1234',
localId: serverUser.localId!
});
mockEndpoint(Endpoint.GET_ACCOUNT_INFO, {
users: [serverUser]
});
});
afterEach(mockFetch.tearDown);
it('should sign in the user', async () => {
const continueUrl = 'https://www.example.com/path/to/file?a=1&b=2#c=3';
const actionLink =
'https://www.example.com/finishSignIn?' +
'oobCode=CODE&mode=signIn&apiKey=API_KEY&' +
'continueUrl=' +
encodeURIComponent(continueUrl) +
'&languageCode=en&state=bla';
const { _tokenResponse, user, operationType } = (await signInWithEmailLink(
auth,
'some-email',
actionLink
)) as UserCredentialInternal;
expect(_tokenResponse).to.eql({
idToken: 'id-token',
refreshToken: 'refresh-token',
expiresIn: '1234',
localId: serverUser.localId!
});
expect(operationType).to.eq(OperationType.SIGN_IN);
expect(user.uid).to.eq(serverUser.localId);
expect(user.isAnonymous).to.be.false;
});
context('mismatched tenant ID', () => {
it('should throw an error', async () => {
const continueUrl = 'https://www.example.com/path/to/file?a=1&b=2#c=3';
const actionLink =
'https://www.example.com/finishSignIn?' +
'oobCode=CODE&mode=signIn&apiKey=API_KEY&' +
'continueUrl=' +
encodeURIComponent(continueUrl) +
'&languageCode=en&tenantId=OTHER_TENANT_ID&state=bla';
await expect(
signInWithEmailLink(auth, 'some-email', actionLink)
).to.be.rejectedWith(FirebaseError, 'auth/tenant-id-mismatch');
});
});
});