Skip to content

Commit fe065a9

Browse files
authored
feat(nextjs,astro,backend): Redirect to tasks on auth.protect and auth.redirectToSignIn (#5440)
1 parent cd6ee92 commit fe065a9

File tree

9 files changed

+332
-209
lines changed

9 files changed

+332
-209
lines changed

.changeset/tiny-planes-lead.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/backend': minor
3+
'@clerk/nextjs': minor
4+
'@clerk/astro': minor
5+
---
6+
7+
Redirect to tasks on `auth.protect` and `auth.redirectToSignIn`

integration/tests/session-tasks-sign-in.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { test } from '@playwright/test';
1+
import { expect, test } from '@playwright/test';
22

33
import { appConfigs } from '../presets';
44
import type { FakeUser } from '../testUtils';
@@ -38,6 +38,10 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withSessionTasks] })(
3838
await u.po.signIn.continue();
3939
await u.po.expect.toBeSignedIn();
4040

41+
// Redirects back to tasks when accessing protected route by `auth.protect`
42+
await u.page.goToRelative('/page-protected');
43+
expect(page.url()).toContain('tasks');
44+
4145
// Resolves task
4246
await u.po.sessionTask.resolveForceOrganizationSelectionTask(fakeOrganization);
4347
await u.po.expect.toHaveResolvedTask();

integration/tests/session-tasks-sign-up.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { test } from '@playwright/test';
1+
import { expect, test } from '@playwright/test';
22

33
import { appConfigs } from '../presets';
44
import type { FakeUser } from '../testUtils';
@@ -38,6 +38,11 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withSessionTasks] })(
3838
email: fakeUser.email,
3939
password: fakeUser.password,
4040
});
41+
await u.po.expect.toBeSignedIn();
42+
43+
// Redirects back to tasks when accessing protected route by `auth.protect`
44+
await u.page.goToRelative('/page-protected');
45+
expect(page.url()).toContain('tasks');
4146

4247
// Resolves task
4348
await u.po.sessionTask.resolveForceOrganizationSelectionTask(fakeOrganization);

packages/astro/src/server/clerk-middleware.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ function decorateAstroLocal(clerkRequest: ClerkRequest, context: APIContext, req
277277
publishableKey: getSafeEnv(context).pk!,
278278
signInUrl: requestState.signInUrl,
279279
signUpUrl: requestState.signUpUrl,
280+
sessionStatus: requestState.toAuth()?.sessionStatus,
280281
}).redirectToSignIn({
281282
returnBackUrl: opts.returnBackUrl === null ? '' : opts.returnBackUrl || clerkUrl.toString(),
282283
});
@@ -395,6 +396,7 @@ const handleControlFlowErrors = (
395396
signUpUrl: requestState.signUpUrl,
396397
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
397398
publishableKey: getSafeEnv(context).pk!,
399+
sessionStatus: requestState.toAuth()?.sessionStatus,
398400
}).redirectToSignIn({ returnBackUrl: e.returnBackUrl });
399401
default:
400402
throw e;

packages/backend/src/__tests__/createRedirect.test.ts

Lines changed: 277 additions & 200 deletions
Large diffs are not rendered by default.

packages/backend/src/createRedirect.ts

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { buildAccountsBaseUrl } from '@clerk/shared/buildAccountsBaseUrl';
2+
import type { SessionStatusClaim } from '@clerk/types';
23

34
import { constants } from './constants';
45
import { errorThrower, parsePublishableKey } from './util/shared';
@@ -70,36 +71,54 @@ type CreateRedirect = <ReturnType>(params: {
7071
baseUrl: URL | string;
7172
signInUrl?: URL | string;
7273
signUpUrl?: URL | string;
74+
sessionStatus?: SessionStatusClaim | null;
7375
}) => {
7476
redirectToSignIn: RedirectFun<ReturnType>;
7577
redirectToSignUp: RedirectFun<ReturnType>;
7678
};
7779

