Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add signUpPaths configuration #123

Merged
merged 7 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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', '/dashboard/:path*'],
});
```

### Debugging

To enable debug logs, initialize the middleware with the debug flag enabled.
Expand Down
1 change: 1 addition & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,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 };