@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.
@@ -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 | null>;
4
- findByEmail(email: string): Promise<U | null>;
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 | null>;
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> | null>;
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 | null>;
51
- findBySlug(slug: string): Promise<O | null>;
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 | null>;
57
- findByUserAndOrganization(userId: string, organizationId: string): Promise<M | null>;
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> | null>;
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 | null>;
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 | null>;
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 | null>;
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 | null>;
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 | null>;
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 | null>;
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 | null>;
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 | null>;
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>;
@@ -1,4 +1,4 @@
1
- export * from "./model.js";
2
1
  export * from "./adapters.js";
3
- export * from "./results.js";
2
+ export * from "./model.js";
4
3
  export * from "./plugins.js";
4
+ export * from "./results.js";
@@ -1,4 +1,4 @@
1
- export * from "./model.js";
2
1
  export * from "./adapters.js";
3
- export * from "./results.js";
2
+ export * from "./model.js";
4
3
  export * from "./plugins.js";
4
+ export * from "./results.js";
@@ -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" | "email_otp" | "passkey" | "recovery_code";
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
- email?: string;
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
- attestation: WebAuthnJson;
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: "oauth2" | "passkey";
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;
@@ -1,10 +1,12 @@
1
1
  import type { CoreAdapters, OrganizationsPluginHandlers } from "./adapters.js";
2
- import type { AccountDiscoveryMode, AuthRequestContext, CompleteProfileInput, DiscoverAccountInput, DiscoverAccountDecision, MembershipBase, OrganizationBase, OrganizationEntitlementSnapshot, SecondFactorMethod, TwoFactorVerifyInput, UserBase } from "./model.js";
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;
@@ -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;
@@ -1,4 +1,13 @@
1
- export const successResult = (user, sessionId, issues = []) => ({ ok: true, 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 = []) => ({ ok: true, 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.0.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.0",
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
  }