-
Notifications
You must be signed in to change notification settings - Fork 386
/
login.ts
346 lines (325 loc) · 9.86 KB
/
login.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
import { NextApiResponse, NextApiRequest } from 'next';
import { NextRequest, NextResponse } from 'next/server';
import {
AuthorizationParameters,
HandleLogin as BaseHandleLogin,
LoginOptions as BaseLoginOptions
} from '../auth0-session';
import toSafeRedirect from '../utils/url-helpers';
import { assertReqRes } from '../utils/assert';
import { GetConfig, NextConfig } from '../config';
import { HandlerErrorCause, LoginHandlerError } from '../utils/errors';
import { Auth0NextApiRequest, Auth0NextApiResponse, Auth0NextRequest, Auth0NextResponse } from '../http';
import { AppRouteHandlerFnContext, getHandler, OptionsProvider, Handler, AuthHandler } from './router-helpers';
/**
* Get login state hook for page router {@link GetLoginStatePageRoute} and app router {@link GetLoginStateAppRoute}.
*/
export type GetLoginState = GetLoginStatePageRoute | GetLoginStateAppRoute;
/**
* Use this to store additional state for the user before they visit the identity provider to log in.
*
* ```js
* // pages/api/auth/[auth0].js
* import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
*
* const getLoginState = (req, loginOptions) => {
* return { basket_id: getBasketId(req) };
* };
*
* export default handleAuth({
* login: handleLogin({ getLoginState })
* });
* ```
*
* @category Server
*/
export type GetLoginStatePageRoute = (req: NextApiRequest, options: LoginOptions) => { [key: string]: any };
/**
* Use this to store additional state for the user before they visit the identity provider to log in.
*
* ```js
* // app/api/auth/[auth0]/route.js
* import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
*
* const getLoginState = (req, loginOptions) => {
* return { basket_id: getBasketId(req) };
* };
*
* export default handleAuth({
* login: handleLogin({ getLoginState })
* });
* ```
*
* @category Server
*/
export type GetLoginStateAppRoute = (req: NextRequest, options: LoginOptions) => { [key: string]: any };
/**
* Authorization params to pass to the login handler.
*
* @category Server
*/
export interface AuthorizationParams extends Partial<AuthorizationParameters> {
/**
* The name of an OAuth2/social connection. Use it to directly show that
* identity provider's login page, skipping the Universal Login page itself.
* By default no connection is specified, so the Universal Login page will be displayed.
*
* ```js
* import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
*
* export default handleAuth({
* login: async (req, res) => {
* try {
* await handleLogin(req, res, {
* // Get the connection name from the Auth0 Dashboard
* authorizationParams: { connection: 'github' }
* });
* } catch (error) {
* console.error(error);
* }
* }
* });
* ```
*/
connection?: string;
/**
* Provider scopes for OAuth2/social connections, such as GitHub or Google.
*
* ```js
* import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
*
* export default handleAuth({
* login: async (req, res) => {
* try {
* await handleLogin(req, res, {
* authorizationParams: {
* connection: 'github',
* connection_scope: 'public_repo read:user'
* }
* });
* } catch (error) {
* console.error(error);
* }
* }
* });
* ```
*/
connection_scope?: string;
/**
* The invitation id to join an organization.
*
* To create a link for your user's to accept an organization invite, read the `invitation` and `organization`
* query params and pass them to the authorization server to log the user in:
*
* ```js
* // pages/api/invite.js
* import { handleLogin } from '@auth0/nextjs-auth0';
*
* export default async function invite(req, res) {
* try {
* const { invitation, organization } = req.query;
* if (!invitation) {
* res.status(400).end('Missing "invitation" parameter');
* }
* await handleLogin(req, res, {
* authorizationParams: {
* invitation,
* organization
* }
* });
* } catch (error) {
* res.status(error.status || 500).end();
* }
* } ;
* ```
*
* Your invite url can then take the format:
* `https://example.com/api/invite?invitation=invitation_id&organization=org_id_or_name`.
*/
invitation?: string;
/**
* This is useful to specify instead of {@link NextConfig.organization} when your app has multiple
* organizations. It should match {@link CallbackOptions.organization}.
*/
organization?: string;
/**
* Provides a hint to Auth0 as to what flow should be displayed. The default behavior is to show a
* login page but you can override this by passing 'signup' to show the signup page instead.
*
* This only affects the New Universal Login Experience.
*/
screen_hint?: string;
}
/**
* Options to customize the login handler.
*
* @see {@link HandleLogin}
*
* @category Server
*/
export interface LoginOptions {
/**
* Override the default {@link BaseConfig.authorizationParams authorizationParams}.
*/
authorizationParams?: AuthorizationParams;
/**
* URL to return to after login. Overrides the default in {@link BaseConfig.baseURL}.
*/
returnTo?: string;
/**
* Generate a unique state value for use during login transactions.
*/
getLoginState?: GetLoginState;
}
/**
* Options provider for the default login handler.
* Use this to generate options that depend on values from the request.
*
* @category Server
*/
export type LoginOptionsProvider = OptionsProvider<LoginOptions>;
/**
* Use this to customize the default login handler without overriding it.
* You can still override the handler if needed.
*
* @example Pass an options object
*
* ```js
* // pages/api/auth/[auth0].js
* import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
*
* export default handleAuth({
* login: handleLogin({
* authorizationParams: { connection: 'github' }
* })
* });
* ```
*
* @example Pass a function that receives the request and returns an options object
*
* ```js
* // pages/api/auth/[auth0].js
* import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
*
* export default handleAuth({
* login: handleLogin((req) => {
* return {
* authorizationParams: { connection: 'github' }
* };
* })
* });
* ```
*
* This is useful for generating options that depend on values from the request.
*
* @example Override the login handler
*
* ```js
* import { handleAuth, handleLogin } from '@auth0/nextjs-auth0';
*
* export default handleAuth({
* login: async (req, res) => {
* try {
* await handleLogin(req, res, {
* authorizationParams: { connection: 'github' }
* });
* } catch (error) {
* console.error(error);
* }
* }
* });
* ```
*
* @category Server
*/
export type HandleLogin = AuthHandler<LoginOptions>;
/**
* The handler for the `/api/auth/login` API route.
*
* @throws {@link HandlerError}
*
* @category Server
*/
export type LoginHandler = Handler<LoginOptions>;
/**
* @ignore
*/
export default function handleLoginFactory(handler: BaseHandleLogin, getConfig: GetConfig): HandleLogin {
const appRouteHandler = appRouteHandlerFactory(handler, getConfig);
const pageRouteHandler = pageRouteHandlerFactory(handler, getConfig);
return getHandler<LoginOptions>(appRouteHandler, pageRouteHandler) as HandleLogin;
}
/**
* @ignore
*/
const applyOptions = (
req: NextApiRequest | NextRequest,
options: LoginOptions,
dangerousReturnTo: string | undefined | null,
config: NextConfig
): BaseLoginOptions => {
let opts: BaseLoginOptions;
let getLoginState: GetLoginState | undefined;
// eslint-disable-next-line prefer-const
({ getLoginState, ...opts } = options);
if (dangerousReturnTo) {
const safeBaseUrl = new URL(options.authorizationParams?.redirect_uri || config.baseURL);
const returnTo = toSafeRedirect(dangerousReturnTo, safeBaseUrl);
opts = { ...opts, returnTo };
}
if (config.organization) {
opts = {
...opts,
authorizationParams: { organization: config.organization, ...opts.authorizationParams }
};
}
if (getLoginState) {
opts.getLoginState = (_opts) => (getLoginState as GetLoginState)(req as any, _opts as any);
}
return opts;
};
/**
* @ignore
*/
const appRouteHandlerFactory: (
handler: BaseHandleLogin,
getConfig: GetConfig
) => (req: NextRequest, ctx: AppRouteHandlerFnContext, options?: LoginOptions) => Promise<Response> | Response =
(handler, getConfig) =>
async (req, _ctx, options = {}) => {
try {
const auth0Req = new Auth0NextRequest(req);
const config = await getConfig(auth0Req);
const url = new URL(req.url);
const dangerousReturnTo = url.searchParams.get('returnTo');
const auth0Res = new Auth0NextResponse(new NextResponse());
await handler(auth0Req, auth0Res, applyOptions(req, options, dangerousReturnTo, config) as BaseLoginOptions);
return auth0Res.res;
} catch (e) {
throw new LoginHandlerError(e as HandlerErrorCause);
}
};
/**
* @ignore
*/
const pageRouteHandlerFactory: (
handler: BaseHandleLogin,
getConfig: GetConfig
) => (req: NextApiRequest, res: NextApiResponse, options?: LoginOptions) => Promise<void> | void =
(handler, getConfig) =>
async (req, res, options = {}) => {
try {
const auth0Req = new Auth0NextApiRequest(req);
const config = await getConfig(auth0Req);
assertReqRes(req, res);
const dangerousReturnTo =
req.query.returnTo && Array.isArray(req.query.returnTo) ? req.query.returnTo[0] : req.query.returnTo;
return await handler(
auth0Req,
new Auth0NextApiResponse(res),
applyOptions(req, options, dangerousReturnTo, config) as BaseLoginOptions
);
} catch (e) {
throw new LoginHandlerError(e as HandlerErrorCause);
}
};