Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add beforeEmailSent blocking function #1462

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
130 changes: 119 additions & 11 deletions spec/common/providers/identity.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import * as identity from "../../../src/common/providers/identity";

const EVENT = "EVENT_TYPE";
const now = new Date();
const TEST_NAME = "John Doe";
const ALLOW = "ALLOW";
const BLOCK = "BLOCK";

describe("identity", () => {
describe("userRecordConstructor", () => {
Expand Down Expand Up @@ -232,14 +235,14 @@ describe("identity", () => {
describe("parseProviderData", () => {
const decodedUserInfo = {
provider_id: "google.com",
display_name: "John Doe",
display_name: TEST_NAME,
photo_url: "https://lh3.googleusercontent.com/1234567890/photo.jpg",
uid: "1234567890",
email: "[email protected]",
};
const userInfo = {
providerId: "google.com",
displayName: "John Doe",
displayName: TEST_NAME,
photoURL: "https://lh3.googleusercontent.com/1234567890/photo.jpg",
uid: "1234567890",
email: "[email protected]",
Expand Down Expand Up @@ -340,12 +343,12 @@ describe("identity", () => {
uid: "abcdefghijklmnopqrstuvwxyz",
email: "[email protected]",
email_verified: true,
display_name: "John Doe",
display_name: TEST_NAME,
phone_number: "+11234567890",
provider_data: [
{
provider_id: "google.com",
display_name: "John Doe",
display_name: TEST_NAME,
photo_url: "https://lh3.googleusercontent.com/1234567890/photo.jpg",
email: "[email protected]",
uid: "1234567890",
Expand All @@ -366,7 +369,7 @@ describe("identity", () => {
provider_id: "password",
email: "[email protected]",
uid: "[email protected]",
display_name: "John Doe",
display_name: TEST_NAME,
},
],
password_hash: "passwordHash",
Expand Down Expand Up @@ -407,11 +410,11 @@ describe("identity", () => {
phoneNumber: "+11234567890",
emailVerified: true,
disabled: false,
displayName: "John Doe",
displayName: TEST_NAME,
providerData: [
{
providerId: "google.com",
displayName: "John Doe",
displayName: TEST_NAME,
photoURL: "https://lh3.googleusercontent.com/1234567890/photo.jpg",
email: "[email protected]",
uid: "1234567890",
Expand All @@ -435,7 +438,7 @@ describe("identity", () => {
},
{
providerId: "password",
displayName: "John Doe",
displayName: TEST_NAME,
photoURL: undefined,
email: "[email protected]",
uid: "[email protected]",
Expand Down Expand Up @@ -489,8 +492,9 @@ describe("identity", () => {
});

describe("parseAuthEventContext", () => {
const TEST_RECAPTCHA_SCORE = 0.9;
const rawUserInfo = {
name: "John Doe",
name: TEST_NAME,
granted_scopes:
"openid https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile",
id: "123456789",
Expand All @@ -516,13 +520,15 @@ describe("identity", () => {
user_agent: "USER_AGENT",
locale: "en",
raw_user_info: JSON.stringify(rawUserInfo),
recaptcha_score: TEST_RECAPTCHA_SCORE,
};
const context = {
locale: "en",
ipAddress: "1.2.3.4",
userAgent: "USER_AGENT",
eventId: "EVENT_ID",
eventType: EVENT,
emailType: undefined,
authType: "UNAUTHENTICATED",
resource: {
service: "identitytoolkit.googleapis.com",
Expand All @@ -534,6 +540,8 @@ describe("identity", () => {
profile: rawUserInfo,
username: undefined,
isNewUser: false,
recaptchaScore: TEST_RECAPTCHA_SCORE,
email: undefined,
},
credential: null,
params: {},
Expand Down Expand Up @@ -563,13 +571,15 @@ describe("identity", () => {
oauth_refresh_token: "REFRESH_TOKEN",
oauth_token_secret: "OAUTH_TOKEN_SECRET",
oauth_expires_in: 3600,
recaptcha_score: TEST_RECAPTCHA_SCORE,
};
const context = {
locale: "en",
ipAddress: "1.2.3.4",
userAgent: "USER_AGENT",
eventId: "EVENT_ID",
eventType: "providers/cloud.auth/eventTypes/user.beforeSignIn:password",
emailType: undefined,
authType: "UNAUTHENTICATED",
resource: {
service: "identitytoolkit.googleapis.com",
Expand All @@ -581,6 +591,8 @@ describe("identity", () => {
profile: rawUserInfo,
username: undefined,
isNewUser: false,
recaptchaScore: TEST_RECAPTCHA_SCORE,
email: undefined,
},
credential: {
claims: undefined,
Expand Down Expand Up @@ -619,14 +631,14 @@ describe("identity", () => {
uid: "abcdefghijklmnopqrstuvwxyz",
email: "[email protected]",
email_verified: true,
display_name: "John Doe",
display_name: TEST_NAME,
phone_number: "+11234567890",
provider_data: [
{
provider_id: "oidc.provider",
email: "[email protected]",
uid: "[email protected]",
display_name: "John Doe",
display_name: TEST_NAME,
},
],
photo_url: "https://lh3.googleusercontent.com/1234567890/photo.jpg",
Expand All @@ -647,13 +659,15 @@ describe("identity", () => {
oauth_token_secret: "OAUTH_TOKEN_SECRET",
oauth_expires_in: 3600,
raw_user_info: JSON.stringify(rawUserInfo),
recaptcha_score: TEST_RECAPTCHA_SCORE,
};
const context = {
locale: "en",
ipAddress: "1.2.3.4",
userAgent: "USER_AGENT",
eventId: "EVENT_ID",
eventType: "providers/cloud.auth/eventTypes/user.beforeCreate:oidc.provider",
emailType: undefined,
authType: "USER",
resource: {
service: "identitytoolkit.googleapis.com",
Expand All @@ -665,6 +679,8 @@ describe("identity", () => {
providerId: "oidc.provider",
profile: rawUserInfo,
isNewUser: true,
recaptchaScore: TEST_RECAPTCHA_SCORE,
email: undefined,
},
credential: {
claims: undefined,
Expand All @@ -681,6 +697,50 @@ describe("identity", () => {

expect(identity.parseAuthEventContext(decodedJwt, "project-id", time)).to.deep.equal(context);
});

it("should parse a beforeSendEmail event", () => {
const time = now.getTime();
const decodedJwt = {
iss: "https://securetoken.google.com/project_id",
aud: "https://us-east1-project_id.cloudfunctions.net/function-1",
iat: 1,
exp: 60 * 60 + 1,
event_id: "EVENT_ID",
event_type: "beforeSendEmail",
user_agent: "USER_AGENT",
ip_address: "1.2.3.4",
locale: "en",
recaptcha_score: TEST_RECAPTCHA_SCORE,
email_type: "RESET_PASSWORD",
email: "[email protected]",
};
const context = {
locale: "en",
ipAddress: "1.2.3.4",
userAgent: "USER_AGENT",
eventId: "EVENT_ID",
eventType: "providers/cloud.auth/eventTypes/user.beforeSendEmail",
emailType: "RESET_PASSWORD",
authType: "UNAUTHENTICATED",
resource: {
service: "identitytoolkit.googleapis.com",
name: "projects/project-id",
},
timestamp: new Date(1000).toUTCString(),
additionalUserInfo: {
isNewUser: false,
profile: undefined,
providerId: undefined,
username: undefined,
recaptchaScore: TEST_RECAPTCHA_SCORE,
email: "[email protected]",
},
credential: null,
params: {},
};

expect(identity.parseAuthEventContext(decodedJwt, "project-id", time)).to.deep.equal(context);
});
});

describe("validateAuthResponse", () => {
Expand Down Expand Up @@ -762,4 +822,52 @@ describe("identity", () => {
);
});
});

describe("generateResponsePayload", () => {
const DISPLAY_NAME_FIELD = "displayName";
const TEST_RESPONSE = {
displayName: TEST_NAME,
recaptchaActionOverride: BLOCK,
} as identity.BeforeCreateResponse;

const EXPECT_PAYLOAD = {
userRecord: { displayName: TEST_NAME, updateMask: DISPLAY_NAME_FIELD },
recaptchaActionOverride: BLOCK,
};

const TEST_RESPONSE_RECAPTCHA_ALLOW = {
recaptchaActionOverride: ALLOW,
} as identity.BeforeCreateResponse;

const EXPECT_PAYLOAD_RECAPTCHA_ALLOW = {
recaptchaActionOverride: ALLOW,
};

const TEST_RESPONSE_RECAPTCHA_UNDEFINED = {
displayName: TEST_NAME,
} as identity.BeforeSignInResponse;

const EXPECT_PAYLOAD_UNDEFINED = {
userRecord: { displayName: TEST_NAME, updateMask: DISPLAY_NAME_FIELD },
};
it("should return empty object on undefined response", () => {
expect(identity.generateResponsePayload()).to.eql({});
});

it("should exclude recaptchaActionOverride field from updateMask", () => {
expect(identity.generateResponsePayload(TEST_RESPONSE)).to.deep.equal(EXPECT_PAYLOAD);
});

it("should return recaptchaActionOverride when it is true on response", () => {
expect(identity.generateResponsePayload(TEST_RESPONSE_RECAPTCHA_ALLOW)).to.deep.equal(
EXPECT_PAYLOAD_RECAPTCHA_ALLOW
);
});

it("should not return recaptchaActionOverride if undefined", () => {
const payload = identity.generateResponsePayload(TEST_RESPONSE_RECAPTCHA_UNDEFINED);
expect(payload.hasOwnProperty("recaptchaActionOverride")).to.be.false;
expect(payload).to.deep.equal(EXPECT_PAYLOAD_UNDEFINED);
});
});
});
90 changes: 90 additions & 0 deletions spec/v1/providers/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,96 @@ describe("Auth Functions", () => {
});
});

describe("beforeEmail", () => {
it("should create function without options", () => {
const fn = auth.user().beforeEmail(() => Promise.resolve());

expect(fn.__trigger).to.deep.equal({
labels: {},
blockingTrigger: {
eventType: "providers/cloud.auth/eventTypes/user.beforeSendEmail",
options: {
accessToken: false,
idToken: false,
refreshToken: false,
},
},
});
expect(fn.__endpoint).to.deep.equal({
...MINIMAL_V1_ENDPOINT,
platform: "gcfv1",
labels: {},
blockingTrigger: {
eventType: "providers/cloud.auth/eventTypes/user.beforeSendEmail",
options: {
accessToken: false,
idToken: false,
refreshToken: false,
},
},
});
expect(fn.__requiredAPIs).to.deep.equal([
{
api: "identitytoolkit.googleapis.com",
reason: "Needed for auth blocking functions",
},
]);
});

it("should create the function with options", () => {
const fn = functions
.region("us-east1")
.runWith({
timeoutSeconds: 90,
memory: "256MB",
})
.auth.user({
blockingOptions: {
accessToken: true,
refreshToken: false,
},
})
.beforeEmail(() => Promise.resolve());

expect(fn.__trigger).to.deep.equal({
labels: {},
regions: ["us-east1"],
availableMemoryMb: 256,
timeout: "90s",
blockingTrigger: {
eventType: "providers/cloud.auth/eventTypes/user.beforeSendEmail",
options: {
accessToken: true,
idToken: false,
refreshToken: false,
},
},
});
expect(fn.__endpoint).to.deep.equal({
...MINIMAL_V1_ENDPOINT,
platform: "gcfv1",
labels: {},
region: ["us-east1"],
availableMemoryMb: 256,
timeoutSeconds: 90,
blockingTrigger: {
eventType: "providers/cloud.auth/eventTypes/user.beforeSendEmail",
options: {
accessToken: true,
idToken: false,
refreshToken: false,
},
},
});
expect(fn.__requiredAPIs).to.deep.equal([
{
api: "identitytoolkit.googleapis.com",
reason: "Needed for auth blocking functions",
},
]);
});
});

describe("#_dataConstructor", () => {
let cloudFunctionDelete: CloudFunction<UserRecord>;

Expand Down
Loading
Loading