From 6ac16c769652a79c0dcc38b65400ed96846b4264 Mon Sep 17 00:00:00 2001
From: Paul Asjes
Date: Mon, 28 Oct 2024 19:16:50 +0100
Subject: [PATCH 1/5] Add signUpPaths config
---
README.md | 12 ++++++++++++
src/interfaces.ts | 1 +
src/middleware.ts | 3 ++-
src/session.ts | 23 ++++++++++++++++++++---
4 files changed, 35 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index a222684..8a6d607 100644
--- a/README.md
+++ b/README.md
@@ -282,6 +282,18 @@ Use the `refreshSession` method in a server action or route handler to fetch the
The `organizationId` parameter can be passed to `refreshSession` in order to switch the session to a different organization. If the current session is not authorized for the next organization, an appropriate [authentication error](https://workos.com/docs/reference/user-management/authentication-errors) will be returned.
+### Sign up paths
+
+The `signUpPaths` option can be passed to `authkitMiddleware` to specify paths that should use the 'sign-up' screen hint when redirecting to AuthKit. This is useful for cases where you want a path that mandates authentication to be treated as a sign up page.
+
+```ts
+import { authkitMiddleware } from '@workos-inc/authkit-nextjs';
+
+export default authkitMiddleware({
+ signUpPaths: ['/account/sign-up'],
+});
+```
+
### Debugging
To enable debug logs, initialize the middleware with the debug flag enabled.
diff --git a/src/interfaces.ts b/src/interfaces.ts
index 32d17e4..9583b32 100644
--- a/src/interfaces.ts
+++ b/src/interfaces.ts
@@ -57,6 +57,7 @@ export interface AuthkitMiddlewareOptions {
debug?: boolean;
middlewareAuth?: AuthkitMiddlewareAuth;
redirectUri?: string;
+ signUpPaths?: string[];
}
export interface CookieOptions {
diff --git a/src/middleware.ts b/src/middleware.ts
index 016d868..8474b31 100644
--- a/src/middleware.ts
+++ b/src/middleware.ts
@@ -7,8 +7,9 @@ export function authkitMiddleware({
debug = false,
middlewareAuth = { enabled: false, unauthenticatedPaths: [] },
redirectUri = WORKOS_REDIRECT_URI,
+ signUpPaths = [],
}: AuthkitMiddlewareOptions = {}): NextMiddleware {
return function (request) {
- return updateSession(request, debug, middlewareAuth, redirectUri);
+ return updateSession(request, debug, middlewareAuth, redirectUri, signUpPaths);
};
}
diff --git a/src/session.ts b/src/session.ts
index 05d7203..21b3417 100644
--- a/src/session.ts
+++ b/src/session.ts
@@ -16,6 +16,7 @@ import { parse, tokensToRegexp } from 'path-to-regexp';
const sessionHeaderName = 'x-workos-session';
const middlewareHeaderName = 'x-workos-middleware';
const redirectUriHeaderName = 'x-redirect-uri';
+const signUpPathsHeaderName = 'x-sign-up-paths';
const JWKS = createRemoteJWKSet(new URL(workos.userManagement.getJwksUrl(WORKOS_CLIENT_ID)));
@@ -28,6 +29,7 @@ async function updateSession(
debug: boolean,
middlewareAuth: AuthkitMiddlewareAuth,
redirectUri: string,
+ signUpPaths: string[],
) {
if (!redirectUri && !WORKOS_REDIRECT_URI) {
throw new Error('You must provide a redirect URI in the AuthKit middleware or in the environment variables.');
@@ -44,6 +46,11 @@ async function updateSession(
// Record that the request was routed through the middleware so we can check later for DX purposes
newRequestHeaders.set(middlewareHeaderName, 'true');
+ // Record the sign up paths so we can use it later
+ if (signUpPaths.length > 0) {
+ newRequestHeaders.set(signUpPathsHeaderName, signUpPaths.join(','));
+ }
+
let url;
// If the redirect URI is set, store it in the headers so we can use it later
@@ -77,6 +84,8 @@ async function updateSession(
return pathRegex.exec(request.nextUrl.pathname);
});
+ const screenHint = signUpPaths.includes(request.nextUrl.pathname) ? 'sign-up' : 'sign-in';
+
// If the user is logged out and this path isn't on the allowlist for logged out paths, redirect to AuthKit.
if (middlewareAuth.enabled && matchedPaths.length === 0 && !session) {
if (debug) console.log(`Unauthenticated user on protected route ${request.url}, redirecting to AuthKit`);
@@ -85,6 +94,7 @@ async function updateSession(
await getAuthorizationUrl({
returnPathname: getReturnPathname(request.url),
redirectUri: redirectUri ?? WORKOS_REDIRECT_URI,
+ screenHint,
}),
);
}
@@ -227,10 +237,17 @@ function getMiddlewareAuthPathRegex(pathGlob: string) {
async function redirectToSignIn() {
const headersList = await headers();
- const url = headersList.get('x-url');
- const returnPathname = url ? getReturnPathname(url) : undefined;
+ const url = headersList.get('x-url') ?? '';
+
+ // Determine if the current route is in the sign up paths
+ const signUpPaths = headersList.get(signUpPathsHeaderName)?.split(',');
+
+ const pathname = new URL(url).pathname;
+ const screenHint = signUpPaths?.includes(pathname) ? 'sign-up' : 'sign-in';
+
+ const returnPathname = url && getReturnPathname(url);
- redirect(await getAuthorizationUrl({ returnPathname }));
+ redirect(await getAuthorizationUrl({ returnPathname, screenHint }));
}
async function withAuth(options?: { ensureSignedIn: false }): Promise;
From 1faa251bc6873385bf2a41f7f664b14abe7472c3 Mon Sep 17 00:00:00 2001
From: Paul Asjes
Date: Wed, 6 Nov 2024 11:51:16 +0100
Subject: [PATCH 2/5] Add regex logic and make it reusable
---
src/session.ts | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/src/session.ts b/src/session.ts
index f1afe31..f6ff87b 100644
--- a/src/session.ts
+++ b/src/session.ts
@@ -84,8 +84,6 @@ async function updateSession(
return pathRegex.exec(request.nextUrl.pathname);
});
- const screenHint = signUpPaths.includes(request.nextUrl.pathname) ? 'sign-up' : 'sign-in';
-
// If the user is logged out and this path isn't on the allowlist for logged out paths, redirect to AuthKit.
if (middlewareAuth.enabled && matchedPaths.length === 0 && !session) {
if (debug) console.log(`Unauthenticated user on protected route ${request.url}, redirecting to AuthKit`);
@@ -93,7 +91,7 @@ async function updateSession(
const redirectTo = await getAuthorizationUrl({
returnPathname: getReturnPathname(request.url),
redirectUri: redirectUri ?? WORKOS_REDIRECT_URI,
- screenHint,
+ screenHint: getScreenHint(signUpPaths, request.nextUrl.pathname),
});
// Fall back to standard Response if NextResponse is not available.
@@ -252,7 +250,7 @@ async function redirectToSignIn() {
const signUpPaths = headersList.get(signUpPathsHeaderName)?.split(',');
const pathname = new URL(url).pathname;
- const screenHint = signUpPaths?.includes(pathname) ? 'sign-up' : 'sign-in';
+ const screenHint = getScreenHint(signUpPaths, pathname);
const returnPathname = url && getReturnPathname(url);
@@ -361,4 +359,19 @@ function getReturnPathname(url: string): string {
return `${newUrl.pathname}${newUrl.searchParams.size > 0 ? '?' + newUrl.searchParams.toString() : ''}`;
}
+function getScreenHint(signUpPaths: string[] | string | undefined, pathname: string) {
+ if (!signUpPaths) return 'sign-in';
+
+ if (!Array.isArray(signUpPaths)) {
+ return signUpPaths.includes(pathname) ? 'sign-up' : 'sign-in';
+ }
+
+ const screenHintPaths: string[] = signUpPaths.filter((pathGlob) => {
+ const pathRegex = getMiddlewareAuthPathRegex(pathGlob);
+ return pathRegex.exec(pathname);
+ });
+
+ return screenHintPaths.length > 0 ? 'sign-up' : 'sign-in';
+}
+
export { encryptSession, withAuth, refreshSession, terminateSession, updateSession, getSession };
From c64814d402c068712b5096af8cdaa942160232dd Mon Sep 17 00:00:00 2001
From: Paul Asjes
Date: Wed, 6 Nov 2024 11:52:48 +0100
Subject: [PATCH 3/5] Clarify in README that you can use glob logic
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 8a6d607..491925c 100644
--- a/README.md
+++ b/README.md
@@ -290,7 +290,7 @@ The `signUpPaths` option can be passed to `authkitMiddleware` to specify paths t
import { authkitMiddleware } from '@workos-inc/authkit-nextjs';
export default authkitMiddleware({
- signUpPaths: ['/account/sign-up'],
+ signUpPaths: ['/account/sign-up', '/dashboard/:path*'],
});
```
From bbcb95ab59cbb46b328744e2506c9e9e4aceaee5 Mon Sep 17 00:00:00 2001
From: Paul Asjes
Date: Fri, 8 Nov 2024 11:57:38 +0100
Subject: [PATCH 4/5] Run the regex on single strings as well
---
src/session.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/session.ts b/src/session.ts
index f6ff87b..3bdf45c 100644
--- a/src/session.ts
+++ b/src/session.ts
@@ -363,7 +363,8 @@ function getScreenHint(signUpPaths: string[] | string | undefined, pathname: str
if (!signUpPaths) return 'sign-in';
if (!Array.isArray(signUpPaths)) {
- return signUpPaths.includes(pathname) ? 'sign-up' : 'sign-in';
+ const pathRegex = getMiddlewareAuthPathRegex(signUpPaths);
+ return pathRegex.exec(pathname) ? 'sign-up' : 'sign-in';
}
const screenHintPaths: string[] = signUpPaths.filter((pathGlob) => {
From 8930bbd3104126f326fb63765c86744902b84085 Mon Sep 17 00:00:00 2001
From: Paul Asjes
Date: Mon, 11 Nov 2024 15:07:19 +0100
Subject: [PATCH 5/5] Add sign up paths to readme
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 80f3b18..ed19004 100644
--- a/README.md
+++ b/README.md
@@ -108,6 +108,7 @@ The middleware can be configured with several options.
| `redirectUri` | `undefined` | Used in cases where you need your redirect URI to be set dynamically (e.g. Vercel preview deployments) |
| `middlewareAuth` | `undefined` | Used to configure middleware auth options. See [middleware auth](#middleware-auth) for more details. |
| `debug` | `false` | Enables debug logs. |
+| `signUpPaths` | `[]` | Used to specify paths that should use the 'sign-up' screen hint when redirecting to AuthKit. |
#### Custom redirect URI