@supabase/auth-js 2.58.1-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +148 -0
- package/dist/main/AuthAdminApi.d.ts +4 -0
- package/dist/main/AuthAdminApi.d.ts.map +1 -0
- package/dist/main/AuthAdminApi.js +9 -0
- package/dist/main/AuthAdminApi.js.map +1 -0
- package/dist/main/AuthClient.d.ts +4 -0
- package/dist/main/AuthClient.d.ts.map +1 -0
- package/dist/main/AuthClient.js +9 -0
- package/dist/main/AuthClient.js.map +1 -0
- package/dist/main/GoTrueAdminApi.d.ts +99 -0
- package/dist/main/GoTrueAdminApi.d.ts.map +1 -0
- package/dist/main/GoTrueAdminApi.js +278 -0
- package/dist/main/GoTrueAdminApi.js.map +1 -0
- package/dist/main/GoTrueClient.d.ts +531 -0
- package/dist/main/GoTrueClient.d.ts.map +1 -0
- package/dist/main/GoTrueClient.js +2564 -0
- package/dist/main/GoTrueClient.js.map +1 -0
- package/dist/main/index.d.ts +9 -0
- package/dist/main/index.d.ts.map +1 -0
- package/dist/main/index.js +36 -0
- package/dist/main/index.js.map +1 -0
- package/dist/main/lib/base64url.d.ts +76 -0
- package/dist/main/lib/base64url.d.ts.map +1 -0
- package/dist/main/lib/base64url.js +269 -0
- package/dist/main/lib/base64url.js.map +1 -0
- package/dist/main/lib/constants.d.ts +26 -0
- package/dist/main/lib/constants.d.ts.map +1 -0
- package/dist/main/lib/constants.js +31 -0
- package/dist/main/lib/constants.js.map +1 -0
- package/dist/main/lib/error-codes.d.ts +7 -0
- package/dist/main/lib/error-codes.d.ts.map +1 -0
- package/dist/main/lib/error-codes.js +3 -0
- package/dist/main/lib/error-codes.js.map +1 -0
- package/dist/main/lib/errors.d.ts +100 -0
- package/dist/main/lib/errors.d.ts.map +1 -0
- package/dist/main/lib/errors.js +137 -0
- package/dist/main/lib/errors.js.map +1 -0
- package/dist/main/lib/fetch.d.ts +34 -0
- package/dist/main/lib/fetch.d.ts.map +1 -0
- package/dist/main/lib/fetch.js +194 -0
- package/dist/main/lib/fetch.js.map +1 -0
- package/dist/main/lib/helpers.d.ts +67 -0
- package/dist/main/lib/helpers.d.ts.map +1 -0
- package/dist/main/lib/helpers.js +388 -0
- package/dist/main/lib/helpers.js.map +1 -0
- package/dist/main/lib/local-storage.d.ts +9 -0
- package/dist/main/lib/local-storage.d.ts.map +1 -0
- package/dist/main/lib/local-storage.js +21 -0
- package/dist/main/lib/local-storage.js.map +1 -0
- package/dist/main/lib/locks.d.ts +64 -0
- package/dist/main/lib/locks.d.ts.map +1 -0
- package/dist/main/lib/locks.js +187 -0
- package/dist/main/lib/locks.js.map +1 -0
- package/dist/main/lib/polyfills.d.ts +5 -0
- package/dist/main/lib/polyfills.d.ts.map +1 -0
- package/dist/main/lib/polyfills.js +29 -0
- package/dist/main/lib/polyfills.js.map +1 -0
- package/dist/main/lib/types.d.ts +1130 -0
- package/dist/main/lib/types.d.ts.map +1 -0
- package/dist/main/lib/types.js +22 -0
- package/dist/main/lib/types.js.map +1 -0
- package/dist/main/lib/version.d.ts +2 -0
- package/dist/main/lib/version.d.ts.map +1 -0
- package/dist/main/lib/version.js +11 -0
- package/dist/main/lib/version.js.map +1 -0
- package/dist/main/lib/web3/ethereum.d.ts +96 -0
- package/dist/main/lib/web3/ethereum.d.ts.map +1 -0
- package/dist/main/lib/web3/ethereum.js +66 -0
- package/dist/main/lib/web3/ethereum.js.map +1 -0
- package/dist/main/lib/web3/solana.d.ts +160 -0
- package/dist/main/lib/web3/solana.d.ts.map +1 -0
- package/dist/main/lib/web3/solana.js +4 -0
- package/dist/main/lib/web3/solana.js.map +1 -0
- package/dist/main/lib/webauthn.d.ts +274 -0
- package/dist/main/lib/webauthn.d.ts.map +1 -0
- package/dist/main/lib/webauthn.dom.d.ts +583 -0
- package/dist/main/lib/webauthn.dom.d.ts.map +1 -0
- package/dist/main/lib/webauthn.dom.js +4 -0
- package/dist/main/lib/webauthn.dom.js.map +1 -0
- package/dist/main/lib/webauthn.errors.d.ts +80 -0
- package/dist/main/lib/webauthn.errors.d.ts.map +1 -0
- package/dist/main/lib/webauthn.errors.js +265 -0
- package/dist/main/lib/webauthn.errors.js.map +1 -0
- package/dist/main/lib/webauthn.js +702 -0
- package/dist/main/lib/webauthn.js.map +1 -0
- package/dist/module/AuthAdminApi.d.ts +4 -0
- package/dist/module/AuthAdminApi.d.ts.map +1 -0
- package/dist/module/AuthAdminApi.js +4 -0
- package/dist/module/AuthAdminApi.js.map +1 -0
- package/dist/module/AuthClient.d.ts +4 -0
- package/dist/module/AuthClient.d.ts.map +1 -0
- package/dist/module/AuthClient.js +4 -0
- package/dist/module/AuthClient.js.map +1 -0
- package/dist/module/GoTrueAdminApi.d.ts +99 -0
- package/dist/module/GoTrueAdminApi.d.ts.map +1 -0
- package/dist/module/GoTrueAdminApi.js +275 -0
- package/dist/module/GoTrueAdminApi.js.map +1 -0
- package/dist/module/GoTrueClient.d.ts +531 -0
- package/dist/module/GoTrueClient.d.ts.map +1 -0
- package/dist/module/GoTrueClient.js +2559 -0
- package/dist/module/GoTrueClient.js.map +1 -0
- package/dist/module/index.d.ts +9 -0
- package/dist/module/index.d.ts.map +1 -0
- package/dist/module/index.js +9 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/lib/base64url.d.ts +76 -0
- package/dist/module/lib/base64url.d.ts.map +1 -0
- package/dist/module/lib/base64url.js +257 -0
- package/dist/module/lib/base64url.js.map +1 -0
- package/dist/module/lib/constants.d.ts +26 -0
- package/dist/module/lib/constants.d.ts.map +1 -0
- package/dist/module/lib/constants.js +28 -0
- package/dist/module/lib/constants.js.map +1 -0
- package/dist/module/lib/error-codes.d.ts +7 -0
- package/dist/module/lib/error-codes.d.ts.map +1 -0
- package/dist/module/lib/error-codes.js +2 -0
- package/dist/module/lib/error-codes.js.map +1 -0
- package/dist/module/lib/errors.d.ts +100 -0
- package/dist/module/lib/errors.d.ts.map +1 -0
- package/dist/module/lib/errors.js +116 -0
- package/dist/module/lib/errors.js.map +1 -0
- package/dist/module/lib/fetch.d.ts +34 -0
- package/dist/module/lib/fetch.d.ts.map +1 -0
- package/dist/module/lib/fetch.js +184 -0
- package/dist/module/lib/fetch.js.map +1 -0
- package/dist/module/lib/helpers.d.ts +67 -0
- package/dist/module/lib/helpers.d.ts.map +1 -0
- package/dist/module/lib/helpers.js +329 -0
- package/dist/module/lib/helpers.js.map +1 -0
- package/dist/module/lib/local-storage.d.ts +9 -0
- package/dist/module/lib/local-storage.d.ts.map +1 -0
- package/dist/module/lib/local-storage.js +18 -0
- package/dist/module/lib/local-storage.js.map +1 -0
- package/dist/module/lib/locks.d.ts +64 -0
- package/dist/module/lib/locks.d.ts.map +1 -0
- package/dist/module/lib/locks.js +179 -0
- package/dist/module/lib/locks.js.map +1 -0
- package/dist/module/lib/polyfills.d.ts +5 -0
- package/dist/module/lib/polyfills.d.ts.map +1 -0
- package/dist/module/lib/polyfills.js +26 -0
- package/dist/module/lib/polyfills.js.map +1 -0
- package/dist/module/lib/types.d.ts +1130 -0
- package/dist/module/lib/types.d.ts.map +1 -0
- package/dist/module/lib/types.js +19 -0
- package/dist/module/lib/types.js.map +1 -0
- package/dist/module/lib/version.d.ts +2 -0
- package/dist/module/lib/version.d.ts.map +1 -0
- package/dist/module/lib/version.js +8 -0
- package/dist/module/lib/version.js.map +1 -0
- package/dist/module/lib/web3/ethereum.d.ts +96 -0
- package/dist/module/lib/web3/ethereum.d.ts.map +1 -0
- package/dist/module/lib/web3/ethereum.js +60 -0
- package/dist/module/lib/web3/ethereum.js.map +1 -0
- package/dist/module/lib/web3/solana.d.ts +160 -0
- package/dist/module/lib/web3/solana.d.ts.map +1 -0
- package/dist/module/lib/web3/solana.js +3 -0
- package/dist/module/lib/web3/solana.js.map +1 -0
- package/dist/module/lib/webauthn.d.ts +274 -0
- package/dist/module/lib/webauthn.d.ts.map +1 -0
- package/dist/module/lib/webauthn.dom.d.ts +583 -0
- package/dist/module/lib/webauthn.dom.d.ts.map +1 -0
- package/dist/module/lib/webauthn.dom.js +3 -0
- package/dist/module/lib/webauthn.dom.js.map +1 -0
- package/dist/module/lib/webauthn.errors.d.ts +80 -0
- package/dist/module/lib/webauthn.errors.d.ts.map +1 -0
- package/dist/module/lib/webauthn.errors.js +257 -0
- package/dist/module/lib/webauthn.errors.js.map +1 -0
- package/dist/module/lib/webauthn.js +685 -0
- package/dist/module/lib/webauthn.js.map +1 -0
- package/package.json +49 -0
- package/src/AuthAdminApi.ts +5 -0
- package/src/AuthClient.ts +5 -0
- package/src/GoTrueAdminApi.ts +352 -0
- package/src/GoTrueClient.ts +3483 -0
- package/src/index.ts +13 -0
- package/src/lib/base64url.ts +308 -0
- package/src/lib/constants.ts +34 -0
- package/src/lib/error-codes.ts +90 -0
- package/src/lib/errors.ts +165 -0
- package/src/lib/fetch.ts +283 -0
- package/src/lib/helpers.ts +416 -0
- package/src/lib/local-storage.ts +21 -0
- package/src/lib/locks.ts +225 -0
- package/src/lib/polyfills.ts +23 -0
- package/src/lib/types.ts +1450 -0
- package/src/lib/version.ts +7 -0
- package/src/lib/web3/ethereum.ts +184 -0
- package/src/lib/web3/solana.ts +186 -0
- package/src/lib/webauthn.dom.ts +636 -0
- package/src/lib/webauthn.errors.ts +317 -0
- package/src/lib/webauthn.ts +929 -0
package/src/lib/fetch.ts
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { API_VERSIONS, API_VERSION_HEADER_NAME } from './constants'
|
|
2
|
+
import { expiresAt, looksLikeFetchResponse, parseResponseAPIVersion } from './helpers'
|
|
3
|
+
import {
|
|
4
|
+
AuthResponse,
|
|
5
|
+
AuthResponsePassword,
|
|
6
|
+
SSOResponse,
|
|
7
|
+
GenerateLinkProperties,
|
|
8
|
+
GenerateLinkResponse,
|
|
9
|
+
User,
|
|
10
|
+
UserResponse,
|
|
11
|
+
} from './types'
|
|
12
|
+
import {
|
|
13
|
+
AuthApiError,
|
|
14
|
+
AuthRetryableFetchError,
|
|
15
|
+
AuthWeakPasswordError,
|
|
16
|
+
AuthUnknownError,
|
|
17
|
+
AuthSessionMissingError,
|
|
18
|
+
} from './errors'
|
|
19
|
+
|
|
20
|
+
export type Fetch = typeof fetch
|
|
21
|
+
|
|
22
|
+
export interface FetchOptions {
|
|
23
|
+
headers?: {
|
|
24
|
+
[key: string]: string
|
|
25
|
+
}
|
|
26
|
+
noResolveJson?: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface FetchParameters {
|
|
30
|
+
signal?: AbortSignal
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type RequestMethodType = 'GET' | 'POST' | 'PUT' | 'DELETE'
|
|
34
|
+
|
|
35
|
+
const _getErrorMessage = (err: any): string =>
|
|
36
|
+
err.msg || err.message || err.error_description || err.error || JSON.stringify(err)
|
|
37
|
+
|
|
38
|
+
const NETWORK_ERROR_CODES = [502, 503, 504]
|
|
39
|
+
|
|
40
|
+
export async function handleError(error: unknown) {
|
|
41
|
+
if (!looksLikeFetchResponse(error)) {
|
|
42
|
+
throw new AuthRetryableFetchError(_getErrorMessage(error), 0)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (NETWORK_ERROR_CODES.includes(error.status)) {
|
|
46
|
+
// status in 500...599 range - server had an error, request might be retryed.
|
|
47
|
+
throw new AuthRetryableFetchError(_getErrorMessage(error), error.status)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let data: any
|
|
51
|
+
try {
|
|
52
|
+
data = await error.json()
|
|
53
|
+
} catch (e: any) {
|
|
54
|
+
throw new AuthUnknownError(_getErrorMessage(e), e)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let errorCode: string | undefined = undefined
|
|
58
|
+
|
|
59
|
+
const responseAPIVersion = parseResponseAPIVersion(error)
|
|
60
|
+
if (
|
|
61
|
+
responseAPIVersion &&
|
|
62
|
+
responseAPIVersion.getTime() >= API_VERSIONS['2024-01-01'].timestamp &&
|
|
63
|
+
typeof data === 'object' &&
|
|
64
|
+
data &&
|
|
65
|
+
typeof data.code === 'string'
|
|
66
|
+
) {
|
|
67
|
+
errorCode = data.code
|
|
68
|
+
} else if (typeof data === 'object' && data && typeof data.error_code === 'string') {
|
|
69
|
+
errorCode = data.error_code
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!errorCode) {
|
|
73
|
+
// Legacy support for weak password errors, when there were no error codes
|
|
74
|
+
if (
|
|
75
|
+
typeof data === 'object' &&
|
|
76
|
+
data &&
|
|
77
|
+
typeof data.weak_password === 'object' &&
|
|
78
|
+
data.weak_password &&
|
|
79
|
+
Array.isArray(data.weak_password.reasons) &&
|
|
80
|
+
data.weak_password.reasons.length &&
|
|
81
|
+
data.weak_password.reasons.reduce((a: boolean, i: any) => a && typeof i === 'string', true)
|
|
82
|
+
) {
|
|
83
|
+
throw new AuthWeakPasswordError(
|
|
84
|
+
_getErrorMessage(data),
|
|
85
|
+
error.status,
|
|
86
|
+
data.weak_password.reasons
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
} else if (errorCode === 'weak_password') {
|
|
90
|
+
throw new AuthWeakPasswordError(
|
|
91
|
+
_getErrorMessage(data),
|
|
92
|
+
error.status,
|
|
93
|
+
data.weak_password?.reasons || []
|
|
94
|
+
)
|
|
95
|
+
} else if (errorCode === 'session_not_found') {
|
|
96
|
+
// The `session_id` inside the JWT does not correspond to a row in the
|
|
97
|
+
// `sessions` table. This usually means the user has signed out, has been
|
|
98
|
+
// deleted, or their session has somehow been terminated.
|
|
99
|
+
throw new AuthSessionMissingError()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
throw new AuthApiError(_getErrorMessage(data), error.status || 500, errorCode)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const _getRequestParams = (
|
|
106
|
+
method: RequestMethodType,
|
|
107
|
+
options?: FetchOptions,
|
|
108
|
+
parameters?: FetchParameters,
|
|
109
|
+
body?: object
|
|
110
|
+
) => {
|
|
111
|
+
const params: { [k: string]: any } = { method, headers: options?.headers || {} }
|
|
112
|
+
|
|
113
|
+
if (method === 'GET') {
|
|
114
|
+
return params
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
params.headers = { 'Content-Type': 'application/json;charset=UTF-8', ...options?.headers }
|
|
118
|
+
params.body = JSON.stringify(body)
|
|
119
|
+
return { ...params, ...parameters }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
interface GotrueRequestOptions extends FetchOptions {
|
|
123
|
+
jwt?: string
|
|
124
|
+
redirectTo?: string
|
|
125
|
+
body?: object
|
|
126
|
+
query?: { [key: string]: string }
|
|
127
|
+
/**
|
|
128
|
+
* Function that transforms api response from gotrue into a desirable / standardised format
|
|
129
|
+
*/
|
|
130
|
+
xform?: (data: any) => any
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export async function _request(
|
|
134
|
+
fetcher: Fetch,
|
|
135
|
+
method: RequestMethodType,
|
|
136
|
+
url: string,
|
|
137
|
+
options?: GotrueRequestOptions
|
|
138
|
+
) {
|
|
139
|
+
const headers = {
|
|
140
|
+
...options?.headers,
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!headers[API_VERSION_HEADER_NAME]) {
|
|
144
|
+
headers[API_VERSION_HEADER_NAME] = API_VERSIONS['2024-01-01'].name
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (options?.jwt) {
|
|
148
|
+
headers['Authorization'] = `Bearer ${options.jwt}`
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const qs = options?.query ?? {}
|
|
152
|
+
if (options?.redirectTo) {
|
|
153
|
+
qs['redirect_to'] = options.redirectTo
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const queryString = Object.keys(qs).length ? '?' + new URLSearchParams(qs).toString() : ''
|
|
157
|
+
const data = await _handleRequest(
|
|
158
|
+
fetcher,
|
|
159
|
+
method,
|
|
160
|
+
url + queryString,
|
|
161
|
+
{
|
|
162
|
+
headers,
|
|
163
|
+
noResolveJson: options?.noResolveJson,
|
|
164
|
+
},
|
|
165
|
+
{},
|
|
166
|
+
options?.body
|
|
167
|
+
)
|
|
168
|
+
return options?.xform ? options?.xform(data) : { data: { ...data }, error: null }
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function _handleRequest(
|
|
172
|
+
fetcher: Fetch,
|
|
173
|
+
method: RequestMethodType,
|
|
174
|
+
url: string,
|
|
175
|
+
options?: FetchOptions,
|
|
176
|
+
parameters?: FetchParameters,
|
|
177
|
+
body?: object
|
|
178
|
+
): Promise<any> {
|
|
179
|
+
const requestParams = _getRequestParams(method, options, parameters, body)
|
|
180
|
+
|
|
181
|
+
let result: any
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
result = await fetcher(url, {
|
|
185
|
+
...requestParams,
|
|
186
|
+
})
|
|
187
|
+
} catch (e) {
|
|
188
|
+
console.error(e)
|
|
189
|
+
|
|
190
|
+
// fetch failed, likely due to a network or CORS error
|
|
191
|
+
throw new AuthRetryableFetchError(_getErrorMessage(e), 0)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (!result.ok) {
|
|
195
|
+
await handleError(result)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (options?.noResolveJson) {
|
|
199
|
+
return result
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
return await result.json()
|
|
204
|
+
} catch (e: any) {
|
|
205
|
+
await handleError(e)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function _sessionResponse(data: any): AuthResponse {
|
|
210
|
+
let session = null
|
|
211
|
+
if (hasSession(data)) {
|
|
212
|
+
session = { ...data }
|
|
213
|
+
|
|
214
|
+
if (!data.expires_at) {
|
|
215
|
+
session.expires_at = expiresAt(data.expires_in)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const user: User = data.user ?? (data as User)
|
|
220
|
+
return { data: { session, user }, error: null }
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function _sessionResponsePassword(data: any): AuthResponsePassword {
|
|
224
|
+
const response = _sessionResponse(data) as AuthResponsePassword
|
|
225
|
+
|
|
226
|
+
if (
|
|
227
|
+
!response.error &&
|
|
228
|
+
data.weak_password &&
|
|
229
|
+
typeof data.weak_password === 'object' &&
|
|
230
|
+
Array.isArray(data.weak_password.reasons) &&
|
|
231
|
+
data.weak_password.reasons.length &&
|
|
232
|
+
data.weak_password.message &&
|
|
233
|
+
typeof data.weak_password.message === 'string' &&
|
|
234
|
+
data.weak_password.reasons.reduce((a: boolean, i: any) => a && typeof i === 'string', true)
|
|
235
|
+
) {
|
|
236
|
+
response.data.weak_password = data.weak_password
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return response
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function _userResponse(data: any): UserResponse {
|
|
243
|
+
const user: User = data.user ?? (data as User)
|
|
244
|
+
return { data: { user }, error: null }
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export function _ssoResponse(data: any): SSOResponse {
|
|
248
|
+
return { data, error: null }
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function _generateLinkResponse(data: any): GenerateLinkResponse {
|
|
252
|
+
const { action_link, email_otp, hashed_token, redirect_to, verification_type, ...rest } = data
|
|
253
|
+
|
|
254
|
+
const properties: GenerateLinkProperties = {
|
|
255
|
+
action_link,
|
|
256
|
+
email_otp,
|
|
257
|
+
hashed_token,
|
|
258
|
+
redirect_to,
|
|
259
|
+
verification_type,
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const user: User = { ...rest }
|
|
263
|
+
return {
|
|
264
|
+
data: {
|
|
265
|
+
properties,
|
|
266
|
+
user,
|
|
267
|
+
},
|
|
268
|
+
error: null,
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function _noResolveJsonResponse(data: any): Response {
|
|
273
|
+
return data
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* hasSession checks if the response object contains a valid session
|
|
278
|
+
* @param data A response object
|
|
279
|
+
* @returns true if a session is in the response
|
|
280
|
+
*/
|
|
281
|
+
function hasSession(data: any): boolean {
|
|
282
|
+
return data.access_token && data.refresh_token && data.expires_in
|
|
283
|
+
}
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { API_VERSION_HEADER_NAME, BASE64URL_REGEX } from './constants'
|
|
2
|
+
import { AuthInvalidJwtError } from './errors'
|
|
3
|
+
import { base64UrlToUint8Array, stringFromBase64URL } from './base64url'
|
|
4
|
+
import { JwtHeader, JwtPayload, SupportedStorage, User } from './types'
|
|
5
|
+
import { Uint8Array_ } from './webauthn.dom'
|
|
6
|
+
|
|
7
|
+
export function expiresAt(expiresIn: number) {
|
|
8
|
+
const timeNow = Math.round(Date.now() / 1000)
|
|
9
|
+
return timeNow + expiresIn
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function uuid() {
|
|
13
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
14
|
+
const r = (Math.random() * 16) | 0,
|
|
15
|
+
v = c == 'x' ? r : (r & 0x3) | 0x8
|
|
16
|
+
return v.toString(16)
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const isBrowser = () => typeof window !== 'undefined' && typeof document !== 'undefined'
|
|
21
|
+
|
|
22
|
+
const localStorageWriteTests = {
|
|
23
|
+
tested: false,
|
|
24
|
+
writable: false,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Checks whether localStorage is supported on this browser.
|
|
29
|
+
*/
|
|
30
|
+
export const supportsLocalStorage = () => {
|
|
31
|
+
if (!isBrowser()) {
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
if (typeof globalThis.localStorage !== 'object') {
|
|
37
|
+
return false
|
|
38
|
+
}
|
|
39
|
+
} catch (e) {
|
|
40
|
+
// DOM exception when accessing `localStorage`
|
|
41
|
+
return false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (localStorageWriteTests.tested) {
|
|
45
|
+
return localStorageWriteTests.writable
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const randomKey = `lswt-${Math.random()}${Math.random()}`
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
globalThis.localStorage.setItem(randomKey, randomKey)
|
|
52
|
+
globalThis.localStorage.removeItem(randomKey)
|
|
53
|
+
|
|
54
|
+
localStorageWriteTests.tested = true
|
|
55
|
+
localStorageWriteTests.writable = true
|
|
56
|
+
} catch (e) {
|
|
57
|
+
// localStorage can't be written to
|
|
58
|
+
// https://www.chromium.org/for-testers/bug-reporting-guidelines/uncaught-securityerror-failed-to-read-the-localstorage-property-from-window-access-is-denied-for-this-document
|
|
59
|
+
|
|
60
|
+
localStorageWriteTests.tested = true
|
|
61
|
+
localStorageWriteTests.writable = false
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return localStorageWriteTests.writable
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Extracts parameters encoded in the URL both in the query and fragment.
|
|
69
|
+
*/
|
|
70
|
+
export function parseParametersFromURL(href: string) {
|
|
71
|
+
const result: { [parameter: string]: string } = {}
|
|
72
|
+
|
|
73
|
+
const url = new URL(href)
|
|
74
|
+
|
|
75
|
+
if (url.hash && url.hash[0] === '#') {
|
|
76
|
+
try {
|
|
77
|
+
const hashSearchParams = new URLSearchParams(url.hash.substring(1))
|
|
78
|
+
hashSearchParams.forEach((value, key) => {
|
|
79
|
+
result[key] = value
|
|
80
|
+
})
|
|
81
|
+
} catch (e: any) {
|
|
82
|
+
// hash is not a query string
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// search parameters take precedence over hash parameters
|
|
87
|
+
url.searchParams.forEach((value, key) => {
|
|
88
|
+
result[key] = value
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
return result
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
type Fetch = typeof fetch
|
|
95
|
+
|
|
96
|
+
export const resolveFetch = (customFetch?: Fetch): Fetch => {
|
|
97
|
+
let _fetch: Fetch
|
|
98
|
+
if (customFetch) {
|
|
99
|
+
_fetch = customFetch
|
|
100
|
+
} else if (typeof fetch === 'undefined') {
|
|
101
|
+
_fetch = (...args) =>
|
|
102
|
+
import('@supabase/node-fetch' as any).then(({ default: fetch }) => fetch(...args))
|
|
103
|
+
} else {
|
|
104
|
+
_fetch = fetch
|
|
105
|
+
}
|
|
106
|
+
return (...args) => _fetch(...args)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const looksLikeFetchResponse = (maybeResponse: unknown): maybeResponse is Response => {
|
|
110
|
+
return (
|
|
111
|
+
typeof maybeResponse === 'object' &&
|
|
112
|
+
maybeResponse !== null &&
|
|
113
|
+
'status' in maybeResponse &&
|
|
114
|
+
'ok' in maybeResponse &&
|
|
115
|
+
'json' in maybeResponse &&
|
|
116
|
+
typeof (maybeResponse as any).json === 'function'
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Storage helpers
|
|
121
|
+
export const setItemAsync = async (
|
|
122
|
+
storage: SupportedStorage,
|
|
123
|
+
key: string,
|
|
124
|
+
data: any
|
|
125
|
+
): Promise<void> => {
|
|
126
|
+
await storage.setItem(key, JSON.stringify(data))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export const getItemAsync = async (storage: SupportedStorage, key: string): Promise<unknown> => {
|
|
130
|
+
const value = await storage.getItem(key)
|
|
131
|
+
|
|
132
|
+
if (!value) {
|
|
133
|
+
return null
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
return JSON.parse(value)
|
|
138
|
+
} catch {
|
|
139
|
+
return value
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export const removeItemAsync = async (storage: SupportedStorage, key: string): Promise<void> => {
|
|
144
|
+
await storage.removeItem(key)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* A deferred represents some asynchronous work that is not yet finished, which
|
|
149
|
+
* may or may not culminate in a value.
|
|
150
|
+
* Taken from: https://github.com/mike-north/types/blob/master/src/async.ts
|
|
151
|
+
*/
|
|
152
|
+
export class Deferred<T = any> {
|
|
153
|
+
public static promiseConstructor: PromiseConstructor = Promise
|
|
154
|
+
|
|
155
|
+
public readonly promise!: PromiseLike<T>
|
|
156
|
+
|
|
157
|
+
public readonly resolve!: (value?: T | PromiseLike<T>) => void
|
|
158
|
+
|
|
159
|
+
public readonly reject!: (reason?: any) => any
|
|
160
|
+
|
|
161
|
+
public constructor() {
|
|
162
|
+
// eslint-disable-next-line @typescript-eslint/no-extra-semi
|
|
163
|
+
;(this as any).promise = new Deferred.promiseConstructor((res, rej) => {
|
|
164
|
+
// eslint-disable-next-line @typescript-eslint/no-extra-semi
|
|
165
|
+
;(this as any).resolve = res
|
|
166
|
+
// eslint-disable-next-line @typescript-eslint/no-extra-semi
|
|
167
|
+
;(this as any).reject = rej
|
|
168
|
+
})
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function decodeJWT(token: string): {
|
|
173
|
+
header: JwtHeader
|
|
174
|
+
payload: JwtPayload
|
|
175
|
+
signature: Uint8Array_
|
|
176
|
+
raw: {
|
|
177
|
+
header: string
|
|
178
|
+
payload: string
|
|
179
|
+
}
|
|
180
|
+
} {
|
|
181
|
+
const parts = token.split('.')
|
|
182
|
+
|
|
183
|
+
if (parts.length !== 3) {
|
|
184
|
+
throw new AuthInvalidJwtError('Invalid JWT structure')
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Regex checks for base64url format
|
|
188
|
+
for (let i = 0; i < parts.length; i++) {
|
|
189
|
+
if (!BASE64URL_REGEX.test(parts[i] as string)) {
|
|
190
|
+
throw new AuthInvalidJwtError('JWT not in base64url format')
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const data = {
|
|
194
|
+
// using base64url lib
|
|
195
|
+
header: JSON.parse(stringFromBase64URL(parts[0])),
|
|
196
|
+
payload: JSON.parse(stringFromBase64URL(parts[1])),
|
|
197
|
+
signature: base64UrlToUint8Array(parts[2]),
|
|
198
|
+
raw: {
|
|
199
|
+
header: parts[0],
|
|
200
|
+
payload: parts[1],
|
|
201
|
+
},
|
|
202
|
+
}
|
|
203
|
+
return data
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Creates a promise that resolves to null after some time.
|
|
208
|
+
*/
|
|
209
|
+
export async function sleep(time: number): Promise<null> {
|
|
210
|
+
return await new Promise((accept) => {
|
|
211
|
+
setTimeout(() => accept(null), time)
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Converts the provided async function into a retryable function. Each result
|
|
217
|
+
* or thrown error is sent to the isRetryable function which should return true
|
|
218
|
+
* if the function should run again.
|
|
219
|
+
*/
|
|
220
|
+
export function retryable<T>(
|
|
221
|
+
fn: (attempt: number) => Promise<T>,
|
|
222
|
+
isRetryable: (attempt: number, error: any | null, result?: T) => boolean
|
|
223
|
+
): Promise<T> {
|
|
224
|
+
const promise = new Promise<T>((accept, reject) => {
|
|
225
|
+
// eslint-disable-next-line @typescript-eslint/no-extra-semi
|
|
226
|
+
;(async () => {
|
|
227
|
+
for (let attempt = 0; attempt < Infinity; attempt++) {
|
|
228
|
+
try {
|
|
229
|
+
const result = await fn(attempt)
|
|
230
|
+
|
|
231
|
+
if (!isRetryable(attempt, null, result)) {
|
|
232
|
+
accept(result)
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
} catch (e: any) {
|
|
236
|
+
if (!isRetryable(attempt, e)) {
|
|
237
|
+
reject(e)
|
|
238
|
+
return
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
})()
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
return promise
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function dec2hex(dec: number) {
|
|
249
|
+
return ('0' + dec.toString(16)).substr(-2)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Functions below taken from: https://stackoverflow.com/questions/63309409/creating-a-code-verifier-and-challenge-for-pkce-auth-on-spotify-api-in-reactjs
|
|
253
|
+
export function generatePKCEVerifier() {
|
|
254
|
+
const verifierLength = 56
|
|
255
|
+
const array = new Uint32Array(verifierLength)
|
|
256
|
+
if (typeof crypto === 'undefined') {
|
|
257
|
+
const charSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'
|
|
258
|
+
const charSetLen = charSet.length
|
|
259
|
+
let verifier = ''
|
|
260
|
+
for (let i = 0; i < verifierLength; i++) {
|
|
261
|
+
verifier += charSet.charAt(Math.floor(Math.random() * charSetLen))
|
|
262
|
+
}
|
|
263
|
+
return verifier
|
|
264
|
+
}
|
|
265
|
+
crypto.getRandomValues(array)
|
|
266
|
+
return Array.from(array, dec2hex).join('')
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function sha256(randomString: string) {
|
|
270
|
+
const encoder = new TextEncoder()
|
|
271
|
+
const encodedData = encoder.encode(randomString)
|
|
272
|
+
const hash = await crypto.subtle.digest('SHA-256', encodedData)
|
|
273
|
+
const bytes = new Uint8Array(hash)
|
|
274
|
+
|
|
275
|
+
return Array.from(bytes)
|
|
276
|
+
.map((c) => String.fromCharCode(c))
|
|
277
|
+
.join('')
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export async function generatePKCEChallenge(verifier: string) {
|
|
281
|
+
const hasCryptoSupport =
|
|
282
|
+
typeof crypto !== 'undefined' &&
|
|
283
|
+
typeof crypto.subtle !== 'undefined' &&
|
|
284
|
+
typeof TextEncoder !== 'undefined'
|
|
285
|
+
|
|
286
|
+
if (!hasCryptoSupport) {
|
|
287
|
+
console.warn(
|
|
288
|
+
'WebCrypto API is not supported. Code challenge method will default to use plain instead of sha256.'
|
|
289
|
+
)
|
|
290
|
+
return verifier
|
|
291
|
+
}
|
|
292
|
+
const hashed = await sha256(verifier)
|
|
293
|
+
return btoa(hashed).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export async function getCodeChallengeAndMethod(
|
|
297
|
+
storage: SupportedStorage,
|
|
298
|
+
storageKey: string,
|
|
299
|
+
isPasswordRecovery = false
|
|
300
|
+
) {
|
|
301
|
+
const codeVerifier = generatePKCEVerifier()
|
|
302
|
+
let storedCodeVerifier = codeVerifier
|
|
303
|
+
if (isPasswordRecovery) {
|
|
304
|
+
storedCodeVerifier += '/PASSWORD_RECOVERY'
|
|
305
|
+
}
|
|
306
|
+
await setItemAsync(storage, `${storageKey}-code-verifier`, storedCodeVerifier)
|
|
307
|
+
const codeChallenge = await generatePKCEChallenge(codeVerifier)
|
|
308
|
+
const codeChallengeMethod = codeVerifier === codeChallenge ? 'plain' : 's256'
|
|
309
|
+
return [codeChallenge, codeChallengeMethod]
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/** Parses the API version which is 2YYY-MM-DD. */
|
|
313
|
+
const API_VERSION_REGEX = /^2[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|1[0-9]|2[0-9]|3[0-1])$/i
|
|
314
|
+
|
|
315
|
+
export function parseResponseAPIVersion(response: Response) {
|
|
316
|
+
const apiVersion = response.headers.get(API_VERSION_HEADER_NAME)
|
|
317
|
+
|
|
318
|
+
if (!apiVersion) {
|
|
319
|
+
return null
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (!apiVersion.match(API_VERSION_REGEX)) {
|
|
323
|
+
return null
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
const date = new Date(`${apiVersion}T00:00:00.0Z`)
|
|
328
|
+
return date
|
|
329
|
+
} catch (e: any) {
|
|
330
|
+
return null
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function validateExp(exp: number) {
|
|
335
|
+
if (!exp) {
|
|
336
|
+
throw new Error('Missing exp claim')
|
|
337
|
+
}
|
|
338
|
+
const timeNow = Math.floor(Date.now() / 1000)
|
|
339
|
+
if (exp <= timeNow) {
|
|
340
|
+
throw new Error('JWT has expired')
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export function getAlgorithm(
|
|
345
|
+
alg: 'HS256' | 'RS256' | 'ES256'
|
|
346
|
+
): RsaHashedImportParams | EcKeyImportParams {
|
|
347
|
+
switch (alg) {
|
|
348
|
+
case 'RS256':
|
|
349
|
+
return {
|
|
350
|
+
name: 'RSASSA-PKCS1-v1_5',
|
|
351
|
+
hash: { name: 'SHA-256' },
|
|
352
|
+
}
|
|
353
|
+
case 'ES256':
|
|
354
|
+
return {
|
|
355
|
+
name: 'ECDSA',
|
|
356
|
+
namedCurve: 'P-256',
|
|
357
|
+
hash: { name: 'SHA-256' },
|
|
358
|
+
}
|
|
359
|
+
default:
|
|
360
|
+
throw new Error('Invalid alg claim')
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
|
|
365
|
+
|
|
366
|
+
export function validateUUID(str: string) {
|
|
367
|
+
if (!UUID_REGEX.test(str)) {
|
|
368
|
+
throw new Error('@supabase/auth-js: Expected parameter to be UUID but is not')
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export function userNotAvailableProxy(): User {
|
|
373
|
+
const proxyTarget = {} as User
|
|
374
|
+
|
|
375
|
+
return new Proxy(proxyTarget, {
|
|
376
|
+
get: (target: any, prop: string) => {
|
|
377
|
+
if (prop === '__isUserNotAvailableProxy') {
|
|
378
|
+
return true
|
|
379
|
+
}
|
|
380
|
+
// Preventative check for common problematic symbols during cloning/inspection
|
|
381
|
+
// These symbols might be accessed by structuredClone or other internal mechanisms.
|
|
382
|
+
if (typeof prop === 'symbol') {
|
|
383
|
+
const sProp = (prop as symbol).toString()
|
|
384
|
+
if (
|
|
385
|
+
sProp === 'Symbol(Symbol.toPrimitive)' ||
|
|
386
|
+
sProp === 'Symbol(Symbol.toStringTag)' ||
|
|
387
|
+
sProp === 'Symbol(util.inspect.custom)'
|
|
388
|
+
) {
|
|
389
|
+
// Node.js util.inspect
|
|
390
|
+
return undefined
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
throw new Error(
|
|
394
|
+
`@supabase/auth-js: client was created with userStorage option and there was no user stored in the user storage. Accessing the "${prop}" property of the session object is not supported. Please use getUser() instead.`
|
|
395
|
+
)
|
|
396
|
+
},
|
|
397
|
+
set: (_target: any, prop: string) => {
|
|
398
|
+
throw new Error(
|
|
399
|
+
`@supabase/auth-js: client was created with userStorage option and there was no user stored in the user storage. Setting the "${prop}" property of the session object is not supported. Please use getUser() to fetch a user object you can manipulate.`
|
|
400
|
+
)
|
|
401
|
+
},
|
|
402
|
+
deleteProperty: (_target: any, prop: string) => {
|
|
403
|
+
throw new Error(
|
|
404
|
+
`@supabase/auth-js: client was created with userStorage option and there was no user stored in the user storage. Deleting the "${prop}" property of the session object is not supported. Please use getUser() to fetch a user object you can manipulate.`
|
|
405
|
+
)
|
|
406
|
+
},
|
|
407
|
+
})
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Deep clones a JSON-serializable object using JSON.parse(JSON.stringify(obj)).
|
|
412
|
+
* Note: Only works for JSON-safe data.
|
|
413
|
+
*/
|
|
414
|
+
export function deepClone<T>(obj: T): T {
|
|
415
|
+
return JSON.parse(JSON.stringify(obj))
|
|
416
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SupportedStorage } from './types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns a localStorage-like object that stores the key-value pairs in
|
|
5
|
+
* memory.
|
|
6
|
+
*/
|
|
7
|
+
export function memoryLocalStorageAdapter(store: { [key: string]: string } = {}): SupportedStorage {
|
|
8
|
+
return {
|
|
9
|
+
getItem: (key) => {
|
|
10
|
+
return store[key] || null
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
setItem: (key, value) => {
|
|
14
|
+
store[key] = value
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
removeItem: (key) => {
|
|
18
|
+
delete store[key]
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
}
|