Skip to content

Commit

Permalink
Add signUpPaths configuration (#123)
Browse files Browse the repository at this point in the history
* Add signUpPaths config

* Add regex logic and make it reusable

* Clarify in README that you can use glob logic

* Run the regex on single strings as well

* Add sign up paths to readme
  • Loading branch information
Paul Asjes authored Nov 11, 2024
1 parent 3984301 commit 8c225f2
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 4 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -297,6 +298,28 @@ 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', '/dashboard/:path*'],
});
```
### Debugging
To enable debug logs, initialize the middleware with the debug flag enabled.
```js
import { authkitMiddleware } from '@workos-inc/authkit-nextjs';
export default authkitMiddleware({ debug: true });
```
### Troubleshooting
#### NEXT_REDIRECT error when using try/catch blocks
Expand Down
1 change: 1 addition & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export interface AuthkitMiddlewareOptions {
debug?: boolean;
middlewareAuth?: AuthkitMiddlewareAuth;
redirectUri?: string;
signUpPaths?: string[];
}

export interface CookieOptions {
Expand Down
3 changes: 2 additions & 1 deletion src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
};
}
37 changes: 34 additions & 3 deletions src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)));

Expand All @@ -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.');
Expand All @@ -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
Expand Down Expand Up @@ -84,6 +91,7 @@ async function updateSession(
const redirectTo = await getAuthorizationUrl({
returnPathname: getReturnPathname(request.url),
redirectUri: redirectUri ?? WORKOS_REDIRECT_URI,
screenHint: getScreenHint(signUpPaths, request.nextUrl.pathname),
});

// Fall back to standard Response if NextResponse is not available.
Expand Down Expand Up @@ -236,10 +244,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(',');

redirect(await getAuthorizationUrl({ returnPathname }));
const pathname = new URL(url).pathname;
const screenHint = getScreenHint(signUpPaths, pathname);

const returnPathname = url && getReturnPathname(url);

redirect(await getAuthorizationUrl({ returnPathname, screenHint }));
}

async function withAuth(options?: { ensureSignedIn: false }): Promise<UserInfo | NoUserInfo>;
Expand Down Expand Up @@ -344,4 +359,20 @@ 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)) {
const pathRegex = getMiddlewareAuthPathRegex(signUpPaths);
return pathRegex.exec(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 };

0 comments on commit 8c225f2

Please sign in to comment.