Skip to content

Commit aa184a4

Browse files
authored
feat(clerk-js,shared): Add support for modal sign-up in signals (#7193)
1 parent 5f7a12b commit aa184a4

File tree

5 files changed

+105
-19
lines changed

5 files changed

+105
-19
lines changed

.changeset/fruity-rivers-say.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/clerk-js': minor
3+
'@clerk/shared': minor
4+
---
5+
6+
[Experimental] Add support for sign-up via modal in signals implementation

packages/clerk-js/src/core/resources/SignIn.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,8 @@ class SignInFuture implements SignInFutureResource {
910910
}
911911

912912
async sso(params: SignInFutureSSOParams): Promise<{ error: ClerkError | null }> {
913-
const { strategy, redirectUrl, redirectCallbackUrl, popup, oidcPrompt, enterpriseConnectionId } = params;
913+
const { strategy, redirectUrl, redirectCallbackUrl, popup, oidcPrompt, enterpriseConnectionId, identifier } =
914+
params;
914915
return runAsyncResourceTask(this.resource, async () => {
915916
let actionCompleteRedirectUrl = redirectUrl;
916917
try {
@@ -932,6 +933,7 @@ class SignInFuture implements SignInFutureResource {
932933
await this._create({
933934
strategy,
934935
...routes,
936+
identifier,
935937
});
936938

937939
if (strategy === 'enterprise_sso') {

packages/clerk-js/src/core/resources/SignUp.ts

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ import {
5555
getOKXWalletIdentifier,
5656
windowNavigate,
5757
} from '../../utils';
58-
import { _authenticateWithPopup } from '../../utils/authenticateWithPopup';
58+
import {
59+
_authenticateWithPopup,
60+
_futureAuthenticateWithPopup,
61+
wrapWithPopupRoutes,
62+
} from '../../utils/authenticateWithPopup';
5963
import { CaptchaChallenge } from '../../utils/captcha/CaptchaChallenge';
6064
import { createValidatePassword } from '../../utils/passwords/password';
6165
import { normalizeUnsafeMetadata } from '../../utils/resourceParams';
@@ -789,7 +793,17 @@ class SignUpFuture implements SignUpFutureResource {
789793
}
790794

791795
async sso(params: SignUpFutureSSOParams): Promise<{ error: ClerkError | null }> {
792-
const { strategy, redirectUrl, redirectCallbackUrl } = params;
796+
const {
797+
strategy,
798+
redirectUrl,
799+
redirectCallbackUrl,
800+
unsafeMetadata,
801+
legalAccepted,
802+
oidcPrompt,
803+
enterpriseConnectionId,
804+
emailAddress,
805+
popup,
806+
} = params;
793807
return runAsyncResourceTask(this.resource, async () => {
794808
const { captchaToken, captchaWidgetType, captchaError } = await this.getCaptchaToken();
795809

@@ -800,24 +814,56 @@ class SignUpFuture implements SignUpFutureResource {
800814
redirectUrlComplete = window.location.origin + redirectUrl;
801815
}
802816

803-
await this.resource.__internal_basePost({
804-
path: this.resource.pathRoot,
805-
body: {
806-
strategy,
807-
redirectUrl: SignUp.clerk.buildUrlWithAuth(redirectCallbackUrl),
808-
redirectUrlComplete,
809-
captchaToken,
810-
captchaWidgetType,
811-
captchaError,
812-
},
817+
const routes = {
818+
redirectUrl: SignUp.clerk.buildUrlWithAuth(redirectCallbackUrl),
819+
actionCompleteRedirectUrl: redirectUrlComplete,
820+
};
821+
if (popup) {
822+
const wrappedRoutes = wrapWithPopupRoutes(SignUp.clerk, {
823+
redirectCallbackUrl: routes.redirectUrl,
824+
redirectUrl: redirectUrlComplete,
825+
});
826+
routes.redirectUrl = wrappedRoutes.redirectCallbackUrl;
827+
routes.actionCompleteRedirectUrl = wrappedRoutes.redirectUrl;
828+
}
829+
830+
const authenticateFn = () => {
831+
return this.resource.__internal_basePost({
832+
path: this.resource.pathRoot,
833+
body: {
834+
strategy,
835+
...routes,
836+
unsafeMetadata,
837+
legalAccepted,
838+
oidcPrompt,
839+
enterpriseConnectionId,
840+
emailAddress,
841+
captchaToken,
842+
captchaWidgetType,
843+
captchaError,
844+
},
845+
});
846+
};
847+
848+
await authenticateFn().catch(async e => {
849+
if (isClerkAPIResponseError(e) && isCaptchaError(e)) {
850+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
851+
await SignUp.clerk.__unstable__environment!.reload();
852+
return authenticateFn();
853+
}
854+
throw e;
813855
});
814856

815857
const { status, externalVerificationRedirectURL } = this.resource.verifications.externalAccount;
816858

817-
if (status === 'unverified' && !!externalVerificationRedirectURL) {
818-
windowNavigate(externalVerificationRedirectURL);
819-
} else {
820-
clerkInvalidFAPIResponse(status, SignUp.fapiClient.buildEmailAddress('support'));
859+
if (status === 'unverified' && externalVerificationRedirectURL) {
860+
if (popup) {
861+
await _futureAuthenticateWithPopup(SignUp.clerk, { popup, externalVerificationRedirectURL });
862+
// Pick up the modified SignUp resource
863+
await this.resource.reload();
864+
} else {
865+
windowNavigate(externalVerificationRedirectURL);
866+
}
821867
}
822868
});
823869
}

packages/shared/src/types/signInFuture.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ export interface SignInFutureSSOParams {
192192
* await signIn.sso({ popup, strategy: 'oauth_google', redirectUrl: '/dashboard' });
193193
* ```
194194
*/
195-
popup?: { location: { href: string } };
195+
popup?: Window;
196196
/**
197197
* Optional for `oauth_<provider>` or `enterprise_sso` strategies. The value to pass to the
198198
* [OIDC prompt parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=prompt,reauthentication%20and%20consent.)
@@ -203,6 +203,10 @@ export interface SignInFutureSSOParams {
203203
* @experimental
204204
*/
205205
enterpriseConnectionId?: string;
206+
/**
207+
* The unique identifier of the user. Only supported with the `enterprise_sso` strategy.
208+
*/
209+
identifier?: string;
206210
}
207211

208212
export interface SignInFutureMFAPhoneCodeVerifyParams {

packages/shared/src/types/signUpFuture.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ export interface SignUpFuturePhoneCodeVerifyParams {
188188
code: string;
189189
}
190190

191-
export interface SignUpFutureSSOParams {
191+
export interface SignUpFutureSSOParams extends SignUpFutureAdditionalParams {
192192
/**
193193
* The strategy to use for authentication.
194194
*/
@@ -202,6 +202,34 @@ export interface SignUpFutureSSOParams {
202202
* TODO @revamp-hooks: This should be handled by FAPI instead.
203203
*/
204204
redirectCallbackUrl: string;
205+
/**
206+
* If provided, a `Window` to use for the OAuth flow. Useful in instances where you cannot navigate to an
207+
* OAuth provider.
208+
*
209+
* @example
210+
* ```ts
211+
* const popup = window.open('about:blank', '', 'width=600,height=800');
212+
* if (!popup) {
213+
* throw new Error('Failed to open popup');
214+
* }
215+
* await signIn.sso({ popup, strategy: 'oauth_google', redirectUrl: '/dashboard' });
216+
* ```
217+
*/
218+
popup?: Window;
219+
/**
220+
* Optional for `oauth_<provider>` or `enterprise_sso` strategies. The value to pass to the
221+
* [OIDC prompt parameter](https://openid.net/specs/openid-connect-core-1_0.html#:~:text=prompt,reauthentication%20and%20consent.)
222+
* in the generated OAuth redirect URL.
223+
*/
224+
oidcPrompt?: string;
225+
/**
226+
* @experimental
227+
*/
228+
enterpriseConnectionId?: string;
229+
/**
230+
* Email address to use for targeting an enterprise connection at sign-up.
231+
*/
232+
emailAddress?: string;
205233
}
206234

207235
export interface SignUpFutureTicketParams extends SignUpFutureAdditionalParams {

0 commit comments

Comments
 (0)