Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export const GET = handleAUth({
| `returnPathname` | `/` | The pathname to redirect the user to after signing in |
| `baseURL` | `undefined` | The base URL to use for the redirect URI instead of the one in the request. Useful if the app is being run in a container like docker where the hostname can be different from the one in the request |
| `onSuccess` | `undefined` | A function that receives successful authentication data and can be used for side-effects like persisting tokens |
| `onError` | `undefined` | A function that can receive the error and the request and handle the error in its own way. |

### Middleware

Expand Down
20 changes: 20 additions & 0 deletions __tests__/authkit-callback-route.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,26 @@ describe('authkit-callback-route', () => {
expect(data.error.message).toBe('Something went wrong');
});

it('should handle authentication failure with custom onError handler', async () => {
// Mock authentication failure
jest.mocked(workos.userManagement.authenticateWithCode).mockRejectedValue('Auth failed');
request.nextUrl.searchParams.set('code', 'invalid-code');

const handler = handleAuth({
onError: ({ error }) => {
return new Response(JSON.stringify({ error: { message: 'Custom error' } }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
});
},
});
const response = await handler(request);

expect(response.status).toBe(500);
const data = await response.json();
expect(data.error.message).toBe('Custom error');
});

it('should handle missing code parameter', async () => {
const handler = handleAuth();
const response = await handler(request);
Expand Down
20 changes: 12 additions & 8 deletions src/authkit-callback-route.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { NextRequest } from 'next/server';
import { cookies } from 'next/headers';
import { workos } from './workos.js';
import { NextRequest } from 'next/server';
import { getCookieOptions } from './cookie.js';
import { WORKOS_CLIENT_ID, WORKOS_COOKIE_NAME } from './env-variables.js';
import { HandleAuthOptions } from './interfaces.js';
import { encryptSession } from './session.js';
import { errorResponseWithFallback, redirectWithFallback } from './utils.js';
import { getCookieOptions } from './cookie.js';
import { HandleAuthOptions } from './interfaces.js';
import { workos } from './workos.js';

export function handleAuth(options: HandleAuthOptions = {}) {
const { returnPathname: returnPathnameOption = '/', baseURL, onSuccess } = options;
const { returnPathname: returnPathnameOption = '/', baseURL, onSuccess, onError } = options;

// Throw early if baseURL is provided but invalid
if (baseURL) {
Expand Down Expand Up @@ -83,14 +83,18 @@ export function handleAuth(options: HandleAuthOptions = {}) {

console.error(errorRes);

return errorResponse();
return errorResponse(request, error);
}
}

return errorResponse();
return errorResponse(request);
};

function errorResponse() {
function errorResponse(request: NextRequest, error?: unknown) {
if (onError) {
return onError({ error, request });
}

return errorResponseWithFallback({
error: {
message: 'Something went wrong',
Expand Down
2 changes: 2 additions & 0 deletions src/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { OauthTokens, User } from '@workos-inc/node';
import { type NextRequest } from 'next/server';

export interface HandleAuthOptions {
returnPathname?: string;
baseURL?: string;
onSuccess?: (data: HandleAuthSuccessData) => void | Promise<void>;
onError?: (params: { error?: unknown; request: NextRequest }) => Response | Promise<Response>;
}

export interface HandleAuthSuccessData extends Session {
Expand Down