forked from clerk/javascript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkeys.ts
More file actions
126 lines (101 loc) · 4.18 KB
/
Copy pathkeys.ts
File metadata and controls
126 lines (101 loc) · 4.18 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
126
import type { PublishableKey } from '@clerk/types';
import { DEV_OR_STAGING_SUFFIXES, LEGACY_DEV_INSTANCE_SUFFIXES } from './constants';
import { isomorphicAtob } from './isomorphicAtob';
import { isomorphicBtoa } from './isomorphicBtoa';
type ParsePublishableKeyOptions = {
fatal?: boolean;
domain?: string;
proxyUrl?: string;
};
const PUBLISHABLE_KEY_LIVE_PREFIX = 'pk_live_';
const PUBLISHABLE_KEY_TEST_PREFIX = 'pk_test_';
// This regex matches the publishable like frontend API keys (e.g. foo-bar-13.clerk.accounts.dev)
const PUBLISHABLE_FRONTEND_API_DEV_REGEX = /^(([a-z]+)-){2}([0-9]{1,2})\.clerk\.accounts([a-z.]*)(dev|com)$/i;
export function buildPublishableKey(frontendApi: string): string {
const isDevKey =
PUBLISHABLE_FRONTEND_API_DEV_REGEX.test(frontendApi) ||
(frontendApi.startsWith('clerk.') && LEGACY_DEV_INSTANCE_SUFFIXES.some(s => frontendApi.endsWith(s)));
const keyPrefix = isDevKey ? PUBLISHABLE_KEY_TEST_PREFIX : PUBLISHABLE_KEY_LIVE_PREFIX;
return `${keyPrefix}${isomorphicBtoa(`${frontendApi}$`)}`;
}
export function parsePublishableKey(
key: string | undefined,
options: ParsePublishableKeyOptions & { fatal: true },
): PublishableKey;
export function parsePublishableKey(
key: string | undefined,
options?: ParsePublishableKeyOptions,
): PublishableKey | null;
export function parsePublishableKey(
key: string | undefined,
options: { fatal?: boolean; domain?: string; proxyUrl?: string } = {},
): PublishableKey | null {
key = key || '';
if (!key || !isPublishableKey(key)) {
if (options.fatal) {
throw new Error('Publishable key not valid.');
}
return null;
}
const instanceType = key.startsWith(PUBLISHABLE_KEY_LIVE_PREFIX) ? 'production' : 'development';
let frontendApi = isomorphicAtob(key.split('_')[2]);
// TODO(@dimkl): validate packages/clerk-js/src/utils/instance.ts
frontendApi = frontendApi.slice(0, -1);
if (options.proxyUrl) {
frontendApi = options.proxyUrl;
} else if (instanceType !== 'development' && options.domain) {
frontendApi = `clerk.${options.domain}`;
}
return {
instanceType,
frontendApi,
};
}
export function isPublishableKey(key: string) {
key = key || '';
const hasValidPrefix = key.startsWith(PUBLISHABLE_KEY_LIVE_PREFIX) || key.startsWith(PUBLISHABLE_KEY_TEST_PREFIX);
const hasValidFrontendApiPostfix = isomorphicAtob(key.split('_')[2] || '').endsWith('$');
return hasValidPrefix && hasValidFrontendApiPostfix;
}
export function createDevOrStagingUrlCache() {
const devOrStagingUrlCache = new Map<string, boolean>();
return {
isDevOrStagingUrl: (url: string | URL): boolean => {
if (!url) {
return false;
}
const hostname = typeof url === 'string' ? url : url.hostname;
let res = devOrStagingUrlCache.get(hostname);
if (res === undefined) {
res = DEV_OR_STAGING_SUFFIXES.some(s => hostname.endsWith(s));
devOrStagingUrlCache.set(hostname, res);
}
return res;
},
};
}
export function isDevelopmentFromPublishableKey(apiKey: string): boolean {
return apiKey.startsWith('test_') || apiKey.startsWith('pk_test_');
}
export function isProductionFromPublishableKey(apiKey: string): boolean {
return apiKey.startsWith('live_') || apiKey.startsWith('pk_live_');
}
export function isDevelopmentFromSecretKey(apiKey: string): boolean {
return apiKey.startsWith('test_') || apiKey.startsWith('sk_test_');
}
export function isProductionFromSecretKey(apiKey: string): boolean {
return apiKey.startsWith('live_') || apiKey.startsWith('sk_live_');
}
export async function getCookieSuffix(
publishableKey: string,
subtle: SubtleCrypto = globalThis.crypto.subtle,
): Promise<string> {
const data = new TextEncoder().encode(publishableKey);
const digest = await subtle.digest('sha-1', data);
const stringDigest = String.fromCharCode(...new Uint8Array(digest));
// Base 64 Encoding with URL and Filename Safe Alphabet: https://datatracker.ietf.org/doc/html/rfc4648#section-5
return isomorphicBtoa(stringDigest).replace(/\+/gi, '-').replace(/\//gi, '_').substring(0, 8);
}
export const getSuffixedCookieName = (cookieName: string, cookieSuffix: string): string => {
return `${cookieName}_${cookieSuffix}`;
};