@atproto/oauth-provider 0.19.6 → 0.19.8
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/CHANGELOG.md +33 -0
- package/dist/errors/error-parser.js +5 -5
- package/dist/errors/error-parser.js.map +1 -1
- package/package.json +22 -18
- package/src/access-token/access-token-mode.ts +0 -4
- package/src/account/account-manager.ts +0 -591
- package/src/account/account-store.ts +0 -305
- package/src/account/sign-in-data.ts +0 -15
- package/src/account/sign-up-input.ts +0 -20
- package/src/client/client-auth.ts +0 -64
- package/src/client/client-data.ts +0 -9
- package/src/client/client-id.ts +0 -4
- package/src/client/client-info.ts +0 -13
- package/src/client/client-manager.ts +0 -772
- package/src/client/client-store.ts +0 -35
- package/src/client/client-utils.ts +0 -37
- package/src/client/client.ts +0 -394
- package/src/constants.ts +0 -80
- package/src/customization/branding.ts +0 -12
- package/src/customization/build-customization-css.ts +0 -55
- package/src/customization/build-customization-data.ts +0 -24
- package/src/customization/colors.ts +0 -48
- package/src/customization/customization.ts +0 -29
- package/src/customization/links.ts +0 -10
- package/src/device/device-data.ts +0 -11
- package/src/device/device-id.ts +0 -27
- package/src/device/device-manager.ts +0 -292
- package/src/device/device-store.ts +0 -31
- package/src/device/session-id.ts +0 -21
- package/src/dpop/dpop-manager.ts +0 -214
- package/src/dpop/dpop-nonce.ts +0 -121
- package/src/dpop/dpop-proof.ts +0 -6
- package/src/errors/access-denied-error.ts +0 -12
- package/src/errors/account-selection-required-error.ts +0 -12
- package/src/errors/authorization-error.ts +0 -45
- package/src/errors/consent-required-error.ts +0 -12
- package/src/errors/error-parser.ts +0 -147
- package/src/errors/handle-unavailable-error.ts +0 -23
- package/src/errors/invalid-authorization-details-error.ts +0 -27
- package/src/errors/invalid-client-error.ts +0 -20
- package/src/errors/invalid-client-id-error.ts +0 -31
- package/src/errors/invalid-client-metadata-error.ts +0 -68
- package/src/errors/invalid-credentials-error.ts +0 -29
- package/src/errors/invalid-dpop-key-binding-error.ts +0 -21
- package/src/errors/invalid-dpop-proof-error.ts +0 -13
- package/src/errors/invalid-grant-error.ts +0 -21
- package/src/errors/invalid-invite-code-error.ts +0 -10
- package/src/errors/invalid-redirect-uri-error.ts +0 -17
- package/src/errors/invalid-request-error.ts +0 -32
- package/src/errors/invalid-scope-error.ts +0 -15
- package/src/errors/invalid-token-error.ts +0 -60
- package/src/errors/login-required-error.ts +0 -12
- package/src/errors/oauth-error.ts +0 -28
- package/src/errors/second-authentication-factor-required-error.ts +0 -25
- package/src/errors/unauthorized-client-error.ts +0 -20
- package/src/errors/use-dpop-nonce-error.ts +0 -32
- package/src/errors/www-authenticate-error.ts +0 -64
- package/src/index.ts +0 -16
- package/src/lexicon/lexicon-data.ts +0 -11
- package/src/lexicon/lexicon-getter.ts +0 -55
- package/src/lexicon/lexicon-manager.ts +0 -116
- package/src/lexicon/lexicon-store.ts +0 -35
- package/src/lib/csp/index.ts +0 -101
- package/src/lib/hcaptcha.ts +0 -228
- package/src/lib/html/README.md +0 -9
- package/src/lib/html/build-document.ts +0 -151
- package/src/lib/html/escapers.ts +0 -66
- package/src/lib/html/html.ts +0 -56
- package/src/lib/html/hydration-data.ts +0 -19
- package/src/lib/html/index.ts +0 -5
- package/src/lib/html/tags.ts +0 -67
- package/src/lib/html/util.ts +0 -17
- package/src/lib/http/README.md +0 -11
- package/src/lib/http/accept.ts +0 -90
- package/src/lib/http/context.ts +0 -42
- package/src/lib/http/headers.ts +0 -15
- package/src/lib/http/index.ts +0 -10
- package/src/lib/http/method.ts +0 -18
- package/src/lib/http/middleware.ts +0 -169
- package/src/lib/http/parser.ts +0 -101
- package/src/lib/http/path.ts +0 -82
- package/src/lib/http/request.ts +0 -256
- package/src/lib/http/response.ts +0 -122
- package/src/lib/http/route.ts +0 -60
- package/src/lib/http/router.ts +0 -117
- package/src/lib/http/security-headers.ts +0 -100
- package/src/lib/http/stream.ts +0 -57
- package/src/lib/http/types.ts +0 -16
- package/src/lib/http/url.ts +0 -23
- package/src/lib/nsid.ts +0 -10
- package/src/lib/redis.ts +0 -25
- package/src/lib/util/authorization-header.ts +0 -28
- package/src/lib/util/cast.ts +0 -18
- package/src/lib/util/color.ts +0 -168
- package/src/lib/util/crypto.ts +0 -32
- package/src/lib/util/date.ts +0 -7
- package/src/lib/util/error.ts +0 -7
- package/src/lib/util/function.ts +0 -39
- package/src/lib/util/locale.ts +0 -12
- package/src/lib/util/object.ts +0 -23
- package/src/lib/util/redirect-uri.ts +0 -46
- package/src/lib/util/time.ts +0 -49
- package/src/lib/util/type.ts +0 -182
- package/src/lib/util/ui8.ts +0 -14
- package/src/lib/util/well-known.ts +0 -8
- package/src/lib/util/zod-error.ts +0 -26
- package/src/lib/write-form-redirect.ts +0 -58
- package/src/lib/write-html.ts +0 -70
- package/src/metadata/build-metadata.ts +0 -141
- package/src/oauth-client.ts +0 -3
- package/src/oauth-dpop.ts +0 -2
- package/src/oauth-errors.ts +0 -26
- package/src/oauth-hooks.ts +0 -519
- package/src/oauth-middleware.ts +0 -53
- package/src/oauth-provider.ts +0 -1110
- package/src/oauth-store.ts +0 -12
- package/src/oauth-verifier.ts +0 -260
- package/src/replay/replay-manager.ts +0 -51
- package/src/replay/replay-store-memory.ts +0 -36
- package/src/replay/replay-store-redis.ts +0 -30
- package/src/replay/replay-store.ts +0 -44
- package/src/request/code.ts +0 -23
- package/src/request/request-data.ts +0 -36
- package/src/request/request-id.ts +0 -22
- package/src/request/request-manager.ts +0 -521
- package/src/request/request-store.ts +0 -60
- package/src/request/request-uri.ts +0 -42
- package/src/result/authorization-redirect-parameters.ts +0 -24
- package/src/result/authorization-result-authorize-page.ts +0 -16
- package/src/result/authorization-result-redirect.ts +0 -8
- package/src/router/assets/assets-manifest.ts +0 -117
- package/src/router/assets/assets.ts +0 -124
- package/src/router/assets/csrf.ts +0 -79
- package/src/router/assets/send-account-page.ts +0 -23
- package/src/router/assets/send-authorization-page.ts +0 -42
- package/src/router/assets/send-cookie-error-page.ts +0 -21
- package/src/router/assets/send-error-page.ts +0 -25
- package/src/router/assets/send-redirect.ts +0 -140
- package/src/router/create-account-page-middleware.ts +0 -88
- package/src/router/create-api-middleware.ts +0 -1051
- package/src/router/create-authorization-page-middleware.ts +0 -281
- package/src/router/create-oauth-middleware.ts +0 -257
- package/src/router/error-handler.ts +0 -6
- package/src/router/middleware-options.ts +0 -9
- package/src/signer/access-token-payload.ts +0 -25
- package/src/signer/api-token-payload.ts +0 -18
- package/src/signer/signer.ts +0 -121
- package/src/token/refresh-token.ts +0 -30
- package/src/token/token-claims.ts +0 -22
- package/src/token/token-data.ts +0 -40
- package/src/token/token-id.ts +0 -25
- package/src/token/token-manager.ts +0 -427
- package/src/token/token-store.ts +0 -88
- package/src/types/authorization-response-error.ts +0 -27
- package/src/types/color-hue.ts +0 -3
- package/src/types/email-otp.ts +0 -3
- package/src/types/email.ts +0 -26
- package/src/types/handle.ts +0 -23
- package/src/types/invite-code.ts +0 -4
- package/src/types/par-response-error.ts +0 -25
- package/src/types/password.ts +0 -4
- package/src/types/rgb-color.ts +0 -21
- package/tsconfig.build.json +0 -8
- package/tsconfig.build.tsbuildinfo +0 -1
- package/tsconfig.json +0 -4
|
@@ -1,591 +0,0 @@
|
|
|
1
|
-
import { Did } from '@atproto/did'
|
|
2
|
-
import {
|
|
3
|
-
OAuthIssuerIdentifier,
|
|
4
|
-
isOAuthClientIdLoopback,
|
|
5
|
-
} from '@atproto/oauth-types'
|
|
6
|
-
import { ClientId } from '../client/client-id.js'
|
|
7
|
-
import { Client } from '../client/client.js'
|
|
8
|
-
import { DeviceId } from '../device/device-id.js'
|
|
9
|
-
import { InvalidCredentialsError } from '../errors/invalid-credentials-error.js'
|
|
10
|
-
import { InvalidRequestError } from '../errors/invalid-request-error.js'
|
|
11
|
-
import { HCaptchaClient, HcaptchaVerifyResult } from '../lib/hcaptcha.js'
|
|
12
|
-
import { callAsync } from '../lib/util/function.js'
|
|
13
|
-
import { constantTime } from '../lib/util/time.js'
|
|
14
|
-
import { OAuthHooks, RequestMetadata } from '../oauth-hooks.js'
|
|
15
|
-
import { Customization } from '../oauth-provider.js'
|
|
16
|
-
import {
|
|
17
|
-
Account,
|
|
18
|
-
AccountStore,
|
|
19
|
-
AuthorizedClientData,
|
|
20
|
-
DeleteAccountConfirmInput,
|
|
21
|
-
DeleteAccountRequestInput,
|
|
22
|
-
DeviceAccount,
|
|
23
|
-
HandleString,
|
|
24
|
-
ResetPasswordConfirmInput,
|
|
25
|
-
ResetPasswordRequestInput,
|
|
26
|
-
SignUpData,
|
|
27
|
-
UpdateEmailConfirmInput,
|
|
28
|
-
UpdateEmailRequestInput,
|
|
29
|
-
UpdateHandleData,
|
|
30
|
-
VerifyEmailConfirmInput,
|
|
31
|
-
VerifyEmailRequestInput,
|
|
32
|
-
} from './account-store.js'
|
|
33
|
-
import { SignInData } from './sign-in-data.js'
|
|
34
|
-
import { SignUpInput } from './sign-up-input.js'
|
|
35
|
-
|
|
36
|
-
const TIMING_ATTACK_MITIGATION_DELAY = 400
|
|
37
|
-
const BRUTE_FORCE_MITIGATION_DELAY = 300
|
|
38
|
-
|
|
39
|
-
// @TODO Add rate limit to all the OAuth routes.
|
|
40
|
-
|
|
41
|
-
export class AccountManager {
|
|
42
|
-
protected readonly inviteCodeRequired: boolean
|
|
43
|
-
protected readonly hcaptchaClient?: HCaptchaClient
|
|
44
|
-
|
|
45
|
-
constructor(
|
|
46
|
-
issuer: OAuthIssuerIdentifier,
|
|
47
|
-
protected readonly store: AccountStore,
|
|
48
|
-
protected readonly hooks: OAuthHooks,
|
|
49
|
-
customization: Customization,
|
|
50
|
-
) {
|
|
51
|
-
this.inviteCodeRequired = customization.inviteCodeRequired !== false
|
|
52
|
-
this.hcaptchaClient = customization.hcaptcha
|
|
53
|
-
? new HCaptchaClient(new URL(issuer).hostname, customization.hcaptcha)
|
|
54
|
-
: undefined
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
protected async processHcaptchaToken(
|
|
58
|
-
input: SignUpInput,
|
|
59
|
-
deviceId: DeviceId,
|
|
60
|
-
deviceMetadata: RequestMetadata,
|
|
61
|
-
): Promise<HcaptchaVerifyResult | undefined> {
|
|
62
|
-
if (!this.hcaptchaClient) {
|
|
63
|
-
return undefined
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (!input.hcaptchaToken) {
|
|
67
|
-
throw new InvalidRequestError('hCaptcha token is required')
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const tokens = this.hcaptchaClient.buildClientTokens(
|
|
71
|
-
deviceMetadata.ipAddress,
|
|
72
|
-
input.handle,
|
|
73
|
-
deviceMetadata.userAgent,
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
const result = await this.hcaptchaClient
|
|
77
|
-
.verify('signup', input.hcaptchaToken, deviceMetadata.ipAddress, tokens)
|
|
78
|
-
.catch((err) => {
|
|
79
|
-
throw InvalidRequestError.from(err, 'hCaptcha verification failed')
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
await this.hooks.onHcaptchaResult?.call(null, {
|
|
83
|
-
input,
|
|
84
|
-
deviceId,
|
|
85
|
-
deviceMetadata,
|
|
86
|
-
tokens,
|
|
87
|
-
result,
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
this.hcaptchaClient.checkVerifyResult(result, tokens)
|
|
92
|
-
} catch (err) {
|
|
93
|
-
throw InvalidRequestError.from(err, 'hCaptcha verification failed')
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return result
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
protected async enforceInviteCode(
|
|
100
|
-
input: SignUpInput,
|
|
101
|
-
_deviceId: DeviceId,
|
|
102
|
-
_deviceMetadata: RequestMetadata,
|
|
103
|
-
): Promise<string | undefined> {
|
|
104
|
-
if (!this.inviteCodeRequired) {
|
|
105
|
-
return undefined
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (!input.inviteCode) {
|
|
109
|
-
throw new InvalidRequestError('Invite code is required')
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return input.inviteCode
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
protected async buildSignupData(
|
|
116
|
-
input: SignUpInput,
|
|
117
|
-
deviceId: DeviceId,
|
|
118
|
-
deviceMetadata: RequestMetadata,
|
|
119
|
-
): Promise<SignUpData> {
|
|
120
|
-
const [hcaptchaResult, inviteCode] = await Promise.all([
|
|
121
|
-
this.processHcaptchaToken(input, deviceId, deviceMetadata),
|
|
122
|
-
this.enforceInviteCode(input, deviceId, deviceMetadata),
|
|
123
|
-
])
|
|
124
|
-
|
|
125
|
-
return { ...input, hcaptchaResult, inviteCode }
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
public async createAccount(
|
|
129
|
-
deviceId: DeviceId,
|
|
130
|
-
deviceMetadata: RequestMetadata,
|
|
131
|
-
input: SignUpInput,
|
|
132
|
-
): Promise<Account> {
|
|
133
|
-
return constantTime(BRUTE_FORCE_MITIGATION_DELAY, async () => {
|
|
134
|
-
await this.hooks.onSignUpAttempt?.call(null, {
|
|
135
|
-
input,
|
|
136
|
-
deviceId,
|
|
137
|
-
deviceMetadata,
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
const data = await this.buildSignupData(input, deviceId, deviceMetadata)
|
|
141
|
-
|
|
142
|
-
const account = await callAsync(() =>
|
|
143
|
-
this.store.createAccount(data),
|
|
144
|
-
).catch((err) => {
|
|
145
|
-
throw InvalidRequestError.from(err, 'Account creation failed')
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
try {
|
|
149
|
-
await this.hooks.onSignedUp?.call(null, {
|
|
150
|
-
data,
|
|
151
|
-
account,
|
|
152
|
-
deviceId,
|
|
153
|
-
deviceMetadata,
|
|
154
|
-
})
|
|
155
|
-
|
|
156
|
-
return account
|
|
157
|
-
} catch (err) {
|
|
158
|
-
await this.removeDeviceAccount(deviceId, account.did)
|
|
159
|
-
|
|
160
|
-
throw InvalidRequestError.from(
|
|
161
|
-
err,
|
|
162
|
-
'The account was successfully created but something went wrong, try signing-in.',
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
})
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
public async authenticateAccount(
|
|
169
|
-
deviceId: DeviceId,
|
|
170
|
-
deviceMetadata: RequestMetadata,
|
|
171
|
-
data: SignInData,
|
|
172
|
-
clientId?: ClientId,
|
|
173
|
-
): Promise<Account> {
|
|
174
|
-
return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {
|
|
175
|
-
await this.hooks.onSignInAttempt?.call(null, {
|
|
176
|
-
data,
|
|
177
|
-
deviceId,
|
|
178
|
-
deviceMetadata,
|
|
179
|
-
clientId,
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
const account = await callAsync(() =>
|
|
183
|
-
this.store.authenticateAccount(data),
|
|
184
|
-
).catch(async (err) => {
|
|
185
|
-
// Only notify for credential failures (e.g. unknown identifier, wrong
|
|
186
|
-
// password). Server errors and flows that require an additional factor
|
|
187
|
-
// (e.g. SecondAuthenticationFactorRequiredError) are not "failed
|
|
188
|
-
// sign-ins" and do not trigger the hook.
|
|
189
|
-
if (err instanceof InvalidRequestError) {
|
|
190
|
-
// Stores that throw the more specific `InvalidCredentialsError`
|
|
191
|
-
// can attach the matched subject identifier to distinguish
|
|
192
|
-
// "identifier known, password wrong" from "identifier unknown".
|
|
193
|
-
// This information is only exposed to the hook and is never
|
|
194
|
-
// surfaced to the client.
|
|
195
|
-
const isCredentialsError = err instanceof InvalidCredentialsError
|
|
196
|
-
const did = isCredentialsError ? err.did ?? null : null
|
|
197
|
-
|
|
198
|
-
// Swallow any error from the hook itself so that it does not mask
|
|
199
|
-
// the underlying authentication failure being reported.
|
|
200
|
-
try {
|
|
201
|
-
await this.hooks.onSignInFailed?.call(null, {
|
|
202
|
-
data,
|
|
203
|
-
error: err,
|
|
204
|
-
did,
|
|
205
|
-
deviceId,
|
|
206
|
-
deviceMetadata,
|
|
207
|
-
clientId,
|
|
208
|
-
})
|
|
209
|
-
} catch {
|
|
210
|
-
// noop
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (isCredentialsError) {
|
|
214
|
-
// Defensively downgrade to a plain InvalidRequestError
|
|
215
|
-
throw new InvalidRequestError(err.error_description)
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
throw err
|
|
220
|
-
})
|
|
221
|
-
|
|
222
|
-
await this.hooks.onSignedIn?.call(null, {
|
|
223
|
-
data,
|
|
224
|
-
account,
|
|
225
|
-
deviceId,
|
|
226
|
-
deviceMetadata,
|
|
227
|
-
clientId,
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
return account
|
|
231
|
-
}).catch((err) => {
|
|
232
|
-
throw InvalidRequestError.from(
|
|
233
|
-
err,
|
|
234
|
-
'Unable to sign-in due to an unexpected server error',
|
|
235
|
-
)
|
|
236
|
-
})
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
public async upsertDeviceAccount(
|
|
240
|
-
deviceId: DeviceId,
|
|
241
|
-
did: Did,
|
|
242
|
-
): Promise<void> {
|
|
243
|
-
await this.store.upsertDeviceAccount(deviceId, did)
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
public async getDeviceAccount(
|
|
247
|
-
deviceId: DeviceId,
|
|
248
|
-
did: Did,
|
|
249
|
-
): Promise<DeviceAccount> {
|
|
250
|
-
const deviceAccount = await this.store.getDeviceAccount(deviceId, did)
|
|
251
|
-
if (!deviceAccount) throw new InvalidRequestError(`Account not found`)
|
|
252
|
-
|
|
253
|
-
return deviceAccount
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
public async setAuthorizedClient(
|
|
257
|
-
account: Account,
|
|
258
|
-
client: Client,
|
|
259
|
-
data: AuthorizedClientData,
|
|
260
|
-
): Promise<void> {
|
|
261
|
-
// "Loopback" clients are not distinguishable from one another.
|
|
262
|
-
if (isOAuthClientIdLoopback(client.id)) return
|
|
263
|
-
|
|
264
|
-
await this.store.setAuthorizedClient(account.did, client.id, data)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
public async getAccount(did: Did) {
|
|
268
|
-
return this.store.getAccount(did)
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
public async removeDeviceAccount(deviceId: DeviceId, did: Did) {
|
|
272
|
-
return this.store.removeDeviceAccount(deviceId, did)
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
public async listDeviceAccounts(
|
|
276
|
-
deviceId: DeviceId,
|
|
277
|
-
): Promise<DeviceAccount[]> {
|
|
278
|
-
const deviceAccounts = await this.store.listDeviceAccounts({
|
|
279
|
-
deviceId,
|
|
280
|
-
})
|
|
281
|
-
|
|
282
|
-
return deviceAccounts // Fool proof
|
|
283
|
-
.filter((deviceAccount) => deviceAccount.deviceId === deviceId)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
public async listAccountDevices(did: Did): Promise<DeviceAccount[]> {
|
|
287
|
-
const deviceAccounts = await this.store.listDeviceAccounts({
|
|
288
|
-
did,
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
return deviceAccounts // Fool proof
|
|
292
|
-
.filter((deviceAccount) => deviceAccount.account.did === did)
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
public async resetPasswordRequest(
|
|
296
|
-
deviceId: DeviceId,
|
|
297
|
-
deviceMetadata: RequestMetadata,
|
|
298
|
-
input: ResetPasswordRequestInput,
|
|
299
|
-
) {
|
|
300
|
-
return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {
|
|
301
|
-
await this.hooks.onResetPasswordRequest?.call(null, {
|
|
302
|
-
input,
|
|
303
|
-
deviceId,
|
|
304
|
-
deviceMetadata,
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
const account = await this.store.resetPasswordRequest(input)
|
|
308
|
-
|
|
309
|
-
// @NOTE Do not throw here, to prevent user enumeration
|
|
310
|
-
|
|
311
|
-
await this.hooks.onResetPasswordRequested?.call(null, {
|
|
312
|
-
input,
|
|
313
|
-
deviceId,
|
|
314
|
-
deviceMetadata,
|
|
315
|
-
account,
|
|
316
|
-
})
|
|
317
|
-
})
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
public async resetPasswordConfirm(
|
|
321
|
-
deviceId: DeviceId,
|
|
322
|
-
deviceMetadata: RequestMetadata,
|
|
323
|
-
input: ResetPasswordConfirmInput,
|
|
324
|
-
) {
|
|
325
|
-
return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {
|
|
326
|
-
await this.hooks.onResetPasswordConfirm?.call(null, {
|
|
327
|
-
input,
|
|
328
|
-
deviceId,
|
|
329
|
-
deviceMetadata,
|
|
330
|
-
})
|
|
331
|
-
|
|
332
|
-
const account = await this.store.resetPasswordConfirm(input)
|
|
333
|
-
|
|
334
|
-
if (!account) {
|
|
335
|
-
throw new InvalidRequestError('Invalid token')
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
await this.hooks.onResetPasswordConfirmed?.call(null, {
|
|
339
|
-
input,
|
|
340
|
-
deviceId,
|
|
341
|
-
deviceMetadata,
|
|
342
|
-
account,
|
|
343
|
-
})
|
|
344
|
-
|
|
345
|
-
return account
|
|
346
|
-
})
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
public async verifyHandleAvailability(handle: HandleString): Promise<void> {
|
|
350
|
-
return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {
|
|
351
|
-
return this.store.verifyHandleAvailability(handle)
|
|
352
|
-
})
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
public async updateEmailRequest(
|
|
356
|
-
deviceId: DeviceId,
|
|
357
|
-
deviceMetadata: RequestMetadata,
|
|
358
|
-
input: UpdateEmailRequestInput,
|
|
359
|
-
account: Account,
|
|
360
|
-
): Promise<{ tokenRequired: boolean }> {
|
|
361
|
-
await this.hooks.onChangeEmailRequest?.call(null, {
|
|
362
|
-
deviceId,
|
|
363
|
-
deviceMetadata,
|
|
364
|
-
input,
|
|
365
|
-
account,
|
|
366
|
-
})
|
|
367
|
-
|
|
368
|
-
const { tokenRequired } = await this.store.updateEmailRequest(input)
|
|
369
|
-
|
|
370
|
-
await this.hooks.onChangeEmailRequested?.call(null, {
|
|
371
|
-
deviceId,
|
|
372
|
-
deviceMetadata,
|
|
373
|
-
input,
|
|
374
|
-
account,
|
|
375
|
-
})
|
|
376
|
-
|
|
377
|
-
return { tokenRequired: tokenRequired === true }
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
public async updateEmailConfirm(
|
|
381
|
-
deviceId: DeviceId,
|
|
382
|
-
deviceMetadata: RequestMetadata,
|
|
383
|
-
input: UpdateEmailConfirmInput,
|
|
384
|
-
account: Account,
|
|
385
|
-
): Promise<Account> {
|
|
386
|
-
await this.hooks.onUpdateEmailConfirm?.call(null, {
|
|
387
|
-
deviceId,
|
|
388
|
-
deviceMetadata,
|
|
389
|
-
input,
|
|
390
|
-
account,
|
|
391
|
-
})
|
|
392
|
-
|
|
393
|
-
const updatedAccount = await this.store.updateEmailConfirm(input)
|
|
394
|
-
|
|
395
|
-
if (!updatedAccount) {
|
|
396
|
-
throw new InvalidRequestError('Invalid token')
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
await this.hooks.onUpdateEmailConfirmed?.call(null, {
|
|
400
|
-
deviceId,
|
|
401
|
-
deviceMetadata,
|
|
402
|
-
input,
|
|
403
|
-
account: updatedAccount,
|
|
404
|
-
prevAccount: account,
|
|
405
|
-
})
|
|
406
|
-
|
|
407
|
-
return updatedAccount
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
public async verifyEmailRequest(
|
|
411
|
-
deviceId: DeviceId,
|
|
412
|
-
deviceMetadata: RequestMetadata,
|
|
413
|
-
input: VerifyEmailRequestInput,
|
|
414
|
-
account: Account,
|
|
415
|
-
): Promise<void> {
|
|
416
|
-
await this.hooks.onVerifyEmailRequest?.call(null, {
|
|
417
|
-
deviceId,
|
|
418
|
-
deviceMetadata,
|
|
419
|
-
account,
|
|
420
|
-
input,
|
|
421
|
-
})
|
|
422
|
-
|
|
423
|
-
await this.store.verifyEmailRequest(input)
|
|
424
|
-
|
|
425
|
-
await this.hooks.onVerifyEmailRequested?.call(null, {
|
|
426
|
-
deviceId,
|
|
427
|
-
deviceMetadata,
|
|
428
|
-
account,
|
|
429
|
-
input,
|
|
430
|
-
})
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
public async verifyEmailConfirm(
|
|
434
|
-
deviceId: DeviceId,
|
|
435
|
-
deviceMetadata: RequestMetadata,
|
|
436
|
-
input: VerifyEmailConfirmInput,
|
|
437
|
-
account: Account,
|
|
438
|
-
): Promise<Account> {
|
|
439
|
-
await this.hooks.onVerifyEmailConfirm?.call(null, {
|
|
440
|
-
deviceId,
|
|
441
|
-
deviceMetadata,
|
|
442
|
-
account,
|
|
443
|
-
input,
|
|
444
|
-
})
|
|
445
|
-
|
|
446
|
-
const updatedAccount = await this.store.verifyEmailConfirm(input)
|
|
447
|
-
|
|
448
|
-
if (!updatedAccount) {
|
|
449
|
-
throw new InvalidRequestError('Invalid token')
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
await this.hooks.onVerifyEmailConfirmed?.call(null, {
|
|
453
|
-
deviceId,
|
|
454
|
-
deviceMetadata,
|
|
455
|
-
account: updatedAccount,
|
|
456
|
-
input,
|
|
457
|
-
})
|
|
458
|
-
|
|
459
|
-
return updatedAccount
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
public async updateHandle(
|
|
463
|
-
deviceId: DeviceId,
|
|
464
|
-
deviceMetadata: RequestMetadata,
|
|
465
|
-
input: UpdateHandleData,
|
|
466
|
-
account: Account,
|
|
467
|
-
): Promise<Account> {
|
|
468
|
-
await this.hooks.onUpdateHandle?.call(null, {
|
|
469
|
-
deviceId,
|
|
470
|
-
deviceMetadata,
|
|
471
|
-
input,
|
|
472
|
-
account,
|
|
473
|
-
})
|
|
474
|
-
|
|
475
|
-
const updatedAccount = await this.store.updateHandle(input)
|
|
476
|
-
|
|
477
|
-
await this.hooks.onUpdatedHandle?.call(null, {
|
|
478
|
-
deviceId,
|
|
479
|
-
deviceMetadata,
|
|
480
|
-
input,
|
|
481
|
-
account: updatedAccount,
|
|
482
|
-
})
|
|
483
|
-
|
|
484
|
-
return updatedAccount
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
public async deactivateAccount(
|
|
488
|
-
deviceId: DeviceId,
|
|
489
|
-
deviceMetadata: RequestMetadata,
|
|
490
|
-
account: Account,
|
|
491
|
-
): Promise<Account> {
|
|
492
|
-
await this.hooks.onDeactivateAccount?.call(null, {
|
|
493
|
-
deviceId,
|
|
494
|
-
deviceMetadata,
|
|
495
|
-
account,
|
|
496
|
-
})
|
|
497
|
-
|
|
498
|
-
const updatedAccount = await callAsync(() =>
|
|
499
|
-
this.store.deactivateAccount({
|
|
500
|
-
did: account.did,
|
|
501
|
-
// @TODO support setting this from the UI/API
|
|
502
|
-
deleteAfter: undefined,
|
|
503
|
-
}),
|
|
504
|
-
).catch((err) => {
|
|
505
|
-
throw InvalidRequestError.from(err, 'Account deactivation failed')
|
|
506
|
-
})
|
|
507
|
-
|
|
508
|
-
await this.hooks.onDeactivatedAccount?.call(null, {
|
|
509
|
-
deviceId,
|
|
510
|
-
deviceMetadata,
|
|
511
|
-
account: updatedAccount,
|
|
512
|
-
})
|
|
513
|
-
|
|
514
|
-
return updatedAccount
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
public async reactivateAccount(
|
|
518
|
-
deviceId: DeviceId,
|
|
519
|
-
deviceMetadata: RequestMetadata,
|
|
520
|
-
account: Account,
|
|
521
|
-
): Promise<Account> {
|
|
522
|
-
await this.hooks.onReactivateAccount?.call(null, {
|
|
523
|
-
deviceId,
|
|
524
|
-
deviceMetadata,
|
|
525
|
-
account,
|
|
526
|
-
})
|
|
527
|
-
|
|
528
|
-
const updatedAccount = await callAsync(() =>
|
|
529
|
-
this.store.reactivateAccount({ did: account.did }),
|
|
530
|
-
).catch((err) => {
|
|
531
|
-
throw InvalidRequestError.from(err, 'Account reactivation failed')
|
|
532
|
-
})
|
|
533
|
-
|
|
534
|
-
await this.hooks.onReactivatedAccount?.call(null, {
|
|
535
|
-
deviceId,
|
|
536
|
-
deviceMetadata,
|
|
537
|
-
account: updatedAccount,
|
|
538
|
-
})
|
|
539
|
-
|
|
540
|
-
return updatedAccount
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
public async deleteAccountRequest(
|
|
544
|
-
deviceId: DeviceId,
|
|
545
|
-
deviceMetadata: RequestMetadata,
|
|
546
|
-
input: DeleteAccountRequestInput,
|
|
547
|
-
account: Account,
|
|
548
|
-
): Promise<void> {
|
|
549
|
-
await this.hooks.onDeleteAccountRequest?.call(null, {
|
|
550
|
-
deviceId,
|
|
551
|
-
deviceMetadata,
|
|
552
|
-
account,
|
|
553
|
-
})
|
|
554
|
-
|
|
555
|
-
await this.store.deleteAccountRequest(input)
|
|
556
|
-
|
|
557
|
-
await this.hooks.onDeleteAccountRequested?.call(null, {
|
|
558
|
-
deviceId,
|
|
559
|
-
deviceMetadata,
|
|
560
|
-
account,
|
|
561
|
-
})
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
public async deleteAccountConfirm(
|
|
565
|
-
deviceId: DeviceId,
|
|
566
|
-
deviceMetadata: RequestMetadata,
|
|
567
|
-
input: DeleteAccountConfirmInput,
|
|
568
|
-
account: Account,
|
|
569
|
-
): Promise<void> {
|
|
570
|
-
return constantTime(BRUTE_FORCE_MITIGATION_DELAY, async () => {
|
|
571
|
-
await this.hooks.onDeleteAccountConfirm?.call(null, {
|
|
572
|
-
deviceId,
|
|
573
|
-
deviceMetadata,
|
|
574
|
-
account,
|
|
575
|
-
})
|
|
576
|
-
|
|
577
|
-
await callAsync(() => this.store.deleteAccountConfirm(input)).catch(
|
|
578
|
-
(err) => {
|
|
579
|
-
throw InvalidRequestError.from(err, 'Account deletion failed')
|
|
580
|
-
},
|
|
581
|
-
)
|
|
582
|
-
|
|
583
|
-
await this.hooks.onDeleteAccountConfirmed?.call(null, {
|
|
584
|
-
deviceId,
|
|
585
|
-
deviceMetadata,
|
|
586
|
-
account,
|
|
587
|
-
input,
|
|
588
|
-
})
|
|
589
|
-
})
|
|
590
|
-
}
|
|
591
|
-
}
|