forked from clerk/javascript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathauthenticateRequest.ts
More file actions
125 lines (110 loc) · 4.93 KB
/
Copy pathauthenticateRequest.ts
File metadata and controls
125 lines (110 loc) · 4.93 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
117
118
119
120
121
122
123
124
125
import type { RequestState } from '@clerk/backend';
import { buildRequestUrl, constants, createIsomorphicRequest } from '@clerk/backend';
import { handleValueOrFn } from '@clerk/shared/handleValueOrFn';
import { isHttpOrHttps, isProxyUrlRelative, isValidProxyUrl } from '@clerk/shared/proxy';
import type { ServerResponse } from 'http';
import type { AuthenticateRequestParams, ClerkClient } from './types';
import { loadApiEnv, loadClientEnv } from './utils';
export async function loadInterstitial({
clerkClient,
requestState,
}: {
clerkClient: ClerkClient;
requestState: RequestState;
}) {
const { clerkJSVersion, clerkJSUrl } = loadClientEnv();
/**
* When publishable key or frontendApi is present utilize the localInterstitial method
* and avoid the extra network call
*/
if (requestState.publishableKey || requestState.frontendApi) {
return clerkClient.localInterstitial({
// Use frontendApi only when legacy frontendApi is used to avoid showing deprecation warning
// since the requestState always contains the frontendApi constructed by publishableKey.
frontendApi: requestState.publishableKey ? '' : requestState.frontendApi,
publishableKey: requestState.publishableKey,
proxyUrl: requestState.proxyUrl,
signInUrl: requestState.signInUrl,
isSatellite: requestState.isSatellite,
domain: requestState.domain,
clerkJSVersion,
clerkJSUrl,
});
}
return await clerkClient.remotePrivateInterstitial();
}
export const authenticateRequest = (opts: AuthenticateRequestParams) => {
const { clerkClient, apiKey, secretKey, frontendApi, publishableKey, req, options } = opts;
const { jwtKey, authorizedParties, audience } = options || {};
const env = { ...loadApiEnv(), ...loadClientEnv() };
const isomorphicRequest = createIsomorphicRequest((Request, Headers) => {
const headers = Object.keys(req.headers).reduce((acc, key) => Object.assign(acc, { [key]: req?.headers[key] }), {});
// @ts-ignore Optimistic attempt to get the protocol in case
// req extends IncomingMessage in a useful way. No guarantee
// it'll work.
const protocol = req.connection?.encrypted ? 'https' : 'http';
const dummyOriginReqUrl = new URL(req.url || '', `${protocol}://clerk-dummy`);
return new Request(dummyOriginReqUrl, {
method: req.method,
headers: new Headers(headers),
});
});
const requestUrl = buildRequestUrl(isomorphicRequest);
const isSatellite = handleValueOrFn(options?.isSatellite, requestUrl, env.isSatellite);
const domain = handleValueOrFn(options?.domain, requestUrl) || env.domain;
const signInUrl = options?.signInUrl || env.signInUrl;
const proxyUrl = absoluteProxyUrl(
handleValueOrFn(options?.proxyUrl, requestUrl, env.proxyUrl),
requestUrl.toString(),
);
if (isSatellite && !proxyUrl && !domain) {
throw new Error(satelliteAndMissingProxyUrlAndDomain);
}
if (isSatellite && !isHttpOrHttps(signInUrl) && isDevelopmentFromApiKey(secretKey || apiKey || '')) {
throw new Error(satelliteAndMissingSignInUrl);
}
return clerkClient.authenticateRequest({
audience,
apiKey,
secretKey,
frontendApi,
publishableKey,
jwtKey,
authorizedParties,
proxyUrl,
isSatellite,
domain,
signInUrl,
request: isomorphicRequest,
});
};
export const handleUnknownCase = (res: ServerResponse, requestState: RequestState) => {
if (requestState.isUnknown) {
res.writeHead(401, { 'Content-Type': 'text/html' });
res.end();
}
};
export const handleInterstitialCase = (res: ServerResponse, requestState: RequestState, interstitial: string) => {
if (requestState.isInterstitial) {
res.writeHead(401, { 'Content-Type': 'text/html' });
res.end(interstitial);
}
};
export const decorateResponseWithObservabilityHeaders = (res: ServerResponse, requestState: RequestState) => {
requestState.message && res.setHeader(constants.Headers.AuthMessage, encodeURIComponent(requestState.message));
requestState.reason && res.setHeader(constants.Headers.AuthReason, encodeURIComponent(requestState.reason));
requestState.status && res.setHeader(constants.Headers.AuthStatus, encodeURIComponent(requestState.status));
};
const isDevelopmentFromApiKey = (apiKey: string): boolean =>
apiKey.startsWith('test_') || apiKey.startsWith('sk_test_');
const absoluteProxyUrl = (relativeOrAbsoluteUrl: string, baseUrl: string): string => {
if (!relativeOrAbsoluteUrl || !isValidProxyUrl(relativeOrAbsoluteUrl) || !isProxyUrlRelative(relativeOrAbsoluteUrl)) {
return relativeOrAbsoluteUrl;
}
return new URL(relativeOrAbsoluteUrl, baseUrl).toString();
};
const satelliteAndMissingProxyUrlAndDomain =
'Missing domain and proxyUrl. A satellite application needs to specify a domain or a proxyUrl';
const satelliteAndMissingSignInUrl = `
Invalid signInUrl. A satellite application requires a signInUrl for development instances.
Check if signInUrl is missing from your configuration or if it is not an absolute URL.`;