@molecule/api-resource-user 1.0.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 +115 -0
- package/dist/authorization.d.ts +107 -0
- package/dist/authorization.d.ts.map +1 -0
- package/dist/authorization.js +375 -0
- package/dist/authorization.js.map +1 -0
- package/dist/authorizers/auth.d.ts +8 -0
- package/dist/authorizers/auth.d.ts.map +1 -0
- package/dist/authorizers/auth.js +19 -0
- package/dist/authorizers/auth.js.map +1 -0
- package/dist/authorizers/authSelf.d.ts +14 -0
- package/dist/authorizers/authSelf.d.ts.map +1 -0
- package/dist/authorizers/authSelf.js +31 -0
- package/dist/authorizers/authSelf.js.map +1 -0
- package/dist/authorizers/index.d.ts +5 -0
- package/dist/authorizers/index.d.ts.map +1 -0
- package/dist/authorizers/index.js +5 -0
- package/dist/authorizers/index.js.map +1 -0
- package/dist/authorizers/rateLimit.d.ts +84 -0
- package/dist/authorizers/rateLimit.d.ts.map +1 -0
- package/dist/authorizers/rateLimit.js +144 -0
- package/dist/authorizers/rateLimit.js.map +1 -0
- package/dist/authorizers/requireWebhookAuthenticity.d.ts +23 -0
- package/dist/authorizers/requireWebhookAuthenticity.d.ts.map +1 -0
- package/dist/authorizers/requireWebhookAuthenticity.js +76 -0
- package/dist/authorizers/requireWebhookAuthenticity.js.map +1 -0
- package/dist/browser-guard.d.ts +2 -0
- package/dist/browser-guard.d.ts.map +1 -0
- package/dist/browser-guard.js +19 -0
- package/dist/browser-guard.js.map +1 -0
- package/dist/handlers/create.d.ts +52 -0
- package/dist/handlers/create.d.ts.map +1 -0
- package/dist/handlers/create.js +208 -0
- package/dist/handlers/create.js.map +1 -0
- package/dist/handlers/del.d.ts +30 -0
- package/dist/handlers/del.d.ts.map +1 -0
- package/dist/handlers/del.js +55 -0
- package/dist/handlers/del.js.map +1 -0
- package/dist/handlers/forgotPassword.d.ts +34 -0
- package/dist/handlers/forgotPassword.d.ts.map +1 -0
- package/dist/handlers/forgotPassword.js +119 -0
- package/dist/handlers/forgotPassword.js.map +1 -0
- package/dist/handlers/index.d.ts +16 -0
- package/dist/handlers/index.d.ts.map +1 -0
- package/dist/handlers/index.js +16 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/logIn.d.ts +91 -0
- package/dist/handlers/logIn.d.ts.map +1 -0
- package/dist/handlers/logIn.js +223 -0
- package/dist/handlers/logIn.js.map +1 -0
- package/dist/handlers/logInOAuth.d.ts +66 -0
- package/dist/handlers/logInOAuth.d.ts.map +1 -0
- package/dist/handlers/logInOAuth.js +462 -0
- package/dist/handlers/logInOAuth.js.map +1 -0
- package/dist/handlers/logout.d.ts +22 -0
- package/dist/handlers/logout.d.ts.map +1 -0
- package/dist/handlers/logout.js +57 -0
- package/dist/handlers/logout.js.map +1 -0
- package/dist/handlers/oauthAuthorize.d.ts +44 -0
- package/dist/handlers/oauthAuthorize.d.ts.map +1 -0
- package/dist/handlers/oauthAuthorize.js +141 -0
- package/dist/handlers/oauthAuthorize.js.map +1 -0
- package/dist/handlers/payments/handlePaymentNotification.d.ts +28 -0
- package/dist/handlers/payments/handlePaymentNotification.d.ts.map +1 -0
- package/dist/handlers/payments/handlePaymentNotification.js +217 -0
- package/dist/handlers/payments/handlePaymentNotification.js.map +1 -0
- package/dist/handlers/payments/registry.d.ts +13 -0
- package/dist/handlers/payments/registry.d.ts.map +1 -0
- package/dist/handlers/payments/registry.js +13 -0
- package/dist/handlers/payments/registry.js.map +1 -0
- package/dist/handlers/payments/verifyPayment.d.ts +37 -0
- package/dist/handlers/payments/verifyPayment.d.ts.map +1 -0
- package/dist/handlers/payments/verifyPayment.js +383 -0
- package/dist/handlers/payments/verifyPayment.js.map +1 -0
- package/dist/handlers/read.d.ts +17 -0
- package/dist/handlers/read.d.ts.map +1 -0
- package/dist/handlers/read.js +60 -0
- package/dist/handlers/read.js.map +1 -0
- package/dist/handlers/readSelf.d.ts +23 -0
- package/dist/handlers/readSelf.d.ts.map +1 -0
- package/dist/handlers/readSelf.js +77 -0
- package/dist/handlers/readSelf.js.map +1 -0
- package/dist/handlers/resetPassword.d.ts +40 -0
- package/dist/handlers/resetPassword.d.ts.map +1 -0
- package/dist/handlers/resetPassword.js +147 -0
- package/dist/handlers/resetPassword.js.map +1 -0
- package/dist/handlers/update.d.ts +28 -0
- package/dist/handlers/update.d.ts.map +1 -0
- package/dist/handlers/update.js +250 -0
- package/dist/handlers/update.js.map +1 -0
- package/dist/handlers/updatePassword.d.ts +35 -0
- package/dist/handlers/updatePassword.d.ts.map +1 -0
- package/dist/handlers/updatePassword.js +113 -0
- package/dist/handlers/updatePassword.js.map +1 -0
- package/dist/handlers/updatePlan.d.ts +41 -0
- package/dist/handlers/updatePlan.d.ts.map +1 -0
- package/dist/handlers/updatePlan.js +242 -0
- package/dist/handlers/updatePlan.js.map +1 -0
- package/dist/handlers/verifyTwoFactor.d.ts +46 -0
- package/dist/handlers/verifyTwoFactor.d.ts.map +1 -0
- package/dist/handlers/verifyTwoFactor.js +162 -0
- package/dist/handlers/verifyTwoFactor.js.map +1 -0
- package/dist/i18n.d.ts +10 -0
- package/dist/i18n.d.ts.map +1 -0
- package/dist/i18n.js +25 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +88 -0
- package/dist/index.js.map +1 -0
- package/dist/requestHandlerMap.d.ts +44 -0
- package/dist/requestHandlerMap.d.ts.map +1 -0
- package/dist/requestHandlerMap.js +68 -0
- package/dist/requestHandlerMap.js.map +1 -0
- package/dist/resource.d.ts +22 -0
- package/dist/resource.d.ts.map +1 -0
- package/dist/resource.js +23 -0
- package/dist/resource.js.map +1 -0
- package/dist/routes.d.ts +44 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +121 -0
- package/dist/routes.js.map +1 -0
- package/dist/schema.d.ts +193 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +229 -0
- package/dist/schema.js.map +1 -0
- package/dist/secrets.d.ts +16 -0
- package/dist/secrets.d.ts.map +1 -0
- package/dist/secrets.js +28 -0
- package/dist/secrets.js.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/utilities/getPlan.d.ts +19 -0
- package/dist/utilities/getPlan.d.ts.map +1 -0
- package/dist/utilities/getPlan.js +26 -0
- package/dist/utilities/getPlan.js.map +1 -0
- package/dist/utilities/hashResetToken.d.ts +16 -0
- package/dist/utilities/hashResetToken.d.ts.map +1 -0
- package/dist/utilities/hashResetToken.js +19 -0
- package/dist/utilities/hashResetToken.js.map +1 -0
- package/dist/utilities/index.d.ts +5 -0
- package/dist/utilities/index.d.ts.map +1 -0
- package/dist/utilities/index.js +5 -0
- package/dist/utilities/index.js.map +1 -0
- package/dist/utilities/invalidateEntitlements.d.ts +24 -0
- package/dist/utilities/invalidateEntitlements.d.ts.map +1 -0
- package/dist/utilities/invalidateEntitlements.js +41 -0
- package/dist/utilities/invalidateEntitlements.js.map +1 -0
- package/dist/utilities/normalizeEmail.d.ts +22 -0
- package/dist/utilities/normalizeEmail.d.ts.map +1 -0
- package/dist/utilities/normalizeEmail.js +28 -0
- package/dist/utilities/normalizeEmail.js.map +1 -0
- package/dist/utilities/notify.d.ts +21 -0
- package/dist/utilities/notify.d.ts.map +1 -0
- package/dist/utilities/notify.js +39 -0
- package/dist/utilities/notify.js.map +1 -0
- package/dist/utilities/stripSensitiveUserColumns.d.ts +25 -0
- package/dist/utilities/stripSensitiveUserColumns.d.ts.map +1 -0
- package/dist/utilities/stripSensitiveUserColumns.js +43 -0
- package/dist/utilities/stripSensitiveUserColumns.js.map +1 -0
- package/package.json +94 -0
- package/setup/users.sql +22 -0
- package/setup/usersSecrets.sql +9 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { get, getAnalytics, getLogger } from '@molecule/api-bond';
|
|
3
|
+
import { findById, findOne, updateById } from '@molecule/api-database';
|
|
4
|
+
import { t } from '@molecule/api-i18n';
|
|
5
|
+
import { compare, hash } from '@molecule/api-password';
|
|
6
|
+
import * as authorization from '../authorization.js';
|
|
7
|
+
import { hashResetToken } from '../utilities/hashResetToken.js';
|
|
8
|
+
import { normalizeEmail } from '../utilities/normalizeEmail.js';
|
|
9
|
+
import { notify } from '../utilities/notify.js';
|
|
10
|
+
const analytics = getAnalytics();
|
|
11
|
+
const logger = getLogger();
|
|
12
|
+
/**
|
|
13
|
+
* Cached, real bcrypt hash used for the dummy compare on the user-not-found path.
|
|
14
|
+
*
|
|
15
|
+
* Computed lazily (not at import time) via the password bond — which honors the
|
|
16
|
+
* configured cost (`SALT_ROUNDS`) — because the bond may not be wired when this
|
|
17
|
+
* module loads. A previous implementation used a hardcoded literal that was
|
|
18
|
+
* structurally malformed (54 chars after the cost prefix instead of 53), so
|
|
19
|
+
* `compare()` short-circuited to `false` in ~0ms WITHOUT running the KDF,
|
|
20
|
+
* defeating the timing-equalization defense and reintroducing a user-enumeration
|
|
21
|
+
* oracle. Deriving the dummy hash from the bond's `hash()` guarantees it is
|
|
22
|
+
* valid and matches the exact work factor of real password hashes.
|
|
23
|
+
*/
|
|
24
|
+
let dummyHash;
|
|
25
|
+
/**
|
|
26
|
+
* Logs in a user by username or email. Supports password authentication, password reset token
|
|
27
|
+
* authentication (1-hour expiry), and optional two-factor verification via `@molecule/api-two-factor`.
|
|
28
|
+
* On success, creates/updates a device via the bonded DeviceService, sets JWT authorization,
|
|
29
|
+
* and notifies other devices about the new login.
|
|
30
|
+
* @param resource - The user resource configuration (name, tableName, schema).
|
|
31
|
+
* @param resource.name - The resource name.
|
|
32
|
+
* @param resource.tableName - The database table name for users.
|
|
33
|
+
* @param resource.schema - The validation schema for user properties.
|
|
34
|
+
* @returns A request handler that responds with `{ statusCode: 200, body: { props } }` on success,
|
|
35
|
+
* or `{ statusCode: 206, body: { twoFactorRequired: true } }` when 2FA is needed.
|
|
36
|
+
*/
|
|
37
|
+
export const logIn = ({ name: _name, tableName, schema: _schema }) => {
|
|
38
|
+
return async (req, res) => {
|
|
39
|
+
const { body } = req;
|
|
40
|
+
try {
|
|
41
|
+
// Get the user by username or email.
|
|
42
|
+
let user;
|
|
43
|
+
if (body.username) {
|
|
44
|
+
user =
|
|
45
|
+
(await findOne(tableName, [
|
|
46
|
+
{ field: 'username', operator: '=', value: body.username },
|
|
47
|
+
])) ?? undefined;
|
|
48
|
+
}
|
|
49
|
+
else if (body.email) {
|
|
50
|
+
// Emails are stored normalized (lowercased) at signup — look them up the
|
|
51
|
+
// same way, or a mixed-case login (`User@x.com`) never matches its stored
|
|
52
|
+
// lowercase form and the user is locked out of their own account.
|
|
53
|
+
const normalizedEmail = normalizeEmail(body.email);
|
|
54
|
+
if (normalizedEmail) {
|
|
55
|
+
user =
|
|
56
|
+
(await findOne(tableName, [
|
|
57
|
+
{ field: 'email', operator: '=', value: normalizedEmail },
|
|
58
|
+
])) ?? undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!user) {
|
|
62
|
+
// Perform a dummy password compare to prevent timing-based user enumeration.
|
|
63
|
+
// Without this, the ~100-300ms difference between "user not found" (instant)
|
|
64
|
+
// and "wrong password" (bcrypt cost) reveals whether an account exists.
|
|
65
|
+
// The dummy hash MUST be a real, correctly-costed hash so the KDF actually
|
|
66
|
+
// runs at the same work factor as the wrong-password path (see dummyHash docs).
|
|
67
|
+
if (body.password) {
|
|
68
|
+
try {
|
|
69
|
+
dummyHash ??= await hash('molecule-dummy-password');
|
|
70
|
+
await compare(body.password, dummyHash);
|
|
71
|
+
}
|
|
72
|
+
catch (_error) {
|
|
73
|
+
// Best-effort timing equalizer only: a hashing/compare failure must not
|
|
74
|
+
// alter the response or surface an error — the not-found path always
|
|
75
|
+
// returns the same generic invalid-credentials result below.
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
analytics
|
|
79
|
+
.track({ name: 'user.login_failed', properties: { reason: 'invalid_credentials' } })
|
|
80
|
+
.catch(() => { });
|
|
81
|
+
return {
|
|
82
|
+
statusCode: 403,
|
|
83
|
+
body: {
|
|
84
|
+
error: t('user.error.invalidCredentials'),
|
|
85
|
+
errorKey: 'user.error.invalidCredentials',
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// Get the user's secrets.
|
|
90
|
+
const secrets = await findById(`${tableName}Secrets`, user.id);
|
|
91
|
+
if (!secrets) {
|
|
92
|
+
return {
|
|
93
|
+
statusCode: 403,
|
|
94
|
+
body: {
|
|
95
|
+
error: t('user.error.invalidCredentials'),
|
|
96
|
+
errorKey: 'user.error.invalidCredentials',
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
let authenticated = false;
|
|
101
|
+
// Try password authentication.
|
|
102
|
+
if (body.password && secrets.passwordHash) {
|
|
103
|
+
authenticated = await compare(body.password, secrets.passwordHash);
|
|
104
|
+
}
|
|
105
|
+
// Try password reset token (constant-time comparison to prevent timing
|
|
106
|
+
// attacks). The stored value is sha256(token) — hash the incoming one.
|
|
107
|
+
if (!authenticated && body.passwordResetToken && secrets.passwordResetToken) {
|
|
108
|
+
const a = Buffer.from(hashResetToken(body.passwordResetToken), 'utf-8');
|
|
109
|
+
const b = Buffer.from(secrets.passwordResetToken, 'utf-8');
|
|
110
|
+
if (a.length === b.length && crypto.timingSafeEqual(a, b)) {
|
|
111
|
+
// Check if the token is still valid (within 1 hour).
|
|
112
|
+
if (secrets.passwordResetTokenAt) {
|
|
113
|
+
const tokenAge = Date.now() - new Date(secrets.passwordResetTokenAt).getTime();
|
|
114
|
+
if (tokenAge < 1000 * 60 * 60) {
|
|
115
|
+
// Clear the reset token BEFORE granting auth to prevent concurrent
|
|
116
|
+
// replay — two requests with the same token must not both succeed.
|
|
117
|
+
await updateById(`${tableName}Secrets`, user.id, {
|
|
118
|
+
passwordResetToken: null,
|
|
119
|
+
passwordResetTokenAt: null,
|
|
120
|
+
});
|
|
121
|
+
authenticated = true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (!authenticated) {
|
|
127
|
+
analytics
|
|
128
|
+
.track({ name: 'user.login_failed', properties: { reason: 'invalid_credentials' } })
|
|
129
|
+
.catch(() => { });
|
|
130
|
+
return {
|
|
131
|
+
statusCode: 403,
|
|
132
|
+
body: {
|
|
133
|
+
error: t('user.error.invalidCredentials'),
|
|
134
|
+
errorKey: 'user.error.invalidCredentials',
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// Check for two-factor authentication.
|
|
139
|
+
if (user.twoFactorEnabled && secrets.twoFactorSecret) {
|
|
140
|
+
if (!body.twoFactorToken) {
|
|
141
|
+
analytics.track({ name: 'user.two_factor_required', userId: user.id }).catch(() => { });
|
|
142
|
+
return { statusCode: 206, body: { twoFactorRequired: true } };
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const twoFactor = await import('@molecule/api-two-factor');
|
|
146
|
+
const verifyResult = await twoFactor.verify({
|
|
147
|
+
secret: secrets.twoFactorSecret,
|
|
148
|
+
token: body.twoFactorToken,
|
|
149
|
+
// Reject a code whose time step was already consumed (replay protection).
|
|
150
|
+
afterTimeStep: secrets.lastTwoFactorTimeStep,
|
|
151
|
+
});
|
|
152
|
+
if (!verifyResult.valid) {
|
|
153
|
+
analytics.track({ name: 'user.two_factor_failed', userId: user.id }).catch(() => { });
|
|
154
|
+
return {
|
|
155
|
+
statusCode: 403,
|
|
156
|
+
body: {
|
|
157
|
+
error: t('user.error.invalidTwoFactorToken'),
|
|
158
|
+
errorKey: 'user.error.invalidTwoFactorToken',
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
// Persist the consumed time step so the same code cannot be replayed
|
|
163
|
+
// on another session/endpoint within its validity window.
|
|
164
|
+
if (verifyResult.timeStep !== undefined) {
|
|
165
|
+
await updateById(`${tableName}Secrets`, user.id, {
|
|
166
|
+
lastTwoFactorTimeStep: verifyResult.timeStep,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
logger.error('2FA verification failed:', error);
|
|
172
|
+
return {
|
|
173
|
+
statusCode: 500,
|
|
174
|
+
body: {
|
|
175
|
+
error: t('user.error.twoFactorVerificationUnavailable'),
|
|
176
|
+
errorKey: 'user.error.twoFactorVerificationUnavailable',
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Create or update device.
|
|
182
|
+
const deviceId = await get('device')?.createOrUpdate(user.id, body.deviceName || 'Unknown');
|
|
183
|
+
if (!deviceId) {
|
|
184
|
+
return {
|
|
185
|
+
statusCode: 500,
|
|
186
|
+
body: {
|
|
187
|
+
error: t('user.error.failedToCreateSession'),
|
|
188
|
+
errorKey: 'user.error.failedToCreateSession',
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
// Set authorization.
|
|
193
|
+
const accessToken = authorization.set(req, res, { userId: user.id, deviceId });
|
|
194
|
+
// Notify other devices about the login.
|
|
195
|
+
notify({
|
|
196
|
+
userId: user.id,
|
|
197
|
+
deviceId,
|
|
198
|
+
title: t('user.notification.newLogin', undefined, {
|
|
199
|
+
locale: user?.locale || 'en',
|
|
200
|
+
}),
|
|
201
|
+
titleKey: 'user.notification.newLogin',
|
|
202
|
+
body: t('user.notification.newLoginBody', { deviceName: body.deviceName || 'Unknown' }, { locale: user?.locale || 'en' }),
|
|
203
|
+
bodyKey: 'user.notification.newLoginBody',
|
|
204
|
+
}).catch(() => { });
|
|
205
|
+
analytics
|
|
206
|
+
.track({
|
|
207
|
+
name: 'user.login',
|
|
208
|
+
userId: user.id,
|
|
209
|
+
properties: { method: body.passwordResetToken ? 'reset_token' : 'password' },
|
|
210
|
+
})
|
|
211
|
+
.catch(() => { });
|
|
212
|
+
return { statusCode: 200, body: { props: user, accessToken, user } };
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
logger.error(error);
|
|
216
|
+
return {
|
|
217
|
+
statusCode: 500,
|
|
218
|
+
body: { error: t('user.error.loginFailed'), errorKey: 'user.error.loginFailed' },
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
};
|
|
223
|
+
//# sourceMappingURL=logIn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logIn.js","sourceRoot":"","sources":["../../src/handlers/logIn.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { MoleculeRequest, MoleculeResponse } from '@molecule/api-resource';
|
|
2
|
+
import type * as types from '../types.js';
|
|
3
|
+
/** Request body for OAuth login, including the OAuth server name, authorization code, and PKCE verifier. */
|
|
4
|
+
export interface LogInOAuthRequest extends MoleculeRequest {
|
|
5
|
+
body: {
|
|
6
|
+
server?: string;
|
|
7
|
+
code?: string;
|
|
8
|
+
state?: string;
|
|
9
|
+
codeVerifier?: string;
|
|
10
|
+
redirect_uri?: string;
|
|
11
|
+
deviceName?: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Logs in or creates a user via OAuth. Verifies the authorization code with the bonded OAuth
|
|
16
|
+
* provider (`get('oauth', serverName)`), then either finds an existing user by oauthServer+oauthId
|
|
17
|
+
* or creates a new one with a unique username. Sets JWT authorization and creates a session device.
|
|
18
|
+
* @param resource - The user resource configuration (name, tableName, schema).
|
|
19
|
+
* @param resource.name - The resource name.
|
|
20
|
+
* @param resource.tableName - The database table name for users.
|
|
21
|
+
* @param resource.schema - The validation schema for user properties.
|
|
22
|
+
* @returns A request handler that responds with `{ statusCode: 200, body: { props } }` on success.
|
|
23
|
+
*/
|
|
24
|
+
export declare const logInOAuth: ({ name, tableName, schema }: types.Resource) => (req: LogInOAuthRequest, res: MoleculeResponse) => Promise<{
|
|
25
|
+
statusCode: number;
|
|
26
|
+
body: {
|
|
27
|
+
error: string;
|
|
28
|
+
errorKey: string;
|
|
29
|
+
twoFactorRequired?: undefined;
|
|
30
|
+
props?: undefined;
|
|
31
|
+
};
|
|
32
|
+
} | {
|
|
33
|
+
statusCode: number;
|
|
34
|
+
body: {
|
|
35
|
+
error: string;
|
|
36
|
+
errorKey: string;
|
|
37
|
+
twoFactorRequired: boolean;
|
|
38
|
+
props?: undefined;
|
|
39
|
+
};
|
|
40
|
+
} | {
|
|
41
|
+
statusCode: number;
|
|
42
|
+
body: {
|
|
43
|
+
props: {
|
|
44
|
+
id: string;
|
|
45
|
+
createdAt: string;
|
|
46
|
+
updatedAt: string;
|
|
47
|
+
username?: string | undefined;
|
|
48
|
+
name?: string | undefined;
|
|
49
|
+
email?: string | null | undefined;
|
|
50
|
+
emailVerified?: boolean | undefined;
|
|
51
|
+
avatar?: string | null | undefined;
|
|
52
|
+
bio?: string | null | undefined;
|
|
53
|
+
twoFactorEnabled?: boolean | undefined;
|
|
54
|
+
oauthServer?: string | undefined;
|
|
55
|
+
oauthId?: string | undefined;
|
|
56
|
+
oauthData?: Record<string, unknown> | undefined;
|
|
57
|
+
planKey?: string | undefined;
|
|
58
|
+
planExpiresAt?: string | undefined;
|
|
59
|
+
planAutoRenews?: boolean | undefined;
|
|
60
|
+
};
|
|
61
|
+
error?: undefined;
|
|
62
|
+
errorKey?: undefined;
|
|
63
|
+
twoFactorRequired?: undefined;
|
|
64
|
+
};
|
|
65
|
+
}>;
|
|
66
|
+
//# sourceMappingURL=logInOAuth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logInOAuth.d.ts","sourceRoot":"","sources":["../../src/handlers/logInOAuth.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAI/E,OAAO,KAAK,KAAK,KAAK,MAAM,aAAa,CAAA;AAuBzC,4GAA4G;AAC5G,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,IAAI,EAAE;QACJ,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GAAI,6BAA6B,KAAK,CAAC,QAAQ,MAOtD,KAAK,iBAAiB,EAAE,KAAK,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAie5D,CAAA"}
|