-
Notifications
You must be signed in to change notification settings - Fork 218
supporting recaptcha verdict for auth blocking functions #1458
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
Changes from 1 commit
ea509af
ecdc6e1
0de4e7e
bbe4ff0
a0d53f2
398cfc0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,7 @@ import * as identity from "../../../src/common/providers/identity"; | |
|
|
||
| const EVENT = "EVENT_TYPE"; | ||
| const now = new Date(); | ||
| const TEST_NAME = "John Doe"; | ||
|
|
||
| describe("identity", () => { | ||
| describe("userRecordConstructor", () => { | ||
|
|
@@ -232,14 +233,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]", | ||
|
|
@@ -340,12 +341,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", | ||
|
|
@@ -366,7 +367,7 @@ describe("identity", () => { | |
| provider_id: "password", | ||
| email: "[email protected]", | ||
| uid: "[email protected]", | ||
| display_name: "John Doe", | ||
| display_name: TEST_NAME, | ||
| }, | ||
| ], | ||
| password_hash: "passwordHash", | ||
|
|
@@ -407,11 +408,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", | ||
|
|
@@ -435,7 +436,7 @@ describe("identity", () => { | |
| }, | ||
| { | ||
| providerId: "password", | ||
| displayName: "John Doe", | ||
| displayName: TEST_NAME, | ||
| photoURL: undefined, | ||
| email: "[email protected]", | ||
| uid: "[email protected]", | ||
|
|
@@ -489,8 +490,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", | ||
|
|
@@ -516,6 +518,7 @@ describe("identity", () => { | |
| user_agent: "USER_AGENT", | ||
| locale: "en", | ||
| raw_user_info: JSON.stringify(rawUserInfo), | ||
| recaptcha_score: TEST_RECAPTCHA_SCORE, | ||
| }; | ||
| const context = { | ||
| locale: "en", | ||
|
|
@@ -534,6 +537,7 @@ describe("identity", () => { | |
| profile: rawUserInfo, | ||
| username: undefined, | ||
| isNewUser: false, | ||
| recaptchaScore: TEST_RECAPTCHA_SCORE, | ||
| }, | ||
| credential: null, | ||
| params: {}, | ||
|
|
@@ -563,6 +567,7 @@ 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", | ||
|
|
@@ -581,6 +586,7 @@ describe("identity", () => { | |
| profile: rawUserInfo, | ||
| username: undefined, | ||
| isNewUser: false, | ||
| recaptchaScore: TEST_RECAPTCHA_SCORE, | ||
| }, | ||
| credential: { | ||
| claims: undefined, | ||
|
|
@@ -619,14 +625,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", | ||
|
|
@@ -647,6 +653,7 @@ 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", | ||
|
|
@@ -665,6 +672,7 @@ describe("identity", () => { | |
| providerId: "oidc.provider", | ||
| profile: rawUserInfo, | ||
| isNewUser: true, | ||
| recaptchaScore: TEST_RECAPTCHA_SCORE, | ||
| }, | ||
| credential: { | ||
| claims: undefined, | ||
|
|
@@ -762,4 +770,38 @@ describe("identity", () => { | |
| ); | ||
| }); | ||
| }); | ||
|
|
||
| describe("generateRequestPayload", () => { | ||
| const DISPLAY_NAME_FILED = "displayName"; | ||
Xiaoshouzi-gh marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| const TEST_RESPONSE = { | ||
| displayName: TEST_NAME, | ||
| recaptchaPassed: false, | ||
| } as identity.BeforeCreateResponse; | ||
|
|
||
| const EXPECT_PAYLOAD = { | ||
| userRecord: { displayName: TEST_NAME, updateMask: DISPLAY_NAME_FILED }, | ||
| recaptchaPassed: false, | ||
Xiaoshouzi-gh marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| }; | ||
|
|
||
| const TEST_RESPONSE_RECAPTCHA_UNDEFINED = { | ||
| displayName: TEST_NAME, | ||
| } as identity.BeforeSignInResponse; | ||
|
|
||
| const EXPECT_PAYLOAD_UNDEFINED = { | ||
| userRecord: { displayName: TEST_NAME, updateMask: DISPLAY_NAME_FILED }, | ||
| }; | ||
| it("should return empty string on undefined response", () => { | ||
| expect(identity.generateRequestPayload()).to.eq(""); | ||
| }); | ||
|
|
||
| it("should exclude recaptchaPass field from updateMask", () => { | ||
| expect(identity.generateRequestPayload(TEST_RESPONSE)).to.deep.equal(EXPECT_PAYLOAD); | ||
| }); | ||
|
|
||
| it("should not return recaptchaPass if undefined", () => { | ||
| const payload = identity.generateRequestPayload(TEST_RESPONSE_RECAPTCHA_UNDEFINED); | ||
| expect(payload.hasOwnProperty("recaptchaPassed")).to.be.false; | ||
| expect(payload).to.deep.equal(EXPECT_PAYLOAD_UNDEFINED); | ||
| }); | ||
| }); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -310,6 +310,7 @@ export interface AdditionalUserInfo { | |
| profile?: any; | ||
| username?: string; | ||
| isNewUser: boolean; | ||
| recaptchaScore?: number; | ||
| } | ||
|
|
||
| /** The credential component of the auth event context */ | ||
|
|
@@ -338,8 +339,13 @@ export interface AuthBlockingEvent extends AuthEventContext { | |
| data: AuthUserRecord; | ||
| } | ||
|
|
||
| /** The base handler response type for beforeCreate and beforeSignIn blocking events*/ | ||
| export interface BlockingFunctionResponse { | ||
| recaptchaPassed?: boolean; | ||
|
||
| } | ||
|
|
||
| /** The handler response type for beforeCreate blocking events */ | ||
| export interface BeforeCreateResponse { | ||
| export interface BeforeCreateResponse extends BlockingFunctionResponse { | ||
| displayName?: string; | ||
| disabled?: boolean; | ||
| emailVerified?: boolean; | ||
|
|
@@ -423,6 +429,7 @@ export interface DecodedPayload { | |
| oauth_refresh_token?: string; | ||
| oauth_token_secret?: string; | ||
| oauth_expires_in?: number; | ||
| recaptcha_score?: number; | ||
| [key: string]: any; | ||
| } | ||
|
|
||
|
|
@@ -640,9 +647,38 @@ function parseAdditionalUserInfo(decodedJWT: DecodedPayload): AdditionalUserInfo | |
| profile, | ||
| username, | ||
| isNewUser: decodedJWT.event_type === "beforeCreate" ? true : false, | ||
| recaptchaScore: decodedJWT.recaptcha_score, | ||
| }; | ||
| } | ||
|
|
||
| /** Helper to generate payload to GCIP from client request. | ||
|
||
| * @internal | ||
| */ | ||
| export function generateRequestPayload( | ||
Xiaoshouzi-gh marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| authResponse?: BeforeCreateResponse | BeforeSignInResponse | ||
| ): any { | ||
|
||
| if (!authResponse) { | ||
| return ""; | ||
| } | ||
|
|
||
| const { recaptchaPassed, ...formattedAuthResponse } = authResponse; | ||
| const result = {} as any; | ||
| const updateMask = getUpdateMask(formattedAuthResponse); | ||
|
|
||
| if (updateMask.length !== 0) { | ||
| result.userRecord = { | ||
| ...formattedAuthResponse, | ||
| updateMask, | ||
| }; | ||
| } | ||
|
|
||
| if (recaptchaPassed !== undefined) { | ||
| result.recaptchaPassed = recaptchaPassed; | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| /** Helper to get the Credential from the decoded jwt */ | ||
| function parseAuthCredential(decodedJWT: DecodedPayload, time: number): Credential { | ||
| if ( | ||
|
|
@@ -801,7 +837,6 @@ export function wrapHandler(eventType: AuthBlockingEventType, handler: HandlerV1 | |
| : handler.length === 2 | ||
| ? await auth.getAuth(getApp())._verifyAuthBlockingToken(req.body.data.jwt) | ||
| : await auth.getAuth(getApp())._verifyAuthBlockingToken(req.body.data.jwt, "run.app"); | ||
|
|
||
| const authUserRecord = parseAuthUserRecord(decodedPayload.user_record); | ||
| const authEventContext = parseAuthEventContext(decodedPayload, projectId); | ||
|
|
||
|
|
@@ -818,16 +853,7 @@ export function wrapHandler(eventType: AuthBlockingEventType, handler: HandlerV1 | |
| } | ||
|
|
||
| validateAuthResponse(eventType, authResponse); | ||
| const updateMask = getUpdateMask(authResponse); | ||
| const result = | ||
| updateMask.length === 0 | ||
| ? {} | ||
| : { | ||
| userRecord: { | ||
| ...authResponse, | ||
| updateMask, | ||
| }, | ||
| }; | ||
| const result = generateRequestPayload(authResponse); | ||
|
|
||
| res.status(200); | ||
| res.setHeader("Content-Type", "application/json"); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"generateResponsePayload"