7880
export const createRedirect: CreateRedirect = params => {
79-
const { publishableKey, redirectAdapter, signInUrl, signUpUrl, baseUrl } = params;
81+
const { publishableKey, redirectAdapter, signInUrl, signUpUrl, baseUrl, sessionStatus } = params;
8082
const parsedPublishableKey = parsePublishableKey(publishableKey);
8183
const frontendApi = parsedPublishableKey?.frontendApi;
8284
const isDevelopment = parsedPublishableKey?.instanceType === 'development';
8385
const accountsBaseUrl = buildAccountsBaseUrl(frontendApi);
86+
const hasPendingStatus = sessionStatus === 'pending';
87+
88+
const redirectToTasks = (url: string | URL, { returnBackUrl }: RedirectToParams) => {
89+
return redirectAdapter(
90+
buildUrl(baseUrl, `${url}/tasks`, returnBackUrl, isDevelopment ? params.devBrowserToken : null),
91+
);
92+
};
8493

8594
const redirectToSignUp = ({ returnBackUrl }: RedirectToParams = {}) => {
8695
if (!signUpUrl && !accountsBaseUrl) {
8796
errorThrower.throwMissingPublishableKeyError();
8897
}
98+
8999
const accountsSignUpUrl = `${accountsBaseUrl}/sign-up`;
90-
return redirectAdapter(
91-
buildUrl(baseUrl, signUpUrl || accountsSignUpUrl, returnBackUrl, isDevelopment ? params.devBrowserToken : null),
92-
);
100+
const targetUrl = signUpUrl || accountsSignUpUrl;
101+
102+
if (hasPendingStatus) {
103+
return redirectToTasks(targetUrl, { returnBackUrl });
104+
}
105+
106+
return redirectAdapter(buildUrl(baseUrl, targetUrl, returnBackUrl, isDevelopment ? params.devBrowserToken : null));
93107
};
94108

95109
const redirectToSignIn = ({ returnBackUrl }: RedirectToParams = {}) => {
96110
if (!signInUrl && !accountsBaseUrl) {
97111
errorThrower.throwMissingPublishableKeyError();
98112
}
113+
99114
const accountsSignInUrl = `${accountsBaseUrl}/sign-in`;
100-
return redirectAdapter(
101-
buildUrl(baseUrl, signInUrl || accountsSignInUrl, returnBackUrl, isDevelopment ? params.devBrowserToken : null),
102-
);
115+
const targetUrl = signInUrl || accountsSignInUrl;
116+
117+
if (hasPendingStatus) {
118+
return redirectToTasks(targetUrl, { returnBackUrl });
119+
}
120+
121+
return redirectAdapter(buildUrl(baseUrl, targetUrl, returnBackUrl, isDevelopment ? params.devBrowserToken : null));
103122
};
104123

105124
return { redirectToSignUp, redirectToSignIn };

packages/nextjs/src/app-router/server/auth.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export const auth: AuthFn = async () => {
9999
publishableKey: decryptedRequestData.publishableKey || PUBLISHABLE_KEY,
100100
signInUrl: decryptedRequestData.signInUrl || SIGN_IN_URL,
101101
signUpUrl: decryptedRequestData.signUpUrl || SIGN_UP_URL,
102+
sessionStatus: authObject.sessionStatus,
102103
}).redirectToSignIn({
103104
returnBackUrl: opts.returnBackUrl === null ? '' : opts.returnBackUrl || clerkUrl?.toString(),
104105
});

packages/nextjs/src/server/clerkMiddleware.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ const handleControlFlowErrors = (
352352
signInUrl: requestState.signInUrl,
353353
signUpUrl: requestState.signUpUrl,
354354
publishableKey: requestState.publishableKey,
355+
sessionStatus: requestState.toAuth()?.sessionStatus,
355356
}).redirectToSignIn({ returnBackUrl: e.returnBackUrl });
356357
}
357358

packages/nextjs/src/server/protect.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ export function createProtect(opts: {
9090
return notFound();
9191
};
9292

93+
/**
94+
* Redirects the user back to the tasks URL if their session status is pending
95+
*/
96+
if (authObject.sessionStatus === 'pending') {
97+
return handleUnauthenticated();
98+
}
99+
93100
/**
94101
* User is not authenticated
95102
*/

0 commit comments

Comments
 (0)