-
Notifications
You must be signed in to change notification settings - Fork 449
Expand file tree
/
Copy pathclerkMiddleware.ts
More file actions
116 lines (101 loc) · 3.57 KB
/
clerkMiddleware.ts
File metadata and controls
116 lines (101 loc) · 3.57 KB
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
import type { AuthObject } from '@clerk/backend';
import type { RequestState } from '@clerk/backend/internal';
import { AuthStatus, constants, createClerkRequest } from '@clerk/backend/internal';
import { handleNetlifyCacheHeaders } from '@clerk/shared/netlifyCacheHandler';
import type { PendingSessionOptions } from '@clerk/shared/types';
import type { MiddlewareFunction } from 'react-router';
import { createContext } from 'react-router';
import { clerkClient } from './clerkClient';
import { resolveKeysWithKeylessFallback } from './keyless/utils';
import { loadOptions } from './loadOptions';
import type { ClerkMiddlewareOptions } from './types';
import { patchRequest } from './utils';
export const authFnContext = createContext<((options?: PendingSessionOptions) => AuthObject) | null>(null);
export const requestStateContext = createContext<RequestState<any> | null>(null);
/**
* Middleware that integrates Clerk authentication into your React Router application.
* It checks the request's cookies and headers for a session JWT and, if found,
* attaches the Auth object to a context.
*
* @example
* // react-router.config.ts
* export default {
* future: {
* v8_middleware: true,
* },
* }
*
* // root.tsx
* export const middleware: Route.MiddlewareFunction[] = [clerkMiddleware()]
*/
export const clerkMiddleware = (options?: ClerkMiddlewareOptions): MiddlewareFunction<Response> => {
return async (args, next) => {
const clerkRequest = createClerkRequest(patchRequest(args.request));
const loadedOptions = loadOptions(args, options);
const {
publishableKey,
secretKey,
claimUrl: __keylessClaimUrl,
apiKeysUrl: __keylessApiKeysUrl,
} = await resolveKeysWithKeylessFallback(loadedOptions.publishableKey, loadedOptions.secretKey, args, options);
if (publishableKey) {
loadedOptions.publishableKey = publishableKey;
}
if (secretKey) {
loadedOptions.secretKey = secretKey;
}
// Pick only the properties needed by authenticateRequest.
// Used when manually providing options to the middleware.
const {
apiUrl,
jwtKey,
proxyUrl,
isSatellite,
domain,
machineSecretKey,
audience,
authorizedParties,
signInUrl,
signUpUrl,
organizationSyncOptions,
} = loadedOptions;
const requestState = await clerkClient(args, options).authenticateRequest(clerkRequest, {
apiUrl,
secretKey: loadedOptions.secretKey,
jwtKey,
proxyUrl,
isSatellite,
domain,
publishableKey: loadedOptions.publishableKey,
machineSecretKey,
audience,
authorizedParties,
organizationSyncOptions,
signInUrl,
signUpUrl,
acceptsToken: 'any',
});
Object.assign(requestState, {
__keylessClaimUrl,
__keylessApiKeysUrl,
});
await handleNetlifyCacheHeaders(requestState);
const locationHeader = requestState.headers.get(constants.Headers.Location);
if (locationHeader) {
// Trigger a handshake redirect
return new Response(null, { status: 307, headers: requestState.headers });
}
if (requestState.status === AuthStatus.Handshake) {
throw new Error('Clerk: handshake status without redirect');
}
args.context.set(authFnContext, (opts?: PendingSessionOptions) => requestState.toAuth(opts));
args.context.set(requestStateContext, requestState);
const response = await next();
if (requestState.headers) {
requestState.headers.forEach((value, key) => {
response.headers.append(key, value);
});
}
return response;
};
};