Skip to content

Commit 4ce996f

Browse files
authored
feat(clerk-js): support Slack workspace hints during authentication (#10402)
1 parent 6d7e5fc commit 4ce996f

10 files changed

Lines changed: 727 additions & 19 deletions

File tree

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+
Add the optional `slackTeamIdHint` workspace hint to Slack sign-in and sign-up attempts, including redirect, popup, and unified SSO flows.

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

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -423,8 +423,15 @@ export class SignIn extends BaseResource implements SignInResource {
423423
params: AuthenticateWithRedirectParams,
424424
navigateCallback: (url: URL | string) => void,
425425
): Promise<void> => {
426-
const { strategy, redirectUrlComplete, identifier, oidcPrompt, continueSignIn, enterpriseConnectionId } =
427-
params || {};
426+
const {
427+
strategy,
428+
redirectUrlComplete,
429+
identifier,
430+
oidcPrompt,
431+
slackTeamIdHint,
432+
continueSignIn,
433+
enterpriseConnectionId,
434+
} = params || {};
428435
const actionCompleteRedirectUrl = redirectUrlComplete;
429436

430437
const redirectUrl = SignIn.clerk.buildUrlWithAuth(params.redirectUrl);
@@ -450,13 +457,15 @@ export class SignIn extends BaseResource implements SignInResource {
450457
// provider first" and the challenge runs on the way back, where the callback routes to it.
451458
let challengedCreateHandOff: URL | null = null;
452459

453-
if (!this.id || !continueSignIn) {
460+
const shouldCreateSignIn = !this.id || !continueSignIn;
461+
if (shouldCreateSignIn) {
454462
await this.create({
455463
strategy,
456464
identifier,
457465
redirectUrl,
458466
actionCompleteRedirectUrl,
459467
oidcPrompt,
468+
slackTeamIdHint,
460469
});
461470

462471
if (isChallengePending()) {
@@ -467,14 +476,16 @@ export class SignIn extends BaseResource implements SignInResource {
467476
}
468477
}
469478

470-
if (strategy === 'enterprise_sso') {
471-
await this.prepareFirstFactor({
479+
if (strategy === 'enterprise_sso' || (!shouldCreateSignIn && slackTeamIdHint !== undefined)) {
480+
const prepareParams = {
472481
strategy,
473482
redirectUrl,
474483
actionCompleteRedirectUrl,
475484
oidcPrompt,
485+
slackTeamIdHint,
476486
enterpriseConnectionId,
477-
});
487+
};
488+
await this._basePost({ body: prepareParams, action: 'prepare_first_factor', coalesce: true });
478489

479490
// A challenged prepare builds no verification, so any redirect left on the sign-in is from an
480491
// earlier attempt and may be for another connection. Only this call's create hand-off is safe
@@ -1366,8 +1377,16 @@ class SignInFuture implements SignInFutureResource {
13661377
}
13671378

13681379
async sso(params: SignInFutureSSOParams): Promise<{ error: ClerkError | null }> {
1369-
const { strategy, redirectUrl, redirectCallbackUrl, popup, oidcPrompt, enterpriseConnectionId, identifier } =
1370-
params;
1380+
const {
1381+
strategy,
1382+
redirectUrl,
1383+
redirectCallbackUrl,
1384+
popup,
1385+
oidcPrompt,
1386+
slackTeamIdHint,
1387+
enterpriseConnectionId,
1388+
identifier,
1389+
} = params;
13711390
return runAsyncResourceTask(this.#resource, async () => {
13721391
let actionCompleteRedirectUrl = redirectUrl;
13731392
try {
@@ -1388,13 +1407,12 @@ class SignInFuture implements SignInFutureResource {
13881407

13891408
// Reuse the existing sign-in by default so any state already attached to it carries
13901409
// into the SSO attempt (e.g. a ticket from `signIn.create({ strategy: 'ticket' })`,
1391-
// or a discovered identifier). OAuth is the exception: its redirect URL only comes
1392-
// back from `_create` and cannot be refreshed in place, so any redirect already
1393-
// lingering on the resource is stale. Reusing it would replay a previous attempt's
1394-
// redirect, including a retry of the same provider after an abandoned redirect
1395-
// (SDK-75), so whenever an OAuth call finds a pending redirect we start fresh.
1396-
// `enterprise_sso` is always safe to reuse because the `prepare_first_factor` call
1397-
// below refreshes its redirect against the existing sign-in.
1410+
// or a discovered identifier). For OAuth, a pending redirect may belong to an
1411+
// abandoned attempt, including a retry of the same provider (SDK-75), so start
1412+
// fresh rather than replay it. `enterprise_sso` can reuse the sign-in because
1413+
// `prepare_first_factor` below refreshes its redirect. When reusing a sign-in
1414+
// without a pending redirect, an explicit `slackTeamIdHint` also triggers preparation
1415+
// so the hint is applied to the existing attempt.
13981416
const hasPendingRedirect = !!this.#resource.firstFactorVerification.externalVerificationRedirectURL;
13991417
const wouldReplayStaleRedirect = strategy !== 'enterprise_sso' && hasPendingRedirect;
14001418
const shouldCreateSignIn = !this.#resource.id || wouldReplayStaleRedirect;
@@ -1415,6 +1433,7 @@ class SignInFuture implements SignInFutureResource {
14151433
...routes,
14161434
identifier,
14171435
oidcPrompt,
1436+
slackTeamIdHint,
14181437
},
14191438
{ resolveProtectCheck: false },
14201439
);
@@ -1427,15 +1446,16 @@ class SignInFuture implements SignInFutureResource {
14271446
}
14281447
}
14291448

1430-
if (strategy === 'enterprise_sso') {
1449+
if (strategy === 'enterprise_sso' || (!shouldCreateSignIn && slackTeamIdHint !== undefined)) {
14311450
const prepare = (options?: { resolveProtectCheck?: boolean }) =>
14321451
this.#resource.__internal_basePost(
14331452
{
14341453
body: {
14351454
...routes,
14361455
oidcPrompt,
1456+
slackTeamIdHint,
14371457
enterpriseConnectionId,
1438-
strategy: 'enterprise_sso',
1458+
strategy,
14391459
},
14401460
action: 'prepare_first_factor',
14411461
coalesce: true,

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ export class SignUp extends BaseResource implements SignUpResource {
452452
emailAddress,
453453
legalAccepted,
454454
oidcPrompt,
455+
slackTeamIdHint,
455456
enterpriseConnectionId,
456457
} = params;
457458

@@ -466,6 +467,7 @@ export class SignUp extends BaseResource implements SignUpResource {
466467
emailAddress,
467468
legalAccepted,
468469
oidcPrompt,
470+
slackTeamIdHint,
469471
enterpriseConnectionId,
470472
};
471473
return continueSignUp && this.id ? this.update(authParams) : this.create(authParams);
@@ -1099,6 +1101,7 @@ class SignUpFuture implements SignUpFutureResource {
10991101
unsafeMetadata,
11001102
legalAccepted,
11011103
oidcPrompt,
1104+
slackTeamIdHint,
11021105
enterpriseConnectionId,
11031106
emailAddress,
11041107
popup,
@@ -1134,6 +1137,7 @@ class SignUpFuture implements SignUpFutureResource {
11341137
unsafeMetadata,
11351138
legalAccepted,
11361139
oidcPrompt,
1140+
slackTeamIdHint,
11371141
enterpriseConnectionId,
11381142
emailAddress,
11391143
captchaToken,

0 commit comments

Comments
 (0)