@nexpress/core 0.1.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 +69 -0
- package/dist/audit-54XLVCWD.js +14 -0
- package/dist/audit-54XLVCWD.js.map +1 -0
- package/dist/auth.d.ts +640 -0
- package/dist/auth.js +94 -0
- package/dist/auth.js.map +1 -0
- package/dist/can-YLUHRJAB.js +19 -0
- package/dist/can-YLUHRJAB.js.map +1 -0
- package/dist/chunk-2G264RCD.js +68 -0
- package/dist/chunk-2G264RCD.js.map +1 -0
- package/dist/chunk-2YDGE7YX.js +92 -0
- package/dist/chunk-2YDGE7YX.js.map +1 -0
- package/dist/chunk-473S4TER.js +538 -0
- package/dist/chunk-473S4TER.js.map +1 -0
- package/dist/chunk-4ZLMEKFX.js +18 -0
- package/dist/chunk-4ZLMEKFX.js.map +1 -0
- package/dist/chunk-55FU6WED.js +179 -0
- package/dist/chunk-55FU6WED.js.map +1 -0
- package/dist/chunk-6YI5K2TI.js +1959 -0
- package/dist/chunk-6YI5K2TI.js.map +1 -0
- package/dist/chunk-BHK3AD3Q.js +41 -0
- package/dist/chunk-BHK3AD3Q.js.map +1 -0
- package/dist/chunk-CRUQBZUF.js +39 -0
- package/dist/chunk-CRUQBZUF.js.map +1 -0
- package/dist/chunk-CTSQ7BRI.js +175 -0
- package/dist/chunk-CTSQ7BRI.js.map +1 -0
- package/dist/chunk-DK2JBJH7.js +81 -0
- package/dist/chunk-DK2JBJH7.js.map +1 -0
- package/dist/chunk-DP2PREDU.js +597 -0
- package/dist/chunk-DP2PREDU.js.map +1 -0
- package/dist/chunk-EQ2Z3KMD.js +24 -0
- package/dist/chunk-EQ2Z3KMD.js.map +1 -0
- package/dist/chunk-FZ7O6DWI.js +305 -0
- package/dist/chunk-FZ7O6DWI.js.map +1 -0
- package/dist/chunk-ISLYFQWL.js +1270 -0
- package/dist/chunk-ISLYFQWL.js.map +1 -0
- package/dist/chunk-JJL74ZPK.js +68 -0
- package/dist/chunk-JJL74ZPK.js.map +1 -0
- package/dist/chunk-JKXAPSU4.js +24 -0
- package/dist/chunk-JKXAPSU4.js.map +1 -0
- package/dist/chunk-KU5M27ZC.js +24 -0
- package/dist/chunk-KU5M27ZC.js.map +1 -0
- package/dist/chunk-LSHHRDVR.js +34 -0
- package/dist/chunk-LSHHRDVR.js.map +1 -0
- package/dist/chunk-M43PGOQY.js +715 -0
- package/dist/chunk-M43PGOQY.js.map +1 -0
- package/dist/chunk-MEJAHXIO.js +150 -0
- package/dist/chunk-MEJAHXIO.js.map +1 -0
- package/dist/chunk-NUCGHWCF.js +101 -0
- package/dist/chunk-NUCGHWCF.js.map +1 -0
- package/dist/chunk-OK5HOCQI.js +845 -0
- package/dist/chunk-OK5HOCQI.js.map +1 -0
- package/dist/chunk-OROPGO65.js +13 -0
- package/dist/chunk-OROPGO65.js.map +1 -0
- package/dist/chunk-PPAS4SZR.js +176 -0
- package/dist/chunk-PPAS4SZR.js.map +1 -0
- package/dist/chunk-PPBWRKO2.js +171 -0
- package/dist/chunk-PPBWRKO2.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-QO7LAQZH.js +321 -0
- package/dist/chunk-QO7LAQZH.js.map +1 -0
- package/dist/chunk-QVJ2HCAX.js +225 -0
- package/dist/chunk-QVJ2HCAX.js.map +1 -0
- package/dist/chunk-RIPHIRPP.js +68 -0
- package/dist/chunk-RIPHIRPP.js.map +1 -0
- package/dist/chunk-S27S42QY.js +134 -0
- package/dist/chunk-S27S42QY.js.map +1 -0
- package/dist/chunk-SBCVAC2Z.js +40 -0
- package/dist/chunk-SBCVAC2Z.js.map +1 -0
- package/dist/chunk-TFJ4MKPH.js +694 -0
- package/dist/chunk-TFJ4MKPH.js.map +1 -0
- package/dist/chunk-THX3SHYA.js +75 -0
- package/dist/chunk-THX3SHYA.js.map +1 -0
- package/dist/chunk-UGQSQO5B.js +222 -0
- package/dist/chunk-UGQSQO5B.js.map +1 -0
- package/dist/chunk-V2UNHGAP.js +26 -0
- package/dist/chunk-V2UNHGAP.js.map +1 -0
- package/dist/chunk-VGTPQXNQ.js +2790 -0
- package/dist/chunk-VGTPQXNQ.js.map +1 -0
- package/dist/chunk-VNIHXQ7W.js +194 -0
- package/dist/chunk-VNIHXQ7W.js.map +1 -0
- package/dist/chunk-WV272MPW.js +31 -0
- package/dist/chunk-WV272MPW.js.map +1 -0
- package/dist/chunk-X5KKBOUS.js +26 -0
- package/dist/chunk-X5KKBOUS.js.map +1 -0
- package/dist/chunk-XANPEOJC.js +17 -0
- package/dist/chunk-XANPEOJC.js.map +1 -0
- package/dist/chunk-XPVQIHAQ.js +83 -0
- package/dist/chunk-XPVQIHAQ.js.map +1 -0
- package/dist/chunk-ZCINJSS4.js +75 -0
- package/dist/chunk-ZCINJSS4.js.map +1 -0
- package/dist/community.d.ts +1425 -0
- package/dist/community.js +206 -0
- package/dist/community.js.map +1 -0
- package/dist/config-2GDU7PCK.js +32 -0
- package/dist/config-2GDU7PCK.js.map +1 -0
- package/dist/context-MNZ4QXPC.js +16 -0
- package/dist/context-MNZ4QXPC.js.map +1 -0
- package/dist/db-schema.d.ts +4 -0
- package/dist/db-schema.js +102 -0
- package/dist/db-schema.js.map +1 -0
- package/dist/db.d.ts +7 -0
- package/dist/db.js +117 -0
- package/dist/db.js.map +1 -0
- package/dist/digest-SY42GQSU.js +17 -0
- package/dist/digest-SY42GQSU.js.map +1 -0
- package/dist/errors-5OS3S2J3.js +22 -0
- package/dist/errors-5OS3S2J3.js.map +1 -0
- package/dist/host-OBOI4MJK.js +51 -0
- package/dist/host-OBOI4MJK.js.map +1 -0
- package/dist/i18n.d.ts +301 -0
- package/dist/i18n.js +68 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index-B6-_vr_m.d.ts +590 -0
- package/dist/index-CY55LC0u.d.ts +4722 -0
- package/dist/index-CeiTvwbp.d.ts +168 -0
- package/dist/index-XwP1ET8b.d.ts +61 -0
- package/dist/index.d.ts +2037 -0
- package/dist/index.js +2205 -0
- package/dist/index.js.map +1 -0
- package/dist/job-log-VZXWQUDK.js +24 -0
- package/dist/job-log-VZXWQUDK.js.map +1 -0
- package/dist/jobs.d.ts +4 -0
- package/dist/jobs.js +76 -0
- package/dist/jobs.js.map +1 -0
- package/dist/logger-DqGaOU_j.d.ts +29 -0
- package/dist/logger-S7REWDNE.js +16 -0
- package/dist/logger-S7REWDNE.js.map +1 -0
- package/dist/media.d.ts +5 -0
- package/dist/media.js +41 -0
- package/dist/media.js.map +1 -0
- package/dist/mentions-2IHFVSHW.js +23 -0
- package/dist/mentions-2IHFVSHW.js.map +1 -0
- package/dist/mutes-EWAE5FZR.js +21 -0
- package/dist/mutes-EWAE5FZR.js.map +1 -0
- package/dist/notification-prefs-VPJDU7I6.js +21 -0
- package/dist/notification-prefs-VPJDU7I6.js.map +1 -0
- package/dist/observability.d.ts +156 -0
- package/dist/observability.js +32 -0
- package/dist/observability.js.map +1 -0
- package/dist/profanity-adapter-NU2JQSLX.js +12 -0
- package/dist/profanity-adapter-NU2JQSLX.js.map +1 -0
- package/dist/queue-XE5BC75T.js +14 -0
- package/dist/queue-XE5BC75T.js.map +1 -0
- package/dist/rate-limit.d.ts +99 -0
- package/dist/rate-limit.js +14 -0
- package/dist/rate-limit.js.map +1 -0
- package/dist/registry-XIXDEPVI.js +31 -0
- package/dist/registry-XIXDEPVI.js.map +1 -0
- package/dist/reputation-JRL2YQHM.js +11 -0
- package/dist/reputation-JRL2YQHM.js.map +1 -0
- package/dist/routes.d.ts +43 -0
- package/dist/routes.js +12 -0
- package/dist/routes.js.map +1 -0
- package/dist/scheduled-CIQM57HT.js +20 -0
- package/dist/scheduled-CIQM57HT.js.map +1 -0
- package/dist/seo.d.ts +410 -0
- package/dist/seo.js +44 -0
- package/dist/seo.js.map +1 -0
- package/dist/settings-FOBIESPB.js +17 -0
- package/dist/settings-FOBIESPB.js.map +1 -0
- package/dist/spam-adapter-XX3G737Z.js +12 -0
- package/dist/spam-adapter-XX3G737Z.js.map +1 -0
- package/dist/strings-VAE47B2C.js +29 -0
- package/dist/strings-VAE47B2C.js.map +1 -0
- package/dist/templates-IFVJMCJ6.js +12 -0
- package/dist/templates-IFVJMCJ6.js.map +1 -0
- package/dist/types-TlsbXS0T.d.ts +871 -0
- package/package.json +129 -0
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
import { p as NpAccessFunction, n as NpUserRole, e as NpAuthUser } from './types-TlsbXS0T.js';
|
|
2
|
+
export { F as NpPrincipal } from './types-TlsbXS0T.js';
|
|
3
|
+
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
4
|
+
import { Options } from '@node-rs/argon2';
|
|
5
|
+
|
|
6
|
+
declare const authenticated: NpAccessFunction;
|
|
7
|
+
declare const isAdmin: NpAccessFunction;
|
|
8
|
+
declare const isEditorOrAbove: NpAccessFunction;
|
|
9
|
+
declare const isOwnerOrAdmin: NpAccessFunction;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Staff-side JWT helpers. Both access (`np-session`) and refresh
|
|
13
|
+
* (`np-refresh`) cookies are signed with this module; the
|
|
14
|
+
* `use: "access" | "refresh"` claim separates them so a stolen
|
|
15
|
+
* refresh JWT cannot be replayed as a session cookie. Without this
|
|
16
|
+
* separation a leaked 7-day refresh became a 7-day admin bearer
|
|
17
|
+
* because both cookies decoded to the same `{ sub, role, ver }`
|
|
18
|
+
* payload through `verifyToken` (#94).
|
|
19
|
+
*
|
|
20
|
+
* The fix mirrors the member-side fix from #92/#93: the `use` claim
|
|
21
|
+
* is required, no legacy fallback for tokens missing the claim. The
|
|
22
|
+
* cost is one forced re-login for staff sessions issued before the
|
|
23
|
+
* deploy; bounded by the 7-day refresh TTL.
|
|
24
|
+
*/
|
|
25
|
+
type NpTokenUse = "access" | "refresh";
|
|
26
|
+
interface NpTokenPayload {
|
|
27
|
+
sub: string;
|
|
28
|
+
role: NpUserRole;
|
|
29
|
+
ver: number;
|
|
30
|
+
/** Required. `verifyToken` refuses tokens missing this claim so
|
|
31
|
+
* legacy refresh JWTs cannot be smuggled into the session
|
|
32
|
+
* cookie path. */
|
|
33
|
+
use: NpTokenUse;
|
|
34
|
+
/** Random per-token id — needed if rotation lands on the staff
|
|
35
|
+
* side (mirrors the member-side `jti` for #45). Optional today
|
|
36
|
+
* but populated on every newly-minted token. */
|
|
37
|
+
jti?: string;
|
|
38
|
+
iat: number;
|
|
39
|
+
exp: number;
|
|
40
|
+
}
|
|
41
|
+
declare function signToken(user: {
|
|
42
|
+
id: string;
|
|
43
|
+
role: NpUserRole;
|
|
44
|
+
tokenVersion: number;
|
|
45
|
+
}, secret: string, expirationSeconds?: number, tokenUse?: NpTokenUse): Promise<string>;
|
|
46
|
+
/**
|
|
47
|
+
* Verify a staff JWT. When `expectedUse` is provided, refuses tokens
|
|
48
|
+
* whose `use` claim doesn't match — that's how `getSessionUser`
|
|
49
|
+
* rejects a refresh token used as a session cookie and how the
|
|
50
|
+
* refresh route rejects an access token as a refresh trigger.
|
|
51
|
+
*
|
|
52
|
+
* Tokens minted before the `use` claim landed have NO `use` payload
|
|
53
|
+
* field. We refuse those outright rather than treating them as
|
|
54
|
+
* `access` — the prior fallback would let still-live legacy refresh
|
|
55
|
+
* JWTs be smuggled into the session cookie and pass the access
|
|
56
|
+
* check. Cost: staff logged in before this deploy must log in once.
|
|
57
|
+
* Bounded by the refresh-token TTL (default 7 days).
|
|
58
|
+
*/
|
|
59
|
+
declare function verifyToken(token: string, secret: string, expectedUse?: NpTokenUse): Promise<NpTokenPayload>;
|
|
60
|
+
/**
|
|
61
|
+
* True when `err` represents a token-verification failure rather than
|
|
62
|
+
* an unrelated runtime fault (DB outage, misconfiguration, …). Auth
|
|
63
|
+
* helpers use this to keep the existing "bad token → 401" behavior
|
|
64
|
+
* silent while letting infrastructure failures surface as 5xx.
|
|
65
|
+
*
|
|
66
|
+
* Covers:
|
|
67
|
+
* - `NpAuthError` — `verifyToken` / `verifyMemberToken` rejecting a
|
|
68
|
+
* missing or wrong `use` claim, or `verifyCsrf` failing.
|
|
69
|
+
* - `jose.errors.JOSEError` — every JWT signature / format /
|
|
70
|
+
* expiration failure, including subclasses like `JWTExpired`,
|
|
71
|
+
* `JWSSignatureVerificationFailed`, `JWTInvalid`.
|
|
72
|
+
*/
|
|
73
|
+
declare function isTokenVerificationError(err: unknown): boolean;
|
|
74
|
+
|
|
75
|
+
declare const ARGON2_OPTIONS: Options;
|
|
76
|
+
declare function hashPassword(password: string): Promise<string>;
|
|
77
|
+
declare function verifyPassword(passwordHash: string, password: string): Promise<boolean>;
|
|
78
|
+
|
|
79
|
+
declare function verifyCsrf(method: string, cookieToken: string | undefined, headerToken: string | undefined): boolean;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Capability-based authorization (#273).
|
|
83
|
+
*
|
|
84
|
+
* Replaced the previous parallel `hasRole(user, minRole)` /
|
|
85
|
+
* `isStaffMod(user)` model. Naming the *behavior* instead of a role
|
|
86
|
+
* hierarchy means a reviewer spots `can(user, "community.moderate")`
|
|
87
|
+
* on a comment-mod path regardless of how the role table evolves
|
|
88
|
+
* later — and the previous trap where a `hasRole(user, "editor")`
|
|
89
|
+
* check silently dropped moderators is gone by construction.
|
|
90
|
+
*
|
|
91
|
+
* Capability vocabulary:
|
|
92
|
+
* - `content.publish` — change publication state on staff-owned
|
|
93
|
+
* content. Editor or admin.
|
|
94
|
+
* - `content.author` — create / edit content. Author, moderator,
|
|
95
|
+
* editor, or admin (moderators get author-
|
|
96
|
+
* level write access in this model so they
|
|
97
|
+
* can leave moderation notes / pinned
|
|
98
|
+
* replies on the content surface).
|
|
99
|
+
* - `community.moderate` — comment hide/restore, report triage, ban
|
|
100
|
+
* operations. Admin, editor, or moderator.
|
|
101
|
+
* - `admin.manage` — admin-only surfaces (site CRUD,
|
|
102
|
+
* super-admin-adjacent settings).
|
|
103
|
+
*
|
|
104
|
+
* Add new capabilities by extending the union AND the exhaustive
|
|
105
|
+
* switch below — TypeScript will surface the missing branch.
|
|
106
|
+
*/
|
|
107
|
+
type NpCapability = "content.publish" | "content.author" | "community.moderate" | "admin.manage";
|
|
108
|
+
declare function can(user: NpAuthUser | null | undefined, capability: NpCapability): boolean;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* OAuth provider registry — extension point for SSO. A provider plugin
|
|
112
|
+
* (e.g. `@nexpress/plugin-oauth-github`) registers itself at startup
|
|
113
|
+
* via `registerOAuthProvider()`; the framework's `/api/auth/oauth/{id}`
|
|
114
|
+
* routes look it up by id.
|
|
115
|
+
*
|
|
116
|
+
* The provider is responsible for:
|
|
117
|
+
* - Building the authorize URL (`authorize`).
|
|
118
|
+
* - Exchanging the callback code for a normalized profile (`exchange`).
|
|
119
|
+
*
|
|
120
|
+
* The framework owns state-cookie signing, identity ↔ user resolution,
|
|
121
|
+
* session minting, and audit. Providers must NOT touch cookies, the DB,
|
|
122
|
+
* or response objects directly.
|
|
123
|
+
*/
|
|
124
|
+
/**
|
|
125
|
+
* Profile returned from a successful `exchange()`. The framework uses
|
|
126
|
+
* `providerUserId` as the durable identifier — `email` may change at the
|
|
127
|
+
* provider but `providerUserId` should not. If the provider doesn't
|
|
128
|
+
* surface `email`, the framework falls back to creating a synthetic
|
|
129
|
+
* placeholder (`<providerUserId>@<provider>.oauth.local`) so the
|
|
130
|
+
* `np_users.email NOT NULL UNIQUE` constraint is still satisfied.
|
|
131
|
+
*/
|
|
132
|
+
interface OAuthProfile {
|
|
133
|
+
/** Stable per-user id from the provider. Required. */
|
|
134
|
+
providerUserId: string;
|
|
135
|
+
/** Optional — falls back to synthetic if missing. */
|
|
136
|
+
email?: string | null;
|
|
137
|
+
/** Optional — defaults to email local-part on user creation. */
|
|
138
|
+
name?: string | null;
|
|
139
|
+
/** Optional — written into `np_user_oauth_identities.metadata`. */
|
|
140
|
+
avatarUrl?: string | null;
|
|
141
|
+
/** Optional — full payload the provider wants to remember (e.g. scopes). */
|
|
142
|
+
metadata?: Record<string, unknown>;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Inputs the provider receives at the two callback boundaries. The
|
|
146
|
+
* framework picks `redirectUri` from `SITE_URL` (or the request origin
|
|
147
|
+
* in dev) so the provider doesn't have to know its own deployment URL.
|
|
148
|
+
*/
|
|
149
|
+
interface OAuthAuthorizeParams {
|
|
150
|
+
state: string;
|
|
151
|
+
redirectUri: string;
|
|
152
|
+
/**
|
|
153
|
+
* PKCE code verifier (32+ char URL-safe random). The framework
|
|
154
|
+
* generates one for every login and threads it through the state
|
|
155
|
+
* cookie. Providers that don't support PKCE (e.g. GitHub) ignore it;
|
|
156
|
+
* providers that require it (e.g. Google) hash it into the
|
|
157
|
+
* `code_challenge` query param.
|
|
158
|
+
*/
|
|
159
|
+
codeVerifier: string;
|
|
160
|
+
}
|
|
161
|
+
interface OAuthExchangeParams {
|
|
162
|
+
code: string;
|
|
163
|
+
state: string;
|
|
164
|
+
redirectUri: string;
|
|
165
|
+
/** Same verifier minted at /start, recovered from the state cookie. */
|
|
166
|
+
codeVerifier: string;
|
|
167
|
+
}
|
|
168
|
+
interface OAuthProvider {
|
|
169
|
+
/** Stable id used in route paths and `np_user_oauth_identities.provider`. */
|
|
170
|
+
id: string;
|
|
171
|
+
/** Human-readable label for admin UI / login buttons. */
|
|
172
|
+
label?: string;
|
|
173
|
+
/**
|
|
174
|
+
* Returns a fully-qualified URL the framework should redirect the
|
|
175
|
+
* browser to. Async to allow providers that need to mint per-request
|
|
176
|
+
* client credentials.
|
|
177
|
+
*/
|
|
178
|
+
authorize(params: OAuthAuthorizeParams): Promise<string> | string;
|
|
179
|
+
/**
|
|
180
|
+
* Validates the callback and returns the normalized profile.
|
|
181
|
+
* Throwing here aborts the login with `OAUTH_EXCHANGE_FAILED`.
|
|
182
|
+
*/
|
|
183
|
+
exchange(params: OAuthExchangeParams): Promise<OAuthProfile>;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Register a provider. Idempotent: re-registering with the same id
|
|
187
|
+
* overwrites — useful in dev when a plugin's `setup()` runs again on
|
|
188
|
+
* reload.
|
|
189
|
+
*/
|
|
190
|
+
declare function registerOAuthProvider(provider: OAuthProvider): void;
|
|
191
|
+
declare function getOAuthProvider(id: string): OAuthProvider | undefined;
|
|
192
|
+
declare function listOAuthProviders(): OAuthProvider[];
|
|
193
|
+
/** Reset the registry — tests use this between cases. Not for runtime use. */
|
|
194
|
+
declare function resetOAuthProviders(): void;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Resolves an `OAuthProfile` to a real `np_users` row, in this order:
|
|
198
|
+
*
|
|
199
|
+
* 1. Lookup by `(provider, provider_user_id)` — the durable link. This
|
|
200
|
+
* is the only path that survives an email change at the provider.
|
|
201
|
+
* 2. Email-match — if the provider gave us an email and an existing
|
|
202
|
+
* user has it, link the OAuth identity to that user. Lets a staff
|
|
203
|
+
* member who originally signed up with a password later "sign in
|
|
204
|
+
* with Google" and have it just work, without an explicit linking
|
|
205
|
+
* UI.
|
|
206
|
+
* 3. Create — auto-provision a new user with the provider's profile,
|
|
207
|
+
* default role `viewer`. The password column is filled with an
|
|
208
|
+
* unrecoverable Argon2 hash of a random secret so the column
|
|
209
|
+
* constraints are satisfied; the user can later run the
|
|
210
|
+
* forgot-password flow to set a real password if they want one.
|
|
211
|
+
*
|
|
212
|
+
* Side effects: writes a row into `np_user_oauth_identities` for paths
|
|
213
|
+
* 2 and 3, updates `metadata` for path 1.
|
|
214
|
+
*/
|
|
215
|
+
interface ResolveOAuthLoginResult {
|
|
216
|
+
user: ResolvedOAuthUser;
|
|
217
|
+
/** Tells the caller whether this login created the underlying user. */
|
|
218
|
+
created: boolean;
|
|
219
|
+
/** Tells the caller whether this login linked a new identity row. */
|
|
220
|
+
linked: boolean;
|
|
221
|
+
}
|
|
222
|
+
interface ResolvedOAuthUser {
|
|
223
|
+
id: string;
|
|
224
|
+
email: string;
|
|
225
|
+
name: string;
|
|
226
|
+
role: NpUserRole;
|
|
227
|
+
tokenVersion: number;
|
|
228
|
+
}
|
|
229
|
+
interface ResolveOAuthLoginInput {
|
|
230
|
+
provider: string;
|
|
231
|
+
profile: OAuthProfile;
|
|
232
|
+
/** Default role for auto-created users. Defaults to `"viewer"`. */
|
|
233
|
+
defaultRole?: NpUserRole;
|
|
234
|
+
}
|
|
235
|
+
declare function resolveOAuthLogin(input: ResolveOAuthLoginInput): Promise<ResolveOAuthLoginResult>;
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Member-side mirror of `resolveOAuthLogin` (the staff resolver in
|
|
239
|
+
* `oauth-resolve.ts`). Walks the same three-step ladder:
|
|
240
|
+
*
|
|
241
|
+
* 1. Lookup by `(provider, subject)` in `np_member_identities` —
|
|
242
|
+
* durable provider link.
|
|
243
|
+
* 2. Email match — if the profile carries an email, link the
|
|
244
|
+
* identity to the existing `np_members` row.
|
|
245
|
+
* 3. Auto-provision a new member with status=`active`, default
|
|
246
|
+
* password = unrecoverable Argon2 of a random secret. The user
|
|
247
|
+
* can later run forgot-password to set a real password if they
|
|
248
|
+
* want one (or stay SSO-only).
|
|
249
|
+
*
|
|
250
|
+
* Members are kept distinct from staff users at every layer
|
|
251
|
+
* (different table, different cookies, different audience claim on
|
|
252
|
+
* the JWT). This resolver intentionally never touches `np_users`.
|
|
253
|
+
*/
|
|
254
|
+
interface ResolvedOAuthMember {
|
|
255
|
+
id: string;
|
|
256
|
+
email: string;
|
|
257
|
+
handle: string;
|
|
258
|
+
displayName: string;
|
|
259
|
+
status: "active" | "pending" | "suspended" | "deleted";
|
|
260
|
+
tokenVersion: number;
|
|
261
|
+
}
|
|
262
|
+
interface ResolveMemberOAuthLoginInput {
|
|
263
|
+
provider: string;
|
|
264
|
+
profile: OAuthProfile;
|
|
265
|
+
}
|
|
266
|
+
interface ResolveMemberOAuthLoginResult {
|
|
267
|
+
member: ResolvedOAuthMember;
|
|
268
|
+
/** True when this login auto-provisioned the underlying member. */
|
|
269
|
+
created: boolean;
|
|
270
|
+
/** True when this login linked a new identity row (covers steps 2 + 3). */
|
|
271
|
+
linked: boolean;
|
|
272
|
+
}
|
|
273
|
+
declare function resolveMemberOAuthLogin(input: ResolveMemberOAuthLoginInput): Promise<ResolveMemberOAuthLoginResult>;
|
|
274
|
+
|
|
275
|
+
interface OAuthStatePayload {
|
|
276
|
+
providerId: string;
|
|
277
|
+
nonce: string;
|
|
278
|
+
expSeconds: number;
|
|
279
|
+
codeVerifier: string;
|
|
280
|
+
}
|
|
281
|
+
interface IssuedOAuthState {
|
|
282
|
+
/** The serialized state token (cookie + redirect query value). */
|
|
283
|
+
token: string;
|
|
284
|
+
/** The PKCE verifier — also embedded in the token, surfaced here so
|
|
285
|
+
* the route can pass it to `provider.authorize()` without re-parsing. */
|
|
286
|
+
codeVerifier: string;
|
|
287
|
+
}
|
|
288
|
+
declare function issueOAuthState(providerId: string, secret: string): IssuedOAuthState;
|
|
289
|
+
interface VerifyOAuthStateResult {
|
|
290
|
+
ok: boolean;
|
|
291
|
+
payload?: OAuthStatePayload;
|
|
292
|
+
reason?: "format" | "signature" | "expired";
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Strict verification:
|
|
296
|
+
* - Format must be `<payload>.<sig>` with two segments.
|
|
297
|
+
* - HMAC must match (constant-time compare).
|
|
298
|
+
* - `expSeconds` must be in the future.
|
|
299
|
+
* - `providerId` in the payload must match the route's expected provider.
|
|
300
|
+
* - `codeVerifier` must be a non-empty string.
|
|
301
|
+
*/
|
|
302
|
+
declare function verifyOAuthState(token: string, expectedProviderId: string, secret: string): VerifyOAuthStateResult;
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Adapter that bridges any [arctic](https://arctic.js.org/) provider
|
|
306
|
+
* (`new GitHub(...)`, `new Google(...)`, `new Apple(...)`, etc.) to
|
|
307
|
+
* NexPress's `OAuthProvider` interface.
|
|
308
|
+
*
|
|
309
|
+
* Why this exists: arctic ships ~25 maintained providers and handles
|
|
310
|
+
* the OAuth dance — token exchange, PKCE hashing, refresh-token
|
|
311
|
+
* support — so plugin authors only have to write the **profile fetch**
|
|
312
|
+
* (the part that varies most by provider). Our framework still owns
|
|
313
|
+
* state cookies, identity ↔ user resolution, and session minting; this
|
|
314
|
+
* adapter just lets users skip the boilerplate token POST.
|
|
315
|
+
*
|
|
316
|
+
* Usage from a plugin:
|
|
317
|
+
*
|
|
318
|
+
* import { Apple } from "arctic";
|
|
319
|
+
* import { fromArctic, registerOAuthProvider } from "@nexpress/core";
|
|
320
|
+
*
|
|
321
|
+
* registerOAuthProvider(fromArctic(
|
|
322
|
+
* // Factory: framework calls this each request with the freshly-
|
|
323
|
+
* // resolved redirectUri (matters in dev when Next.js may bind a
|
|
324
|
+
* // non-default port).
|
|
325
|
+
* (redirectUri) => new Apple(clientId, teamId, keyId, privateKey, redirectUri),
|
|
326
|
+
* {
|
|
327
|
+
* id: "apple",
|
|
328
|
+
* scopes: ["name", "email"],
|
|
329
|
+
* fetchProfile: async (accessToken, tokens) => {
|
|
330
|
+
* // Apple returns the user payload INSIDE the token response
|
|
331
|
+
* // (not a separate userinfo endpoint) — pull it from
|
|
332
|
+
* // `tokens.idToken()` here and parse the JWT body.
|
|
333
|
+
* return { providerUserId: parseAppleSub(tokens.idToken()), email: null };
|
|
334
|
+
* },
|
|
335
|
+
* },
|
|
336
|
+
* ));
|
|
337
|
+
*/
|
|
338
|
+
/**
|
|
339
|
+
* Minimal slice of arctic's provider classes that the adapter actually
|
|
340
|
+
* needs. Both `GitHub` (no PKCE) and `Google` (PKCE-required) match
|
|
341
|
+
* this — the third positional arg is "second positional" for
|
|
342
|
+
* non-PKCE providers (just unused) and "code verifier" for PKCE ones.
|
|
343
|
+
*
|
|
344
|
+
* Declared structurally so we don't drag arctic into the public type
|
|
345
|
+
* graph of `@nexpress/core`. Plugins that import a real arctic class
|
|
346
|
+
* pass it directly; the structural match keeps the signature lined up.
|
|
347
|
+
*/
|
|
348
|
+
interface ArcticLikeProvider {
|
|
349
|
+
createAuthorizationURL(state: string, ...rest: never[]): URL;
|
|
350
|
+
validateAuthorizationCode(code: string, ...rest: never[]): Promise<ArcticLikeTokens>;
|
|
351
|
+
}
|
|
352
|
+
interface ArcticLikeTokens {
|
|
353
|
+
accessToken(): string;
|
|
354
|
+
hasRefreshToken?(): boolean;
|
|
355
|
+
refreshToken?(): string;
|
|
356
|
+
idToken?(): string;
|
|
357
|
+
}
|
|
358
|
+
interface FromArcticOptions {
|
|
359
|
+
/** Provider id used in route paths and `np_user_oauth_identities.provider`. */
|
|
360
|
+
id: string;
|
|
361
|
+
/** Human label for admin UI / login buttons. */
|
|
362
|
+
label?: string;
|
|
363
|
+
/** Scopes passed to `createAuthorizationURL`. Most providers default
|
|
364
|
+
* to nothing useful — set this. */
|
|
365
|
+
scopes?: string[];
|
|
366
|
+
/**
|
|
367
|
+
* Whether the underlying arctic provider expects a PKCE code verifier
|
|
368
|
+
* as the second arg to `createAuthorizationURL` and
|
|
369
|
+
* `validateAuthorizationCode`. Default `true` (Google, Apple, etc.).
|
|
370
|
+
* Set `false` for non-PKCE providers like GitHub.
|
|
371
|
+
*/
|
|
372
|
+
pkce?: boolean;
|
|
373
|
+
/**
|
|
374
|
+
* Turns an access token (and the full token response, useful for
|
|
375
|
+
* providers like Apple that return the profile in the token) into the
|
|
376
|
+
* normalized `OAuthProfile` consumed by `resolveOAuthLogin`.
|
|
377
|
+
*
|
|
378
|
+
* Throwing aborts the login with `oauth_error=exchange_failed`.
|
|
379
|
+
*/
|
|
380
|
+
fetchProfile: (accessToken: string, tokens: ArcticLikeTokens) => Promise<OAuthProfile>;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Wraps an arctic provider into the framework's `OAuthProvider`
|
|
384
|
+
* shape. The framework calls `authorize` and `exchange`; this adapter
|
|
385
|
+
* builds a fresh arctic instance per request via `factory(redirectUri)`
|
|
386
|
+
* so the redirect URI always matches what the framework computed for
|
|
387
|
+
* THIS request — critical in dev where Next.js may fall back to a
|
|
388
|
+
* non-3000 port and a setup-time-frozen redirectUri would diverge.
|
|
389
|
+
*
|
|
390
|
+
* Arctic provider classes are cheap to construct (just hold the three
|
|
391
|
+
* credential strings), so the per-request factory call has no
|
|
392
|
+
* meaningful cost.
|
|
393
|
+
*/
|
|
394
|
+
declare function fromArctic(factory: (redirectUri: string) => ArcticLikeProvider, opts: FromArcticOptions): OAuthProvider;
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Loose Drizzle handle type — every staff-auth caller passes
|
|
398
|
+
* the same NodePgDatabase, but TS over-narrows when the
|
|
399
|
+
* generated schema record is folded in. Using
|
|
400
|
+
* `Record<string, unknown>` keeps the helper portable across
|
|
401
|
+
* schema generations without surfacing as `any`.
|
|
402
|
+
*/
|
|
403
|
+
type SessionDb = NodePgDatabase<Record<string, unknown>>;
|
|
404
|
+
declare function sha256(input: string): Promise<string>;
|
|
405
|
+
/**
|
|
406
|
+
* Verify a staff JWT and resolve the active user.
|
|
407
|
+
*
|
|
408
|
+
* `expectedUse` defaults to `"access"` because every caller of this
|
|
409
|
+
* helper outside the rotation endpoint reads `np-session` (server
|
|
410
|
+
* components, route handlers, the bootstrap layout). Defaulting
|
|
411
|
+
* means a fresh route or RSC page can't accidentally tolerate a
|
|
412
|
+
* refresh JWT in the session cookie just by forgetting the
|
|
413
|
+
* argument. The rotation route explicitly passes `"refresh"` for
|
|
414
|
+
* its `np-refresh` read.
|
|
415
|
+
*
|
|
416
|
+
* Tokens missing the `use` claim throw via `verifyToken`; we let
|
|
417
|
+
* that propagate so a `NpAuthError` surfaces as 401 at the API
|
|
418
|
+
* layer.
|
|
419
|
+
*/
|
|
420
|
+
declare function verifyTokenFull(token: string, secret: string, db: SessionDb, expectedUse?: NpTokenUse): Promise<NpAuthUser | null>;
|
|
421
|
+
declare function invalidateAllSessions(userId: string, db: SessionDb): Promise<void>;
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Admin-side helpers for listing and revoking OAuth identity links.
|
|
425
|
+
* Both staff (`np_user_oauth_identities`) and member
|
|
426
|
+
* (`np_member_identities`) tables use the same shape: one row per
|
|
427
|
+
* (account, provider) pair, holding the durable provider subject
|
|
428
|
+
* plus arbitrary metadata. These helpers are the source of truth for
|
|
429
|
+
* `/api/admin/users/[id]/identities` and the member equivalent.
|
|
430
|
+
*
|
|
431
|
+
* Revoking does not invalidate sessions — the user / member can
|
|
432
|
+
* re-link by signing in via OAuth again, which creates a fresh
|
|
433
|
+
* identity row through the resolver. Revocation is intentionally
|
|
434
|
+
* reversible because the durable link is the only thing dropped;
|
|
435
|
+
* the underlying account remains.
|
|
436
|
+
*/
|
|
437
|
+
interface NpUserIdentityRow {
|
|
438
|
+
id: string;
|
|
439
|
+
userId: string;
|
|
440
|
+
provider: string;
|
|
441
|
+
providerUserId: string;
|
|
442
|
+
metadata: Record<string, unknown>;
|
|
443
|
+
createdAt: Date;
|
|
444
|
+
updatedAt: Date;
|
|
445
|
+
}
|
|
446
|
+
interface NpMemberIdentityRow {
|
|
447
|
+
id: string;
|
|
448
|
+
memberId: string;
|
|
449
|
+
provider: string;
|
|
450
|
+
subject: string;
|
|
451
|
+
email: string | null;
|
|
452
|
+
metadata: Record<string, unknown>;
|
|
453
|
+
createdAt: Date;
|
|
454
|
+
updatedAt: Date;
|
|
455
|
+
}
|
|
456
|
+
declare function listUserIdentities(userId: string): Promise<NpUserIdentityRow[]>;
|
|
457
|
+
declare function listMemberIdentities(memberId: string): Promise<NpMemberIdentityRow[]>;
|
|
458
|
+
interface RevokeIdentityInput {
|
|
459
|
+
/** Staff user id whose identity is being revoked (`actorKind: "staff"`). */
|
|
460
|
+
staffUserId: string;
|
|
461
|
+
}
|
|
462
|
+
declare function revokeUserIdentity(userId: string, identityId: string, actor: RevokeIdentityInput): Promise<void>;
|
|
463
|
+
declare function revokeMemberIdentity(memberId: string, identityId: string, actor: RevokeIdentityInput): Promise<void>;
|
|
464
|
+
|
|
465
|
+
type NpPasswordResetPurpose = "invite" | "reset";
|
|
466
|
+
interface NpIssuedResetToken {
|
|
467
|
+
/** The raw token — deliver to the user, never persist. */
|
|
468
|
+
token: string;
|
|
469
|
+
/** Matches `np_users.password_reset_expires_at`. */
|
|
470
|
+
expiresAt: Date;
|
|
471
|
+
purpose: NpPasswordResetPurpose;
|
|
472
|
+
}
|
|
473
|
+
interface NpCreateResetTokenOptions {
|
|
474
|
+
userId: string;
|
|
475
|
+
purpose: NpPasswordResetPurpose;
|
|
476
|
+
ttlMs: number;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Issues a new password reset token for `userId`. Stores the **hash** of the
|
|
480
|
+
* token in the `np_users` row alongside the expiry and purpose, then returns
|
|
481
|
+
* the raw token for the caller to deliver (email/link).
|
|
482
|
+
*
|
|
483
|
+
* Any previously-outstanding reset token for the user is replaced.
|
|
484
|
+
*/
|
|
485
|
+
declare function createPasswordResetToken(db: NodePgDatabase<Record<string, unknown>>, options: NpCreateResetTokenOptions): Promise<NpIssuedResetToken>;
|
|
486
|
+
interface NpResetRequestResult {
|
|
487
|
+
userId: string | null;
|
|
488
|
+
name: string | null;
|
|
489
|
+
email: string | null;
|
|
490
|
+
issued: NpIssuedResetToken | null;
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Handles the "forgot password" flow. If the email matches a user, issues a
|
|
494
|
+
* reset token and returns their name so the mailer can personalise the email.
|
|
495
|
+
* If not, silently returns nulls so callers can respond with a constant
|
|
496
|
+
* message and avoid email enumeration.
|
|
497
|
+
*/
|
|
498
|
+
declare function requestPasswordReset(db: NodePgDatabase<Record<string, unknown>>, email: string, ttlMs: number): Promise<NpResetRequestResult>;
|
|
499
|
+
interface NpConsumeResetTokenOptions {
|
|
500
|
+
token: string;
|
|
501
|
+
newPassword: string;
|
|
502
|
+
}
|
|
503
|
+
interface NpConsumeResetTokenResult {
|
|
504
|
+
userId: string;
|
|
505
|
+
email: string;
|
|
506
|
+
purpose: NpPasswordResetPurpose;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Verifies a password reset token and atomically:
|
|
510
|
+
* - sets the new password hash
|
|
511
|
+
* - bumps `tokenVersion` and deletes all sessions (force logout everywhere)
|
|
512
|
+
* - clears the reset columns on the user row
|
|
513
|
+
*
|
|
514
|
+
* Throws `NpValidationError` when the token is unknown, expired, or the
|
|
515
|
+
* password is too short. Uses a single DB transaction for atomicity.
|
|
516
|
+
*/
|
|
517
|
+
declare function consumePasswordResetToken(db: NodePgDatabase<Record<string, unknown>>, options: NpConsumeResetTokenOptions): Promise<NpConsumeResetTokenResult>;
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Member-side JWT helpers. Mirrors `signToken` / `verifyToken` for
|
|
521
|
+
* staff but adds a fixed `aud: "member"` claim so a forged JWT signed
|
|
522
|
+
* for a staff user can't be replayed against member-only routes (and
|
|
523
|
+
* vice-versa).
|
|
524
|
+
*
|
|
525
|
+
* The signing secret is the same `NP_SECRET`; rotating it invalidates
|
|
526
|
+
* both staff and member sessions, which is the desired behavior.
|
|
527
|
+
*
|
|
528
|
+
* Every token gets a random `jti` so two tokens minted within the
|
|
529
|
+
* same second for the same member produce DIFFERENT JWT strings —
|
|
530
|
+
* needed for refresh-token rotation: without it, the rotated token
|
|
531
|
+
* hash would collide with the prior token hash and revocation by
|
|
532
|
+
* tokenHash would still resolve the rotated row.
|
|
533
|
+
*
|
|
534
|
+
* `use: "access" | "refresh"` separates the two token purposes. A
|
|
535
|
+
* refresh JWT cannot be presented as the `np-mb-session` cookie and
|
|
536
|
+
* a session JWT cannot drive the rotation endpoint — without this
|
|
537
|
+
* separation a leaked refresh token effectively became a long-lived
|
|
538
|
+
* bearer access token because both kinds were stored as fungible
|
|
539
|
+
* rows in `np_member_sessions` with no row-level kind column.
|
|
540
|
+
*/
|
|
541
|
+
type NpMemberTokenUse = "access" | "refresh";
|
|
542
|
+
interface NpMemberTokenPayload {
|
|
543
|
+
sub: string;
|
|
544
|
+
aud: "member";
|
|
545
|
+
ver: number;
|
|
546
|
+
/** Required. `verifyMemberToken` refuses tokens missing this claim
|
|
547
|
+
* so legacy refresh JWTs from before #92 cannot be smuggled into
|
|
548
|
+
* the session cookie path (#91 reopen). */
|
|
549
|
+
use: NpMemberTokenUse;
|
|
550
|
+
/** Optional only for the deploy window; new tokens always carry
|
|
551
|
+
* one. */
|
|
552
|
+
jti?: string;
|
|
553
|
+
iat: number;
|
|
554
|
+
exp: number;
|
|
555
|
+
}
|
|
556
|
+
declare function signMemberToken(member: {
|
|
557
|
+
id: string;
|
|
558
|
+
tokenVersion: number;
|
|
559
|
+
}, secret: string, expirationSeconds?: number, tokenUse?: NpMemberTokenUse): Promise<string>;
|
|
560
|
+
/**
|
|
561
|
+
* Verify a member JWT and return the parsed payload. When
|
|
562
|
+
* `expectedUse` is provided, refuses tokens whose `use` claim doesn't
|
|
563
|
+
* match — that's how `getSessionMember` rejects a refresh token used
|
|
564
|
+
* as a session cookie and how the refresh route rejects an access
|
|
565
|
+
* token as a refresh trigger.
|
|
566
|
+
*
|
|
567
|
+
* Tokens minted before the `use` claim landed have NO `use` payload
|
|
568
|
+
* field. We refuse those outright rather than treating them as
|
|
569
|
+
* `access` — the prior fallback let still-live legacy refresh JWTs
|
|
570
|
+
* (already persisted in `np_member_sessions` per #45's fix) be
|
|
571
|
+
* smuggled into the session cookie and pass the access check (#91
|
|
572
|
+
* reopen). The cost: members logged in before this deploy must log
|
|
573
|
+
* in once. That's bounded by the access-token TTL (default 2h);
|
|
574
|
+
* legacy session rows that don't match a new login age out via
|
|
575
|
+
* `expiresAt` within 7 days regardless.
|
|
576
|
+
*/
|
|
577
|
+
declare function verifyMemberToken(token: string, secret: string, expectedUse?: NpMemberTokenUse): Promise<NpMemberTokenPayload>;
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Member-side session lookups, mirroring the staff helpers in session.ts
|
|
581
|
+
* but for `np_members` / `np_member_sessions`. The sha256 helper is
|
|
582
|
+
* reused (sessions store hashed tokens regardless of the principal kind).
|
|
583
|
+
*/
|
|
584
|
+
interface NpMemberAuthRow {
|
|
585
|
+
id: string;
|
|
586
|
+
email: string;
|
|
587
|
+
handle: string;
|
|
588
|
+
displayName: string;
|
|
589
|
+
status: "active" | "pending" | "suspended" | "deleted";
|
|
590
|
+
tokenVersion: number;
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Resolve a member from a verified JWT payload AND the raw access
|
|
594
|
+
* token. We hash the token and require a live row in
|
|
595
|
+
* `np_member_sessions` — without that row check, deleting a session in
|
|
596
|
+
* `/api/members/logout` had no effect and a stolen token kept working
|
|
597
|
+
* until JWT expiry. (#45)
|
|
598
|
+
*
|
|
599
|
+
* Backward-compat: when no `accessToken` is passed (legacy callers in
|
|
600
|
+
* tests / older routes), we fall back to the previous tokenVersion
|
|
601
|
+
* check only. New paths should always pass the token.
|
|
602
|
+
*/
|
|
603
|
+
declare function getMemberFromTokenPayload(db: NodePgDatabase<Record<string, unknown>>, payload: {
|
|
604
|
+
sub: string;
|
|
605
|
+
ver: number;
|
|
606
|
+
}, accessToken?: string): Promise<NpMemberAuthRow | null>;
|
|
607
|
+
/**
|
|
608
|
+
* Bumps a member's tokenVersion + drops every session row, force-logging
|
|
609
|
+
* them out everywhere. Call inside the same transaction as a password
|
|
610
|
+
* change / soft-delete so a leaked old JWT can't outlive the change.
|
|
611
|
+
*/
|
|
612
|
+
declare function invalidateAllMemberSessions(db: NodePgDatabase<Record<string, unknown>>, memberId: string): Promise<void>;
|
|
613
|
+
|
|
614
|
+
interface NpIssuedMemberToken {
|
|
615
|
+
/** The raw token to ship to the user. Never persist. */
|
|
616
|
+
token: string;
|
|
617
|
+
expiresAt: Date;
|
|
618
|
+
}
|
|
619
|
+
declare function createMemberEmailVerifyToken(db: NodePgDatabase<Record<string, unknown>>, memberId: string, ttlMs: number): Promise<NpIssuedMemberToken>;
|
|
620
|
+
interface NpConsumeMemberEmailVerifyResult {
|
|
621
|
+
memberId: string;
|
|
622
|
+
email: string;
|
|
623
|
+
handle: string;
|
|
624
|
+
displayName: string;
|
|
625
|
+
}
|
|
626
|
+
declare function consumeMemberEmailVerifyToken(db: NodePgDatabase<Record<string, unknown>>, token: string): Promise<NpConsumeMemberEmailVerifyResult>;
|
|
627
|
+
interface NpMemberResetRequestResult {
|
|
628
|
+
memberId: string | null;
|
|
629
|
+
displayName: string | null;
|
|
630
|
+
email: string | null;
|
|
631
|
+
issued: NpIssuedMemberToken | null;
|
|
632
|
+
}
|
|
633
|
+
declare function requestMemberPasswordReset(db: NodePgDatabase<Record<string, unknown>>, email: string, ttlMs: number): Promise<NpMemberResetRequestResult>;
|
|
634
|
+
interface NpConsumeMemberResetResult {
|
|
635
|
+
memberId: string;
|
|
636
|
+
email: string;
|
|
637
|
+
}
|
|
638
|
+
declare function consumeMemberPasswordReset(db: NodePgDatabase<Record<string, unknown>>, token: string, newPassword: string): Promise<NpConsumeMemberResetResult>;
|
|
639
|
+
|
|
640
|
+
export { ARGON2_OPTIONS, type ArcticLikeProvider, type ArcticLikeTokens, type FromArcticOptions, type IssuedOAuthState, type NpCapability, type NpConsumeMemberEmailVerifyResult, type NpConsumeMemberResetResult, type NpConsumeResetTokenOptions, type NpConsumeResetTokenResult, type NpCreateResetTokenOptions, type NpIssuedMemberToken, type NpIssuedResetToken, type NpMemberAuthRow, type NpMemberIdentityRow, type NpMemberResetRequestResult, type NpMemberTokenPayload, type NpPasswordResetPurpose, type NpResetRequestResult, type NpTokenPayload, type NpUserIdentityRow, type OAuthAuthorizeParams, type OAuthExchangeParams, type OAuthProfile, type OAuthProvider, type OAuthStatePayload, type ResolveMemberOAuthLoginInput, type ResolveMemberOAuthLoginResult, type ResolveOAuthLoginInput, type ResolveOAuthLoginResult, type ResolvedOAuthMember, type ResolvedOAuthUser, type VerifyOAuthStateResult, authenticated, can, consumeMemberEmailVerifyToken, consumeMemberPasswordReset, consumePasswordResetToken, createMemberEmailVerifyToken, createPasswordResetToken, fromArctic, getMemberFromTokenPayload, getOAuthProvider, hashPassword, invalidateAllMemberSessions, invalidateAllSessions, isAdmin, isEditorOrAbove, isOwnerOrAdmin, isTokenVerificationError, issueOAuthState, listMemberIdentities, listOAuthProviders, listUserIdentities, registerOAuthProvider, requestMemberPasswordReset, requestPasswordReset, resetOAuthProviders, resolveMemberOAuthLogin, resolveOAuthLogin, revokeMemberIdentity, revokeUserIdentity, sha256, signMemberToken, signToken, verifyCsrf, verifyMemberToken, verifyOAuthState, verifyPassword, verifyToken, verifyTokenFull };
|