-
Notifications
You must be signed in to change notification settings - Fork 386
fix(clerk-js): Send client_id in CHIPS build #6758
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 3 commits
4ce98f8
cb5a23f
f59c43e
ff88ba4
21e25ab
1b80c5e
5158344
a4feabd
44e5eaa
e24c213
9cef617
e124825
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
'@clerk/clerk-js': minor | ||
'@clerk/types': minor | ||
--- | ||
|
||
Adding iframeContext to SignIn and SignUp params when a CHIPS build is running in an iframe context | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,6 +54,7 @@ import type { | |
} from '@clerk/types'; | ||
|
||
import { debugLogger } from '@/utils/debug'; | ||
import { inIframe } from '@/utils/runtime'; | ||
|
||
import { | ||
generateSignatureWithBase, | ||
|
@@ -298,12 +299,18 @@ export class SignIn extends BaseResource implements SignInResource { | |
const redirectUrl = SignIn.clerk.buildUrlWithAuth(params.redirectUrl); | ||
|
||
if (!this.id || !continueSignIn) { | ||
await this.create({ | ||
const createParams: SignInCreateParams = { | ||
strategy, | ||
identifier, | ||
redirectUrl, | ||
actionCompleteRedirectUrl, | ||
}); | ||
}; | ||
|
||
if (__BUILD_VARIANT_CHIPS__ && inIframe()) { | ||
createParams.iframeContext = true; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the core change. When in CHIPS build and running in an iframe we want to send extra params to FAPI that indicate we are using partitioned cookies in an iframe. FAPI will skip validating the client_id in this scenario |
||
|
||
await this.create(createParams); | ||
} | ||
|
||
if (strategy === 'saml' || strategy === 'enterprise_sso') { | ||
|
@@ -627,9 +634,15 @@ class SignInFuture implements SignInFutureResource { | |
|
||
async create(params: SignInFutureCreateParams): Promise<{ error: unknown }> { | ||
return runAsyncResourceTask(this.resource, async () => { | ||
const createParams: SignInFutureCreateParams = { ...params }; | ||
|
||
if (__BUILD_VARIANT_CHIPS__ && inIframe()) { | ||
createParams.iframeContext = true; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above, this is the core logical change |
||
|
||
await this.resource.__internal_basePost({ | ||
path: this.resource.pathRoot, | ||
body: params, | ||
body: createParams, | ||
}); | ||
}); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,6 +53,7 @@ import { CaptchaChallenge } from '../../utils/captcha/CaptchaChallenge'; | |
import { createValidatePassword } from '../../utils/passwords/password'; | ||
import { normalizeUnsafeMetadata } from '../../utils/resourceParams'; | ||
import { runAsyncResourceTask } from '../../utils/runAsyncResourceTask'; | ||
import { inIframe } from '../../utils/runtime'; | ||
import { | ||
clerkInvalidFAPIResponse, | ||
clerkMissingOptionError, | ||
|
@@ -142,6 +143,10 @@ export class SignUp extends BaseResource implements SignUpResource { | |
|
||
let finalParams = { ...params }; | ||
|
||
if (__BUILD_VARIANT_CHIPS__ && inIframe()) { | ||
finalParams.iframeContext = true; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as the SignIn change |
||
|
||
if (!__BUILD_DISABLE_RHC__ && !this.clientBypass() && !this.shouldBypassCaptchaForAttempt(params)) { | ||
const captchaChallenge = new CaptchaChallenge(SignUp.clerk); | ||
const captchaParams = await captchaChallenge.managedOrInvisible({ action: 'signup' }); | ||
|
@@ -426,8 +431,14 @@ export class SignUp extends BaseResource implements SignUpResource { | |
}; | ||
|
||
update = (params: SignUpUpdateParams): Promise<SignUpResource> => { | ||
const finalParams = { ...params }; | ||
|
||
if (__BUILD_VARIANT_CHIPS__ && inIframe()) { | ||
finalParams.iframeContext = true; | ||
} | ||
|
||
return this._basePatch({ | ||
body: normalizeUnsafeMetadata(params), | ||
body: normalizeUnsafeMetadata(finalParams), | ||
}); | ||
}; | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; | ||
|
||
import { inIframe } from '../../../utils/runtime'; | ||
import { SignIn } from '../SignIn'; | ||
|
||
vi.mock('../../../utils/runtime', () => ({ | ||
inIframe: vi.fn(), | ||
})); | ||
|
||
const originalBuildVariant = global.__BUILD_VARIANT_CHIPS__; | ||
global.__BUILD_VARIANT_CHIPS__ = true; | ||
|
||
describe('SignIn', () => { | ||
let signIn: SignIn; | ||
let mockCreate: any; | ||
let mockBuildUrlWithAuth: any; | ||
|
||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
|
||
const mockClerk = { | ||
buildUrlWithAuth: vi.fn((url: string) => `https://clerk.example.com${url}`), | ||
}; | ||
|
||
const mockFapiClient = { | ||
buildEmailAddress: vi.fn(() => '[email protected]'), | ||
}; | ||
|
||
SignIn.clerk = mockClerk as any; | ||
|
||
Object.defineProperty(SignIn, 'fapiClient', { | ||
get: () => mockFapiClient, | ||
configurable: true, | ||
}); | ||
|
||
signIn = new SignIn({ | ||
id: 'test-signin-id', | ||
status: 'needs_first_factor', | ||
first_factor_verification: { | ||
status: 'unverified', | ||
external_verification_redirect_url: 'https://oauth.provider.com/auth', | ||
}, | ||
} as any); | ||
|
||
mockCreate = vi.fn().mockResolvedValue({}); | ||
signIn.create = mockCreate; | ||
|
||
mockBuildUrlWithAuth = vi.fn((url: string) => `https://clerk.example.com${url}`); | ||
SignIn.clerk.buildUrlWithAuth = mockBuildUrlWithAuth; | ||
}); | ||
|
||
afterEach(() => { | ||
global.__BUILD_VARIANT_CHIPS__ = originalBuildVariant; | ||
}); | ||
|
||
describe('authenticateWithRedirectOrPopup', () => { | ||
it('should set iframeContext to true when CHIPS build and in iframe', async () => { | ||
vi.mocked(inIframe).mockReturnValue(true); | ||
|
||
const params = { | ||
strategy: 'oauth_google' as const, | ||
redirectUrl: '/callback', | ||
identifier: '[email protected]', | ||
}; | ||
|
||
const mockNavigate = vi.fn(); | ||
|
||
Object.defineProperty(signIn, 'firstFactorVerification', { | ||
value: { | ||
status: 'unverified', | ||
externalVerificationRedirectURL: 'https://oauth.provider.com/auth', | ||
}, | ||
writable: true, | ||
}); | ||
|
||
await signIn.authenticateWithRedirectOrPopup(params, mockNavigate); | ||
|
||
expect(mockCreate).toHaveBeenCalledWith({ | ||
strategy: 'oauth_google', | ||
identifier: '[email protected]', | ||
redirectUrl: 'https://clerk.example.com/callback', | ||
actionCompleteRedirectUrl: undefined, | ||
iframeContext: true, | ||
}); | ||
}); | ||
|
||
it('should not set iframeContext when not in iframe', async () => { | ||
vi.mocked(inIframe).mockReturnValue(false); | ||
|
||
const params = { | ||
strategy: 'oauth_google' as const, | ||
redirectUrl: '/callback', | ||
identifier: '[email protected]', | ||
}; | ||
|
||
const mockNavigate = vi.fn(); | ||
|
||
Object.defineProperty(signIn, 'firstFactorVerification', { | ||
value: { | ||
status: 'unverified', | ||
externalVerificationRedirectURL: 'https://oauth.provider.com/auth', | ||
}, | ||
writable: true, | ||
}); | ||
|
||
await signIn.authenticateWithRedirectOrPopup(params, mockNavigate); | ||
|
||
expect(mockCreate).toHaveBeenCalledWith({ | ||
strategy: 'oauth_google', | ||
identifier: '[email protected]', | ||
redirectUrl: 'https://clerk.example.com/callback', | ||
actionCompleteRedirectUrl: undefined, | ||
}); | ||
expect(mockCreate).toHaveBeenCalledWith(expect.not.objectContaining({ iframeContext: true })); | ||
}); | ||
|
||
it('should not set iframeContext when not CHIPS build', async () => { | ||
global.__BUILD_VARIANT_CHIPS__ = false; | ||
|
||
vi.mocked(inIframe).mockReturnValue(true); | ||
|
||
const params = { | ||
strategy: 'oauth_google' as const, | ||
redirectUrl: '/callback', | ||
identifier: '[email protected]', | ||
}; | ||
|
||
const mockNavigate = vi.fn(); | ||
|
||
Object.defineProperty(signIn, 'firstFactorVerification', { | ||
value: { | ||
status: 'unverified', | ||
externalVerificationRedirectURL: 'https://oauth.provider.com/auth', | ||
}, | ||
writable: true, | ||
}); | ||
|
||
await signIn.authenticateWithRedirectOrPopup(params, mockNavigate); | ||
|
||
expect(mockCreate).toHaveBeenCalledWith(expect.not.objectContaining({ iframeContext: true })); | ||
}); | ||
|
||
it('should not set iframeContext when continueSignIn is true', async () => { | ||
vi.mocked(inIframe).mockReturnValue(true); | ||
|
||
const params = { | ||
strategy: 'oauth_google' as const, | ||
redirectUrl: '/callback', | ||
identifier: '[email protected]', | ||
continueSignIn: true, | ||
}; | ||
|
||
const mockNavigate = vi.fn(); | ||
|
||
Object.defineProperty(signIn, 'firstFactorVerification', { | ||
value: { | ||
status: 'unverified', | ||
externalVerificationRedirectURL: 'https://oauth.provider.com/auth', | ||
}, | ||
writable: true, | ||
}); | ||
|
||
await signIn.authenticateWithRedirectOrPopup(params, mockNavigate); | ||
|
||
expect(mockCreate).not.toHaveBeenCalled(); | ||
}); | ||
}); | ||
|
||
describe('SignInFuture.create', () => { | ||
it('should set iframeContext to true when CHIPS build and in iframe', async () => { | ||
global.__BUILD_VARIANT_CHIPS__ = true; | ||
vi.mocked(inIframe).mockReturnValue(true); | ||
|
||
const params = { | ||
strategy: 'oauth_google' as const, | ||
redirectUrl: '/callback', | ||
identifier: '[email protected]', | ||
}; | ||
|
||
const signInFuture = signIn.__internal_future; | ||
|
||
const mockBasePost = vi.fn().mockResolvedValue({}); | ||
signInFuture.resource.__internal_basePost = mockBasePost; | ||
|
||
await signInFuture.create(params); | ||
|
||
expect(mockBasePost).toHaveBeenCalledWith({ | ||
path: '/client/sign_ins', | ||
body: { | ||
strategy: 'oauth_google', | ||
redirectUrl: '/callback', | ||
identifier: '[email protected]', | ||
iframeContext: true, | ||
}, | ||
}); | ||
}); | ||
|
||
it('should not set iframeContext when not in iframe', async () => { | ||
vi.mocked(inIframe).mockReturnValue(false); | ||
|
||
const params = { | ||
strategy: 'oauth_google' as const, | ||
redirectUrl: '/callback', | ||
identifier: '[email protected]', | ||
}; | ||
|
||
const signInFuture = signIn.__internal_future; | ||
|
||
const mockBasePost = vi.fn().mockResolvedValue({}); | ||
signInFuture.resource.__internal_basePost = mockBasePost; | ||
|
||
await signInFuture.create(params); | ||
|
||
expect(mockBasePost).toHaveBeenCalledWith({ | ||
path: '/client/sign_ins', | ||
body: { | ||
strategy: 'oauth_google', | ||
redirectUrl: '/callback', | ||
identifier: '[email protected]', | ||
}, | ||
}); | ||
}); | ||
}); | ||
}); |
Uh oh!
There was an error while loading. Please reload this page.