@oglofus/auth 1.0.1 → 1.1.1
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/README.md +33 -14
- package/dist/core/auth.d.ts +7 -6
- package/dist/core/auth.js +238 -39
- package/dist/core/utils.d.ts +1 -0
- package/dist/core/utils.js +2 -1
- package/dist/core/validators.js +1 -1
- package/dist/errors/index.d.ts +2 -3
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/plugins/email-otp.js +31 -18
- package/dist/plugins/index.d.ts +3 -2
- package/dist/plugins/index.js +3 -2
- package/dist/plugins/magic-link.js +15 -24
- package/dist/plugins/oauth2.d.ts +8 -1
- package/dist/plugins/oauth2.js +72 -39
- package/dist/plugins/organizations.d.ts +1 -1
- package/dist/plugins/organizations.js +58 -9
- package/dist/plugins/passkey.js +22 -31
- package/dist/plugins/password.js +4 -7
- package/dist/plugins/stripe.d.ts +19 -0
- package/dist/plugins/stripe.js +583 -0
- package/dist/plugins/two-factor.js +3 -6
- package/dist/types/adapters.d.ts +58 -23
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.js +2 -2
- package/dist/types/model.d.ts +86 -13
- package/dist/types/plugins.d.ts +91 -5
- package/dist/types/results.d.ts +1 -1
- package/dist/types/results.js +11 -2
- package/package.json +10 -3
package/dist/types/adapters.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { MembershipBase, OrganizationBase, OrganizationRoleCatalog, ProfileCompletionState, SecondFactorMethod, Session, SignInMethodHint, UserBase } from "./model.js";
|
|
1
|
+
import type { MembershipBase, OrganizationBase, OrganizationRoleCatalog, ProfileCompletionState, SecondFactorMethod, Session, SignInMethodHint, StripeCustomerRecord, StripeSubject, StripeSubscriptionSnapshot, UserBase } from "./model.js";
|
|
2
|
+
export type MaybeFound<T> = T | null | undefined;
|
|
2
3
|
export interface UserAdapter<U extends UserBase> {
|
|
3
|
-
findById(id: string): Promise<U
|
|
4
|
-
findByEmail(email: string): Promise<U
|
|
4
|
+
findById(id: string): Promise<MaybeFound<U>>;
|
|
5
|
+
findByEmail(email: string): Promise<MaybeFound<U>>;
|
|
5
6
|
create(input: Omit<U, "id" | "createdAt" | "updatedAt">): Promise<U>;
|
|
6
|
-
update(id: string, patch: Partial<U>): Promise<U
|
|
7
|
+
update(id: string, patch: Partial<U>): Promise<MaybeFound<U>>;
|
|
7
8
|
}
|
|
8
9
|
export interface RateLimitResult {
|
|
9
10
|
allowed: boolean;
|
|
@@ -31,7 +32,7 @@ export interface IdentitySnapshot {
|
|
|
31
32
|
methods: SignInMethodHint[];
|
|
32
33
|
}
|
|
33
34
|
export interface IdentityAdapter {
|
|
34
|
-
findByEmail(email: string): Promise<IdentitySnapshot
|
|
35
|
+
findByEmail(email: string): Promise<MaybeFound<IdentitySnapshot>>;
|
|
35
36
|
}
|
|
36
37
|
export interface PendingProfileRecord<U extends UserBase> extends ProfileCompletionState<U> {
|
|
37
38
|
expiresAt: Date;
|
|
@@ -39,7 +40,7 @@ export interface PendingProfileRecord<U extends UserBase> extends ProfileComplet
|
|
|
39
40
|
}
|
|
40
41
|
export interface PendingProfileAdapter<U extends UserBase> {
|
|
41
42
|
create(record: PendingProfileRecord<U>): Promise<void>;
|
|
42
|
-
findById(pendingProfileId: string): Promise<PendingProfileRecord<U
|
|
43
|
+
findById(pendingProfileId: string): Promise<MaybeFound<PendingProfileRecord<U>>>;
|
|
43
44
|
consume(pendingProfileId: string): Promise<boolean>;
|
|
44
45
|
}
|
|
45
46
|
export interface IdempotencyAdapter {
|
|
@@ -47,18 +48,18 @@ export interface IdempotencyAdapter {
|
|
|
47
48
|
}
|
|
48
49
|
export interface OrganizationAdapter<O extends OrganizationBase> {
|
|
49
50
|
create(input: Omit<O, "id" | "createdAt" | "updatedAt">): Promise<O>;
|
|
50
|
-
findById(organizationId: string): Promise<O
|
|
51
|
-
findBySlug(slug: string): Promise<O
|
|
52
|
-
update(organizationId: string, patch: Partial<O>): Promise<O
|
|
51
|
+
findById(organizationId: string): Promise<MaybeFound<O>>;
|
|
52
|
+
findBySlug(slug: string): Promise<MaybeFound<O>>;
|
|
53
|
+
update(organizationId: string, patch: Partial<O>): Promise<MaybeFound<O>>;
|
|
53
54
|
}
|
|
54
55
|
export interface MembershipAdapter<Role extends string, M extends MembershipBase<Role>> {
|
|
55
56
|
create(input: Omit<M, "id" | "createdAt" | "updatedAt">): Promise<M>;
|
|
56
|
-
findById(membershipId: string): Promise<M
|
|
57
|
-
findByUserAndOrganization(userId: string, organizationId: string): Promise<M
|
|
57
|
+
findById(membershipId: string): Promise<MaybeFound<M>>;
|
|
58
|
+
findByUserAndOrganization(userId: string, organizationId: string): Promise<MaybeFound<M>>;
|
|
58
59
|
listByUser(userId: string): Promise<M[]>;
|
|
59
60
|
listByOrganization(organizationId: string): Promise<M[]>;
|
|
60
|
-
setRole(membershipId: string, role: Role): Promise<M
|
|
61
|
-
setStatus(membershipId: string, status: M["status"]): Promise<M
|
|
61
|
+
setRole(membershipId: string, role: Role): Promise<MaybeFound<M>>;
|
|
62
|
+
setStatus(membershipId: string, status: M["status"]): Promise<MaybeFound<M>>;
|
|
62
63
|
delete(membershipId: string): Promise<void>;
|
|
63
64
|
}
|
|
64
65
|
export interface OrganizationInvite<Role extends string = string> {
|
|
@@ -74,7 +75,7 @@ export interface OrganizationInvite<Role extends string = string> {
|
|
|
74
75
|
}
|
|
75
76
|
export interface OrganizationInviteAdapter<Role extends string = string> {
|
|
76
77
|
create(invite: OrganizationInvite<Role>): Promise<void>;
|
|
77
|
-
findActiveByTokenHash(tokenHash: string): Promise<OrganizationInvite<Role
|
|
78
|
+
findActiveByTokenHash(tokenHash: string): Promise<MaybeFound<OrganizationInvite<Role>>>;
|
|
78
79
|
consume(inviteId: string): Promise<boolean>;
|
|
79
80
|
revoke(inviteId: string): Promise<void>;
|
|
80
81
|
}
|
|
@@ -84,6 +85,37 @@ export interface OrganizationEntitlementsAdapter<Feature extends string, LimitKe
|
|
|
84
85
|
setFeatureOverride(organizationId: string, feature: Feature, enabled: boolean): Promise<void>;
|
|
85
86
|
setLimitOverride(organizationId: string, key: LimitKey, value: number): Promise<void>;
|
|
86
87
|
}
|
|
88
|
+
export interface StripeCustomerAdapter {
|
|
89
|
+
findBySubject(subject: StripeSubject): Promise<MaybeFound<StripeCustomerRecord>>;
|
|
90
|
+
findByStripeCustomerId(stripeCustomerId: string): Promise<MaybeFound<StripeCustomerRecord>>;
|
|
91
|
+
create(record: StripeCustomerRecord): Promise<void>;
|
|
92
|
+
updateByStripeCustomerId(stripeCustomerId: string, patch: Partial<StripeCustomerRecord>): Promise<void>;
|
|
93
|
+
}
|
|
94
|
+
export interface StripeSubscriptionAdapter<Feature extends string, LimitKey extends string> {
|
|
95
|
+
findActiveBySubject(subject: StripeSubject): Promise<MaybeFound<StripeSubscriptionSnapshot<Feature, LimitKey>>>;
|
|
96
|
+
findByStripeSubscriptionId(stripeSubscriptionId: string): Promise<MaybeFound<StripeSubscriptionSnapshot<Feature, LimitKey>>>;
|
|
97
|
+
listBySubject(subject: StripeSubject): Promise<StripeSubscriptionSnapshot<Feature, LimitKey>[]>;
|
|
98
|
+
upsert(snapshot: StripeSubscriptionSnapshot<Feature, LimitKey>): Promise<void>;
|
|
99
|
+
}
|
|
100
|
+
export interface StripeWebhookEventAdapter {
|
|
101
|
+
hasProcessed(eventId: string): Promise<boolean>;
|
|
102
|
+
markProcessed(input: {
|
|
103
|
+
eventId: string;
|
|
104
|
+
processedAt: Date;
|
|
105
|
+
type: string;
|
|
106
|
+
}): Promise<void>;
|
|
107
|
+
}
|
|
108
|
+
export interface StripeTrialUsageAdapter {
|
|
109
|
+
hasUsedTrial(input: {
|
|
110
|
+
subject: StripeSubject;
|
|
111
|
+
planKey: string;
|
|
112
|
+
}): Promise<boolean>;
|
|
113
|
+
markUsedTrial(input: {
|
|
114
|
+
subject: StripeSubject;
|
|
115
|
+
planKey: string;
|
|
116
|
+
usedAt: Date;
|
|
117
|
+
}): Promise<void>;
|
|
118
|
+
}
|
|
87
119
|
export interface OrganizationInviteDeliveryPayload<Role extends string = string> {
|
|
88
120
|
email: string;
|
|
89
121
|
organizationName: string;
|
|
@@ -138,7 +170,7 @@ export interface OutboxAdapter {
|
|
|
138
170
|
markFailed(messageId: string, reason: string, retryAt?: Date): Promise<void>;
|
|
139
171
|
}
|
|
140
172
|
export interface PasswordCredentialAdapter {
|
|
141
|
-
getPasswordHash(userId: string): Promise<string
|
|
173
|
+
getPasswordHash(userId: string): Promise<MaybeFound<string>>;
|
|
142
174
|
setPasswordHash(userId: string, passwordHash: string): Promise<void>;
|
|
143
175
|
}
|
|
144
176
|
export interface OtpChallenge {
|
|
@@ -157,7 +189,7 @@ export interface EmailOtpAdapter {
|
|
|
157
189
|
codeHash: string;
|
|
158
190
|
expiresAt: Date;
|
|
159
191
|
}): Promise<OtpChallenge>;
|
|
160
|
-
findChallengeById(challengeId: string): Promise<OtpChallenge
|
|
192
|
+
findChallengeById(challengeId: string): Promise<MaybeFound<OtpChallenge>>;
|
|
161
193
|
consumeChallenge(challengeId: string): Promise<boolean>;
|
|
162
194
|
incrementAttempts(challengeId: string): Promise<{
|
|
163
195
|
attempts: number;
|
|
@@ -182,7 +214,7 @@ export interface MagicLinkAdapter {
|
|
|
182
214
|
tokenHash: string;
|
|
183
215
|
expiresAt: Date;
|
|
184
216
|
}): Promise<MagicLinkToken>;
|
|
185
|
-
findActiveTokenByHash(tokenHash: string): Promise<MagicLinkToken
|
|
217
|
+
findActiveTokenByHash(tokenHash: string): Promise<MaybeFound<MagicLinkToken>>;
|
|
186
218
|
consumeToken(tokenId: string): Promise<boolean>;
|
|
187
219
|
}
|
|
188
220
|
export interface MagicLinkPluginHandlers {
|
|
@@ -190,7 +222,7 @@ export interface MagicLinkPluginHandlers {
|
|
|
190
222
|
delivery: MagicLinkDeliveryHandler;
|
|
191
223
|
}
|
|
192
224
|
export interface OAuth2AccountAdapter<P extends string> {
|
|
193
|
-
findUserId(provider: P, providerUserId: string): Promise<string
|
|
225
|
+
findUserId(provider: P, providerUserId: string): Promise<MaybeFound<string>>;
|
|
194
226
|
linkAccount(input: {
|
|
195
227
|
userId: string;
|
|
196
228
|
provider: P;
|
|
@@ -210,7 +242,7 @@ export interface PasskeyCredential {
|
|
|
210
242
|
lastUsedAt?: Date;
|
|
211
243
|
}
|
|
212
244
|
export interface PasskeyAdapter {
|
|
213
|
-
findByCredentialId(credentialId: string): Promise<PasskeyCredential
|
|
245
|
+
findByCredentialId(credentialId: string): Promise<MaybeFound<PasskeyCredential>>;
|
|
214
246
|
listByUserId(userId: string): Promise<PasskeyCredential[]>;
|
|
215
247
|
create(credential: PasskeyCredential): Promise<void>;
|
|
216
248
|
updateCounter(credentialId: string, counter: number): Promise<void>;
|
|
@@ -226,7 +258,7 @@ export interface PendingTwoFactorChallenge {
|
|
|
226
258
|
}
|
|
227
259
|
export interface TwoFactorChallengeAdapter {
|
|
228
260
|
create(challenge: PendingTwoFactorChallenge): Promise<void>;
|
|
229
|
-
findById(id: string): Promise<PendingTwoFactorChallenge
|
|
261
|
+
findById(id: string): Promise<MaybeFound<PendingTwoFactorChallenge>>;
|
|
230
262
|
consume(id: string): Promise<boolean>;
|
|
231
263
|
}
|
|
232
264
|
export interface TotpSecret {
|
|
@@ -237,7 +269,7 @@ export interface TotpSecret {
|
|
|
237
269
|
disabledAt?: Date | null;
|
|
238
270
|
}
|
|
239
271
|
export interface TotpAdapter {
|
|
240
|
-
findActiveByUserId(userId: string): Promise<TotpSecret
|
|
272
|
+
findActiveByUserId(userId: string): Promise<MaybeFound<TotpSecret>>;
|
|
241
273
|
upsertActive(userId: string, encryptedSecret: string): Promise<void>;
|
|
242
274
|
disable(userId: string): Promise<void>;
|
|
243
275
|
}
|
|
@@ -254,13 +286,16 @@ export interface RecoveryCodeAdapter {
|
|
|
254
286
|
}
|
|
255
287
|
export interface SessionAdapter {
|
|
256
288
|
create(session: Session): Promise<void>;
|
|
257
|
-
findById(id: string): Promise<Session
|
|
258
|
-
setActiveOrganization(sessionId: string, organizationId?: string): Promise<Session>;
|
|
289
|
+
findById(id: string): Promise<MaybeFound<Session>>;
|
|
259
290
|
revoke(id: string): Promise<void>;
|
|
260
291
|
revokeAllForUser(userId: string): Promise<void>;
|
|
261
292
|
}
|
|
293
|
+
export interface OrganizationSessionAdapter {
|
|
294
|
+
setActiveOrganization(sessionId: string, organizationId?: string): Promise<MaybeFound<Session>>;
|
|
295
|
+
}
|
|
262
296
|
export interface OrganizationsPluginHandlers<O extends OrganizationBase, Role extends string, M extends MembershipBase<Role>, Permission extends string, Feature extends string, LimitKey extends string> {
|
|
263
297
|
organizations: OrganizationAdapter<O>;
|
|
298
|
+
organizationSessions: OrganizationSessionAdapter;
|
|
264
299
|
memberships: MembershipAdapter<Role, M>;
|
|
265
300
|
invites: OrganizationInviteAdapter<Role>;
|
|
266
301
|
inviteDelivery: OrganizationInviteDeliveryHandler<Role>;
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
package/dist/types/model.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type RequireKeys<T, K extends keyof T> = T & {
|
|
|
11
11
|
export type LocalProfileFields<U extends UserBase, K extends keyof U> = Pick<RequireKeys<U, K>, K>;
|
|
12
12
|
export type PrimaryAuthMethod = "password" | "email_otp" | "magic_link" | "oauth2" | "passkey";
|
|
13
13
|
export type AuthMethodName = PrimaryAuthMethod | (string & {});
|
|
14
|
-
export type SecondFactorMethod = "totp" | "
|
|
14
|
+
export type SecondFactorMethod = "totp" | "recovery_code";
|
|
15
15
|
export type AccountDiscoveryMode = "private" | "explicit";
|
|
16
16
|
export type DiscoverIntent = "login" | "register";
|
|
17
17
|
export type SignInMethodHint = {
|
|
@@ -89,31 +89,33 @@ export type OAuth2AuthenticateInput<P extends string> = {
|
|
|
89
89
|
authorizationCode: string;
|
|
90
90
|
redirectUri: string;
|
|
91
91
|
codeVerifier?: string;
|
|
92
|
+
idempotencyKey?: string;
|
|
92
93
|
};
|
|
93
94
|
export type OAuth2RegisterInput<P extends string> = OAuth2AuthenticateInput<P>;
|
|
94
95
|
export type WebAuthnJson = Record<string, unknown>;
|
|
96
|
+
export type VerifiedPasskeyRegistration = {
|
|
97
|
+
credentialId: string;
|
|
98
|
+
publicKey: string;
|
|
99
|
+
counter: number;
|
|
100
|
+
transports?: string[];
|
|
101
|
+
};
|
|
102
|
+
export type VerifiedPasskeyAuthentication = {
|
|
103
|
+
credentialId: string;
|
|
104
|
+
nextCounter: number;
|
|
105
|
+
};
|
|
95
106
|
export type PasskeyAuthenticateInput = {
|
|
96
107
|
method: "passkey";
|
|
97
|
-
|
|
98
|
-
assertion: WebAuthnJson;
|
|
108
|
+
authentication: VerifiedPasskeyAuthentication;
|
|
99
109
|
};
|
|
100
110
|
export type PasskeyRegisterInput<U extends UserBase, K extends keyof U> = {
|
|
101
111
|
method: "passkey";
|
|
102
112
|
email: string;
|
|
103
|
-
|
|
113
|
+
registration: VerifiedPasskeyRegistration;
|
|
104
114
|
} & LocalProfileFields<U, K>;
|
|
105
115
|
export type TwoFactorVerifyInput = {
|
|
106
116
|
method: "totp";
|
|
107
117
|
pendingAuthId: string;
|
|
108
118
|
code: string;
|
|
109
|
-
} | {
|
|
110
|
-
method: "email_otp";
|
|
111
|
-
pendingAuthId: string;
|
|
112
|
-
code: string;
|
|
113
|
-
} | {
|
|
114
|
-
method: "passkey";
|
|
115
|
-
pendingAuthId: string;
|
|
116
|
-
assertion: WebAuthnJson;
|
|
117
119
|
} | {
|
|
118
120
|
method: "recovery_code";
|
|
119
121
|
pendingAuthId: string;
|
|
@@ -121,10 +123,11 @@ export type TwoFactorVerifyInput = {
|
|
|
121
123
|
};
|
|
122
124
|
export type ProfileCompletionState<U extends UserBase> = {
|
|
123
125
|
pendingProfileId: string;
|
|
124
|
-
sourceMethod:
|
|
126
|
+
sourceMethod: AuthMethodName;
|
|
125
127
|
email?: string;
|
|
126
128
|
missingFields: readonly Extract<keyof U, string>[];
|
|
127
129
|
prefill: Partial<U>;
|
|
130
|
+
continuation?: Record<string, unknown>;
|
|
128
131
|
};
|
|
129
132
|
export type CompleteProfileInput<U extends UserBase> = {
|
|
130
133
|
pendingProfileId: string;
|
|
@@ -162,6 +165,76 @@ export type OrganizationEntitlementSnapshot<Feature extends string, LimitKey ext
|
|
|
162
165
|
features: Partial<Record<Feature, boolean>>;
|
|
163
166
|
limits: Partial<Record<LimitKey, number>>;
|
|
164
167
|
};
|
|
168
|
+
export type StripeSubject = {
|
|
169
|
+
kind: "user";
|
|
170
|
+
userId: string;
|
|
171
|
+
} | {
|
|
172
|
+
kind: "organization";
|
|
173
|
+
organizationId: string;
|
|
174
|
+
};
|
|
175
|
+
export type StripeReference = `user:${string}` | `organization:${string}`;
|
|
176
|
+
export type StripeBillingCycle = "monthly" | "annual";
|
|
177
|
+
export type StripeSubscriptionStatus = "trialing" | "active" | "past_due" | "unpaid" | "paused" | "canceled" | "incomplete" | "incomplete_expired";
|
|
178
|
+
export type StripeEntitlementSnapshot<Feature extends string, LimitKey extends string> = {
|
|
179
|
+
planKey?: string;
|
|
180
|
+
status?: StripeSubscriptionStatus;
|
|
181
|
+
features: Partial<Record<Feature, boolean>>;
|
|
182
|
+
limits: Partial<Record<LimitKey, number>>;
|
|
183
|
+
};
|
|
184
|
+
export type StripePlan<Feature extends string, LimitKey extends string> = {
|
|
185
|
+
key: string;
|
|
186
|
+
displayName: string;
|
|
187
|
+
scope: StripeSubject["kind"];
|
|
188
|
+
prices: {
|
|
189
|
+
monthly?: {
|
|
190
|
+
priceId: string;
|
|
191
|
+
};
|
|
192
|
+
annual?: {
|
|
193
|
+
priceId: string;
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
trial?: {
|
|
197
|
+
days: number;
|
|
198
|
+
oncePerSubject?: boolean;
|
|
199
|
+
};
|
|
200
|
+
seats?: {
|
|
201
|
+
enabled: boolean;
|
|
202
|
+
minimum?: number;
|
|
203
|
+
maximum?: number;
|
|
204
|
+
limitKey?: LimitKey;
|
|
205
|
+
};
|
|
206
|
+
features?: Partial<Record<Feature, boolean>>;
|
|
207
|
+
limits?: Partial<Record<LimitKey, number>>;
|
|
208
|
+
metadata?: Record<string, string>;
|
|
209
|
+
};
|
|
210
|
+
export interface StripeCustomerRecord {
|
|
211
|
+
id: string;
|
|
212
|
+
subject: StripeSubject;
|
|
213
|
+
stripeCustomerId: string;
|
|
214
|
+
createdAt: Date;
|
|
215
|
+
updatedAt: Date;
|
|
216
|
+
}
|
|
217
|
+
export interface StripeSubscriptionSnapshot<Feature extends string = string, LimitKey extends string = string> {
|
|
218
|
+
id: string;
|
|
219
|
+
subject: StripeSubject;
|
|
220
|
+
stripeCustomerId: string;
|
|
221
|
+
stripeSubscriptionId: string;
|
|
222
|
+
stripePriceId: string;
|
|
223
|
+
planKey: string;
|
|
224
|
+
status: StripeSubscriptionStatus;
|
|
225
|
+
billingCycle: StripeBillingCycle;
|
|
226
|
+
seats?: number | null;
|
|
227
|
+
cancelAtPeriodEnd: boolean;
|
|
228
|
+
currentPeriodStart?: Date | null;
|
|
229
|
+
currentPeriodEnd?: Date | null;
|
|
230
|
+
trialStartedAt?: Date | null;
|
|
231
|
+
trialEndsAt?: Date | null;
|
|
232
|
+
canceledAt?: Date | null;
|
|
233
|
+
features: Partial<Record<Feature, boolean>>;
|
|
234
|
+
limits: Partial<Record<LimitKey, number>>;
|
|
235
|
+
metadata?: Record<string, string>;
|
|
236
|
+
updatedAt: Date;
|
|
237
|
+
}
|
|
165
238
|
export interface AuthRequestContext {
|
|
166
239
|
requestId?: string;
|
|
167
240
|
ip?: string;
|
package/dist/types/plugins.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { CoreAdapters, OrganizationsPluginHandlers } from "./adapters.js";
|
|
2
|
-
import type { AccountDiscoveryMode, AuthRequestContext, CompleteProfileInput,
|
|
2
|
+
import type { AccountDiscoveryMode, AuthRequestContext, CompleteProfileInput, DiscoverAccountDecision, DiscoverAccountInput, MembershipBase, OrganizationBase, OrganizationEntitlementSnapshot, ProfileCompletionState, SecondFactorMethod, StripeBillingCycle, StripeEntitlementSnapshot, StripePlan, StripeSubject, StripeSubscriptionSnapshot, TwoFactorVerifyInput, UserBase } from "./model.js";
|
|
3
3
|
import type { AuthResult, OperationResult } from "./results.js";
|
|
4
4
|
export interface AuthPluginContext<U extends UserBase> {
|
|
5
5
|
adapters: CoreAdapters<U>;
|
|
6
6
|
now(): Date;
|
|
7
|
+
security?: AuthSecurityConfig;
|
|
7
8
|
request?: AuthRequestContext;
|
|
9
|
+
getPluginApi?<T = unknown>(method: string): T | null;
|
|
8
10
|
}
|
|
9
11
|
export interface BasePlugin<Method extends string, U extends UserBase, ExposedApi extends object = {}> {
|
|
10
12
|
kind: "auth_method" | "domain";
|
|
@@ -12,6 +14,10 @@ export interface BasePlugin<Method extends string, U extends UserBase, ExposedAp
|
|
|
12
14
|
version: string;
|
|
13
15
|
createApi?: (ctx: Omit<AuthPluginContext<U>, "request">) => ExposedApi;
|
|
14
16
|
}
|
|
17
|
+
export interface CompletePendingProfileInput<U extends UserBase> {
|
|
18
|
+
record: ProfileCompletionState<U>;
|
|
19
|
+
user: U;
|
|
20
|
+
}
|
|
15
21
|
export interface AuthMethodPlugin<Method extends string, RegisterInput extends {
|
|
16
22
|
method: Method;
|
|
17
23
|
}, AuthenticateInput extends {
|
|
@@ -35,6 +41,7 @@ export interface AuthMethodPlugin<Method extends string, RegisterInput extends {
|
|
|
35
41
|
authenticate: (ctx: AuthPluginContext<U>, input: AuthenticateInput) => Promise<OperationResult<{
|
|
36
42
|
user: U;
|
|
37
43
|
}>>;
|
|
44
|
+
completePendingProfile?: (ctx: AuthPluginContext<U>, input: CompletePendingProfileInput<U>) => Promise<OperationResult<void>>;
|
|
38
45
|
}
|
|
39
46
|
export interface DomainPlugin<Method extends string, U extends UserBase, ExposedApi extends object = {}> extends BasePlugin<Method, U, ExposedApi> {
|
|
40
47
|
kind: "domain";
|
|
@@ -113,6 +120,13 @@ export interface OrganizationsPluginApi<O extends OrganizationBase, Role extends
|
|
|
113
120
|
organizationId: string;
|
|
114
121
|
membership: M;
|
|
115
122
|
}>>;
|
|
123
|
+
setActiveOrganization(input: {
|
|
124
|
+
sessionId: string;
|
|
125
|
+
organizationId?: string;
|
|
126
|
+
}, request?: AuthRequestContext): Promise<OperationResult<{
|
|
127
|
+
sessionId: string;
|
|
128
|
+
activeOrganizationId: string | null;
|
|
129
|
+
}>>;
|
|
116
130
|
setMemberRole(input: {
|
|
117
131
|
organizationId: string;
|
|
118
132
|
membershipId: string;
|
|
@@ -172,10 +186,85 @@ export interface OrganizationsPluginApi<O extends OrganizationBase, Role extends
|
|
|
172
186
|
remaining?: number;
|
|
173
187
|
}>>;
|
|
174
188
|
}
|
|
189
|
+
export interface StripePluginApi<Feature extends string, LimitKey extends string> {
|
|
190
|
+
createCheckoutSession(input: {
|
|
191
|
+
subject: StripeSubject;
|
|
192
|
+
planKey: string;
|
|
193
|
+
billingCycle: StripeBillingCycle;
|
|
194
|
+
successUrl: string;
|
|
195
|
+
cancelUrl: string;
|
|
196
|
+
seats?: number;
|
|
197
|
+
locale?: string;
|
|
198
|
+
metadata?: Record<string, string>;
|
|
199
|
+
}): Promise<OperationResult<{
|
|
200
|
+
url: string;
|
|
201
|
+
checkoutSessionId: string;
|
|
202
|
+
}>>;
|
|
203
|
+
createBillingPortalSession(input: {
|
|
204
|
+
subject: StripeSubject;
|
|
205
|
+
returnUrl: string;
|
|
206
|
+
locale?: string;
|
|
207
|
+
}): Promise<OperationResult<{
|
|
208
|
+
url: string;
|
|
209
|
+
}>>;
|
|
210
|
+
getSubscription(input: {
|
|
211
|
+
subject: StripeSubject;
|
|
212
|
+
}): Promise<OperationResult<{
|
|
213
|
+
subscription: StripeSubscriptionSnapshot<Feature, LimitKey> | null;
|
|
214
|
+
}>>;
|
|
215
|
+
listSubscriptions(input: {
|
|
216
|
+
subject: StripeSubject;
|
|
217
|
+
}): Promise<OperationResult<{
|
|
218
|
+
subscriptions: StripeSubscriptionSnapshot<Feature, LimitKey>[];
|
|
219
|
+
}>>;
|
|
220
|
+
cancelSubscription(input: {
|
|
221
|
+
subject: StripeSubject;
|
|
222
|
+
subscriptionId?: string;
|
|
223
|
+
atPeriodEnd?: boolean;
|
|
224
|
+
}): Promise<OperationResult<{
|
|
225
|
+
subscription: StripeSubscriptionSnapshot<Feature, LimitKey>;
|
|
226
|
+
}>>;
|
|
227
|
+
resumeSubscription(input: {
|
|
228
|
+
subject: StripeSubject;
|
|
229
|
+
subscriptionId?: string;
|
|
230
|
+
}): Promise<OperationResult<{
|
|
231
|
+
subscription: StripeSubscriptionSnapshot<Feature, LimitKey>;
|
|
232
|
+
}>>;
|
|
233
|
+
changePlan(input: {
|
|
234
|
+
subject: StripeSubject;
|
|
235
|
+
planKey: string;
|
|
236
|
+
billingCycle: StripeBillingCycle;
|
|
237
|
+
subscriptionId?: string;
|
|
238
|
+
seats?: number;
|
|
239
|
+
scheduleAtPeriodEnd?: boolean;
|
|
240
|
+
}): Promise<OperationResult<{
|
|
241
|
+
subscription: StripeSubscriptionSnapshot<Feature, LimitKey>;
|
|
242
|
+
}>>;
|
|
243
|
+
getEntitlements(input: {
|
|
244
|
+
subject: StripeSubject;
|
|
245
|
+
}): Promise<OperationResult<StripeEntitlementSnapshot<Feature, LimitKey>>>;
|
|
246
|
+
handleWebhook(input: {
|
|
247
|
+
rawBody: string | Uint8Array;
|
|
248
|
+
stripeSignature: string;
|
|
249
|
+
}): Promise<OperationResult<{
|
|
250
|
+
processed: true;
|
|
251
|
+
eventId: string;
|
|
252
|
+
}>>;
|
|
253
|
+
}
|
|
175
254
|
export interface OrganizationsPluginConfig<O extends OrganizationBase, Role extends string, M extends MembershipBase<Role>, Permission extends string, Feature extends string, LimitKey extends string, RequiredOrgFields extends keyof O = never> {
|
|
176
255
|
organizationRequiredFields?: readonly Extract<RequiredOrgFields, string>[];
|
|
177
256
|
handlers: OrganizationsPluginHandlers<O, Role, M, Permission, Feature, LimitKey>;
|
|
178
257
|
}
|
|
258
|
+
export type StripePlansResolver<Feature extends string, LimitKey extends string> = readonly StripePlan<Feature, LimitKey>[] | (() => Promise<readonly StripePlan<Feature, LimitKey>[]>);
|
|
259
|
+
export type AuthSecurityRateLimitScope = "discover" | "register" | "authenticate" | "emailOtpRequest" | "magicLinkRequest" | "otpVerify";
|
|
260
|
+
export interface AuthSecurityRateLimitPolicy {
|
|
261
|
+
limit: number;
|
|
262
|
+
windowSeconds: number;
|
|
263
|
+
}
|
|
264
|
+
export interface AuthSecurityConfig {
|
|
265
|
+
rateLimits?: Partial<Record<AuthSecurityRateLimitScope, AuthSecurityRateLimitPolicy>>;
|
|
266
|
+
oauth2IdempotencyTtlSeconds?: number;
|
|
267
|
+
}
|
|
179
268
|
export type AnyMethodPlugin<U extends UserBase> = AuthMethodPlugin<string, any, any, U, any>;
|
|
180
269
|
export type AnyDomainPlugin<U extends UserBase> = DomainPlugin<string, U, any>;
|
|
181
270
|
export type AnyPlugin<U extends UserBase> = AnyMethodPlugin<U> | AnyDomainPlugin<U>;
|
|
@@ -208,6 +297,7 @@ export interface AuthConfig<U extends UserBase, P extends readonly AnyPlugin<U>[
|
|
|
208
297
|
session?: {
|
|
209
298
|
ttlSeconds?: number;
|
|
210
299
|
};
|
|
300
|
+
security?: AuthSecurityConfig;
|
|
211
301
|
validateConfigOnStart?: boolean;
|
|
212
302
|
}
|
|
213
303
|
export interface AuthPublicApi<U extends UserBase, P extends readonly AnyPlugin<U>[]> {
|
|
@@ -217,10 +307,6 @@ export interface AuthPublicApi<U extends UserBase, P extends readonly AnyPlugin<
|
|
|
217
307
|
method<M extends PluginMethodsWithApi<P>>(method: M): PluginApiMap<P>[M];
|
|
218
308
|
verifySecondFactor(input: TwoFactorVerifyInput, request?: AuthRequestContext): Promise<AuthResult<U>>;
|
|
219
309
|
completeProfile(input: CompleteProfileInput<U>, request?: AuthRequestContext): Promise<AuthResult<U>>;
|
|
220
|
-
setActiveOrganization(sessionId: string, organizationId: string, request?: AuthRequestContext): Promise<OperationResult<{
|
|
221
|
-
sessionId: string;
|
|
222
|
-
activeOrganizationId: string;
|
|
223
|
-
}>>;
|
|
224
310
|
validateSession(sessionId: string, request?: AuthRequestContext): Promise<{
|
|
225
311
|
ok: true;
|
|
226
312
|
userId: string;
|
package/dist/types/results.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { AuthError, ProfileCompletionRequiredError, TwoFactorRequiredError } from "../errors/index.js";
|
|
1
2
|
import type { Issue } from "../issues/index.js";
|
|
2
|
-
import type { ProfileCompletionRequiredError, TwoFactorRequiredError, AuthError } from "../errors/index.js";
|
|
3
3
|
import type { UserBase } from "./model.js";
|
|
4
4
|
export type OperationResult<TData> = {
|
|
5
5
|
ok: true;
|
package/dist/types/results.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
export const successResult = (user, sessionId, issues = []) => ({
|
|
1
|
+
export const successResult = (user, sessionId, issues = []) => ({
|
|
2
|
+
ok: true,
|
|
3
|
+
user,
|
|
4
|
+
sessionId,
|
|
5
|
+
issues,
|
|
6
|
+
});
|
|
2
7
|
export const errorResult = (error, issues = error.issues) => ({ ok: false, error, issues });
|
|
3
|
-
export const successOperation = (data, issues = []) => ({
|
|
8
|
+
export const successOperation = (data, issues = []) => ({
|
|
9
|
+
ok: true,
|
|
10
|
+
data,
|
|
11
|
+
issues,
|
|
12
|
+
});
|
|
4
13
|
export const errorOperation = (error, issues = error.issues) => ({ ok: false, error, issues });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oglofus/auth",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Type-safe, plugin-first authentication core",
|
|
5
5
|
"homepage": "https://github.com/oglofus/auth#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"build": "tsc -p tsconfig.json",
|
|
19
19
|
"typecheck": "tsc -p tsconfig.test.json",
|
|
20
20
|
"test": "tsx --test test/**/*.test.ts",
|
|
21
|
-
"prepublishOnly": "npm run build"
|
|
21
|
+
"prepublishOnly": "npm run build",
|
|
22
|
+
"format": "prettier --write ."
|
|
22
23
|
},
|
|
23
24
|
"files": [
|
|
24
25
|
"dist",
|
|
@@ -26,7 +27,10 @@
|
|
|
26
27
|
"LICENSE"
|
|
27
28
|
],
|
|
28
29
|
"devDependencies": {
|
|
29
|
-
"@types/node": "^25.3.
|
|
30
|
+
"@types/node": "^25.3.5",
|
|
31
|
+
"prettier": "^3.8.1",
|
|
32
|
+
"prettier-plugin-organize-imports": "^4.3.0",
|
|
33
|
+
"stripe": "^20.4.1",
|
|
30
34
|
"tsx": "^4.21.0",
|
|
31
35
|
"typescript": "^5.9.3"
|
|
32
36
|
},
|
|
@@ -42,6 +46,9 @@
|
|
|
42
46
|
"@oslojs/otp": "^1.1.0",
|
|
43
47
|
"arctic": "^3.7.0"
|
|
44
48
|
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"stripe": "^20.4.1"
|
|
51
|
+
},
|
|
45
52
|
"publishConfig": {
|
|
46
53
|
"access": "public"
|
|
47
54
|
}
|