Skip to content

Commit 7624f7c

Browse files
authored
fix: next request check for serverless environments (#14466)
* fix: next request check for serverless environments * chore: remove type assertion for next check
1 parent b29d394 commit 7624f7c

File tree

2 files changed

+139
-1
lines changed

2 files changed

+139
-1
lines changed

packages/adapter-nextjs/__tests__/auth/utils/predicates.test.ts

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,124 @@ describe('isNextRequest', () => {
3939

4040
expect(isNextRequest(request)).toBe(true);
4141
});
42+
43+
it('returns true when the request is like a next request', () => {
44+
const mockNextRequest = {
45+
nextUrl: {
46+
pathname: '/api/auth',
47+
search: '?param=value',
48+
searchParams: new URLSearchParams('param=value'),
49+
href: 'https://example.com/api/auth?param=value',
50+
},
51+
cookies: {
52+
get: jest.fn(),
53+
set: jest.fn(),
54+
delete: jest.fn(),
55+
},
56+
url: 'https://example.com/api/auth?param=value',
57+
headers: new Headers({
58+
'content-type': 'application/json',
59+
'user-agent': 'test-agent',
60+
}),
61+
method: 'POST',
62+
body: null,
63+
bodyUsed: false,
64+
};
65+
66+
expect(isNextRequest(mockNextRequest)).toBe(true);
67+
});
68+
69+
it('returns false for regular Request objects without NextRequest properties', () => {
70+
const regularRequest = {
71+
headers: new Headers(),
72+
method: 'GET',
73+
url: 'https://example.com',
74+
};
75+
76+
expect(isNextRequest(regularRequest)).toBe(false);
77+
});
78+
79+
it('returns false for objects with nextUrl but missing other required properties', () => {
80+
const incompleteObject = {
81+
nextUrl: {
82+
pathname: '/test',
83+
search: '',
84+
},
85+
};
86+
87+
expect(isNextRequest(incompleteObject)).toBe(false);
88+
});
89+
90+
it('returns false for objects with malformed nextUrl', () => {
91+
const malformedNextUrl = {
92+
nextUrl: 'not-an-object',
93+
cookies: {},
94+
headers: new Headers(),
95+
method: 'GET',
96+
url: 'https://example.com',
97+
};
98+
99+
expect(isNextRequest(malformedNextUrl)).toBe(false);
100+
});
101+
102+
it('returns false for objects with null nextUrl', () => {
103+
const nullNextUrl = {
104+
nextUrl: null,
105+
cookies: {},
106+
headers: new Headers(),
107+
method: 'GET',
108+
url: 'https://example.com',
109+
};
110+
111+
expect(isNextRequest(nullNextUrl)).toBe(false);
112+
});
113+
114+
it('returns false for objects missing cookies property', () => {
115+
const missingCookies = {
116+
nextUrl: {
117+
pathname: '/test',
118+
search: '',
119+
},
120+
headers: new Headers(),
121+
method: 'GET',
122+
url: 'https://example.com',
123+
};
124+
125+
expect(isNextRequest(missingCookies)).toBe(false);
126+
});
127+
128+
it('returns false for objects with non-string method', () => {
129+
const invalidMethod = {
130+
nextUrl: {
131+
pathname: '/test',
132+
search: '',
133+
},
134+
cookies: {},
135+
headers: new Headers(),
136+
method: 123,
137+
url: 'https://example.com',
138+
};
139+
140+
expect(isNextRequest(invalidMethod)).toBe(false);
141+
});
142+
143+
it('returns false for objects with non-string url', () => {
144+
const invalidUrl = {
145+
nextUrl: {
146+
pathname: '/test',
147+
search: '',
148+
},
149+
cookies: {},
150+
headers: new Headers(),
151+
method: 'GET',
152+
url: 123,
153+
};
154+
155+
expect(isNextRequest(invalidUrl)).toBe(false);
156+
});
157+
158+
it('returns false for null or undefined inputs', () => {
159+
expect(isNextRequest(null as any)).toBe(false);
160+
expect(isNextRequest(undefined as any)).toBe(false);
161+
});
42162
});

packages/adapter-nextjs/src/auth/utils/predicates.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,25 @@ import { AuthRoutesHandlerContext } from '../types';
1010
export function isNextRequest(request: object): request is NextRequest {
1111
// NextRequest extends the Web Request API with additional convenience methods.
1212
// Details: https://nextjs.org/docs/app/api-reference/functions/next-request#nexturl
13-
return request instanceof Request && 'nextUrl' in request;
13+
//
14+
// Use duck typing instead of instanceof to handle Lambda/serverless environments
15+
// where Request constructor references may differ between invocations
16+
17+
return (
18+
typeof request === 'object' &&
19+
request !== null &&
20+
// NextRequest-specific properties
21+
'nextUrl' in request &&
22+
typeof request.nextUrl === 'object' &&
23+
request.nextUrl !== null &&
24+
'cookies' in request &&
25+
// Basic Request API properties
26+
'url' in request &&
27+
typeof request.url === 'string' &&
28+
'headers' in request &&
29+
'method' in request &&
30+
typeof request.method === 'string'
31+
);
1432
}
1533

1634
// AuthRoutesHandlersContext is the 2nd parameter type for the API route handlers in the App Router

0 commit comments

Comments
 (0)