@holeauth/core 0.0.1-alpha.0 → 0.0.2-alpha.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Robert Kratz
3
+ Copyright (c) 2026 Robert Julian Kratz <contact@holeauth.dev> (https://holeauth.dev)
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,3 +1,3 @@
1
- export { B as BuildCookieInput, C as CSRF_HEADER, a as CookieName, b as CookieSpec, c as buildCookie, d as cookieName, e as deleteCookie, g as generateCsrfToken, f as isProduction, s as serializeCookie, v as verifyCsrf } from '../index-BIXESLma.js';
2
- import '../index-BmYQquGs.js';
1
+ export { B as BuildCookieInput, C as CSRF_HEADER, a as CookieName, b as CookieSpec, c as buildCookie, d as cookieName, e as deleteCookie, g as generateCsrfToken, f as isProduction, s as serializeCookie, v as verifyCsrf } from '../index-BELADTaD.js';
2
+ import '../index-Bt3CyLaq.js';
3
3
  import '../index-CNtnPdzk.js';
@@ -1,3 +1,3 @@
1
- export { u as HoleauthEvent, v as HoleauthEventType } from '../index-BmYQquGs.js';
2
- export { e as emit, s as subscribe, u as unsubscribe } from '../index-DRN-5E_H.js';
1
+ export { v as CoreHoleauthEventType, u as HoleauthEvent, w as HoleauthEventType } from '../index-Bt3CyLaq.js';
2
+ export { e as emit, s as subscribe, u as unsubscribe } from '../index-CngIcZZk.js';
3
3
  import '../index-CNtnPdzk.js';
@@ -1,4 +1,4 @@
1
- export { c as changePassword, a as consumeInvite, b as consumePasswordReset, d as createInvite, e as deleteUser, g as getInviteInfo, f as issuePendingToken, l as listInvites, r as refresh, h as register, j as requestPasswordReset, k as revokeInvite, s as signIn, m as signOut, u as updateUser, v as verifyPendingToken } from '../index-BbEXbI_k.js';
2
- import '../index-BmYQquGs.js';
1
+ export { c as changePassword, a as consumeInvite, b as consumePasswordReset, d as createInvite, e as deleteUser, g as getInviteInfo, f as issuePendingToken, l as listInvites, r as refresh, h as register, j as requestPasswordReset, k as revokeInvite, s as signIn, m as signOut, u as updateUser, v as verifyPendingToken } from '../index-C3lSoShz.js';
2
+ import '../index-Bt3CyLaq.js';
3
3
  import '../index-CNtnPdzk.js';
4
- import '../registry-CZhM1tEB.js';
4
+ import '../registry-B-I45f0J.js';
@@ -1,4 +1,4 @@
1
- import { a as HoleauthConfig } from './index-BmYQquGs.js';
1
+ import { a as HoleauthConfig } from './index-Bt3CyLaq.js';
2
2
 
3
3
  interface CookieSpec {
4
4
  name: string;
@@ -1,18 +1,21 @@
1
1
  import { a as AdapterAuditEvent, A as AdapterUser, U as UserAdapter, S as SessionAdapter, b as AuditLogAdapter, c as AccountAdapter, V as VerificationTokenAdapter, T as TransactionAdapter } from './index-CNtnPdzk.js';
2
2
 
3
3
  /**
4
- * Event type is intentionally an open string. Core emits well-known
5
- * `user.*`, `session.*`, `account.*`, `sso.*` events; plugins emit under
6
- * their own `<pluginId>.<name>` namespace (e.g. `twofa.verified`).
4
+ * Well-known event types emitted by @holeauth/core.
7
5
  *
8
- * Well-known core event names (non-exhaustive):
9
- * - user.registered, user.signed_in, user.signed_out
10
- * - session.created, session.rotated, session.revoked, session.reuse_detected
11
- * - account.linked, account.unlinked
12
- * - sso.authorize, sso.callback_ok, sso.callback_failed
13
- * - plugin.error
6
+ * IDEs will autocomplete these names when calling `subscribe()` or
7
+ * `auth.on()`. The type is intentionally open-ended: plugins emit
8
+ * additional events under their own `<pluginId>.<name>` namespace
9
+ * (e.g. `twofa.verified`, `rbac.group_assigned`). Those names are
10
+ * accepted via the `(string & {})` escape hatch.
14
11
  */
15
- type HoleauthEventType = string;
12
+ type CoreHoleauthEventType = 'user.registered' | 'user.signed_in' | 'user.signed_out' | 'user.updated' | 'user.deleted' | 'session.created' | 'session.rotated' | 'session.revoked' | 'session.reuse_detected' | 'password.changed' | 'password.reset_requested' | 'password.reset_consumed' | 'account.linked' | 'account.unlinked' | 'sso.authorize' | 'sso.callback_ok' | 'sso.callback_failed' | 'invite.created' | 'user.invite_consumed' | 'invite.revoked' | 'plugin.error';
13
+ /**
14
+ * The discriminant of all holeauth events. Extends `CoreHoleauthEventType`
15
+ * with an open-string escape hatch so plugin-namespaced events still type-
16
+ * check, while preserving IDE autocomplete for the well-known names.
17
+ */
18
+ type HoleauthEventType = CoreHoleauthEventType | (string & {});
16
19
  interface HoleauthEvent extends AdapterAuditEvent {
17
20
  type: HoleauthEventType;
18
21
  }
@@ -558,6 +561,22 @@ interface HoleauthInstance {
558
561
  tokens: IssuedTokens;
559
562
  }>;
560
563
  };
564
+ /**
565
+ * Subscribe to an event emitted by this auth instance.
566
+ * Shorthand for `import { events } from '@holeauth/core'; events.subscribe(auth.config, type, handler)`.
567
+ *
568
+ * @param type - Well-known event type (autocompleted) or any plugin-namespaced string.
569
+ * @param handler - Async-safe handler; errors are swallowed to avoid blocking flows.
570
+ * @returns An unsubscribe function.
571
+ *
572
+ * @example
573
+ * ```ts
574
+ * auth.on('user.registered', async (e) => {
575
+ * await assignDefaultGroup(e.userId);
576
+ * });
577
+ * ```
578
+ */
579
+ on(type: HoleauthEventType, handler: (e: HoleauthEvent) => void | Promise<void>): () => void;
561
580
  }
562
581
 
563
- export type { SessionRevokeHookData as A, BaseProviderConfig as B, ChallengeResult as C, SessionRotateHookData as D, HoleauthPlugin as H, InviteClaims as I, LoggerOptions as L, OAuth2ProviderConfig as O, PluginsApi as P, RegistrationConfig as R, SessionData as S, TokenPolicy as T, HoleauthConfig as a, HoleauthInstance as b, ConsumeInviteInput as c, ConsumeInviteResult as d, CreateInviteResult as e, HoleauthAdapters as f, HoleauthHooks as g, HoleauthSecrets as h, InviteInput as i, InviteListEntry as j, IssuedTokens as k, OIDCProviderConfig as l, PluginContext as m, PluginCoreSurface as n, PluginEvents as o, PluginLogger as p, PluginRoute as q, PluginRouteContext as r, ProviderConfig as s, SignInResult as t, HoleauthEvent as u, HoleauthEventType as v, PasswordChangeHookInput as w, PasswordResetHookInput as x, RegisterHookInput as y, SessionIssueHookData as z };
582
+ export type { SessionIssueHookData as A, BaseProviderConfig as B, ChallengeResult as C, SessionRevokeHookData as D, SessionRotateHookData as E, HoleauthPlugin as H, InviteClaims as I, LoggerOptions as L, OAuth2ProviderConfig as O, PluginsApi as P, RegistrationConfig as R, SessionData as S, TokenPolicy as T, HoleauthConfig as a, HoleauthInstance as b, ConsumeInviteInput as c, ConsumeInviteResult as d, CreateInviteResult as e, HoleauthAdapters as f, HoleauthHooks as g, HoleauthSecrets as h, InviteInput as i, InviteListEntry as j, IssuedTokens as k, OIDCProviderConfig as l, PluginContext as m, PluginCoreSurface as n, PluginEvents as o, PluginLogger as p, PluginRoute as q, PluginRouteContext as r, ProviderConfig as s, SignInResult as t, HoleauthEvent as u, CoreHoleauthEventType as v, HoleauthEventType as w, PasswordChangeHookInput as x, PasswordResetHookInput as y, RegisterHookInput as z };
@@ -1,5 +1,5 @@
1
- import { H as HoleauthPlugin, C as ChallengeResult, g as HoleauthHooks, w as PasswordChangeHookInput, x as PasswordResetHookInput, m as PluginContext, n as PluginCoreSurface, o as PluginEvents, p as PluginLogger, q as PluginRoute, r as PluginRouteContext, P as PluginsApi, y as RegisterHookInput, z as SessionIssueHookData, A as SessionRevokeHookData, D as SessionRotateHookData } from './index-BmYQquGs.js';
2
- import { H as HookRunner, P as PluginRegistry, b as buildRegistry, e as emptyRegistry, r as runOnInit } from './registry-CZhM1tEB.js';
1
+ import { H as HoleauthPlugin, C as ChallengeResult, g as HoleauthHooks, x as PasswordChangeHookInput, y as PasswordResetHookInput, m as PluginContext, n as PluginCoreSurface, o as PluginEvents, p as PluginLogger, q as PluginRoute, r as PluginRouteContext, P as PluginsApi, z as RegisterHookInput, A as SessionIssueHookData, D as SessionRevokeHookData, E as SessionRotateHookData } from './index-Bt3CyLaq.js';
2
+ import { H as HookRunner, P as PluginRegistry, b as buildRegistry, e as emptyRegistry, r as runOnInit } from './registry-B-I45f0J.js';
3
3
 
4
4
  /**
5
5
  * Identity helper that preserves the literal `id` on the plugin type so
@@ -1,6 +1,6 @@
1
- import { a as HoleauthConfig, t as SignInResult, k as IssuedTokens, c as ConsumeInviteInput, d as ConsumeInviteResult, i as InviteInput, e as CreateInviteResult, I as InviteClaims, j as InviteListEntry } from './index-BmYQquGs.js';
1
+ import { a as HoleauthConfig, t as SignInResult, k as IssuedTokens, c as ConsumeInviteInput, d as ConsumeInviteResult, i as InviteInput, e as CreateInviteResult, I as InviteClaims, j as InviteListEntry } from './index-Bt3CyLaq.js';
2
2
  import { A as AdapterUser } from './index-CNtnPdzk.js';
3
- import { H as HookRunner } from './registry-CZhM1tEB.js';
3
+ import { H as HookRunner } from './registry-B-I45f0J.js';
4
4
 
5
5
  interface RegisterInput {
6
6
  email: string;
@@ -1,4 +1,4 @@
1
- import { a as HoleauthConfig, s as ProviderConfig, k as IssuedTokens, l as OIDCProviderConfig, O as OAuth2ProviderConfig } from './index-BmYQquGs.js';
1
+ import { a as HoleauthConfig, s as ProviderConfig, k as IssuedTokens, l as OIDCProviderConfig, O as OAuth2ProviderConfig } from './index-Bt3CyLaq.js';
2
2
  import { A as AdapterUser } from './index-CNtnPdzk.js';
3
3
 
4
4
  interface AuthorizeParams {
@@ -76,9 +76,73 @@ interface GithubOptions {
76
76
  /** GitHub OAuth2 provider. Uses REST userinfo (no OIDC). */
77
77
  declare function GithubProvider(opts: GithubOptions): OAuth2ProviderConfig;
78
78
 
79
+ interface DiscordOptions {
80
+ clientId: string;
81
+ clientSecret: string;
82
+ redirectUri: string;
83
+ id?: string;
84
+ scopes?: string[];
85
+ }
86
+ /** Discord OAuth2 provider. Uses REST userinfo (no OIDC discovery). */
87
+ declare function DiscordProvider(opts: DiscordOptions): OAuth2ProviderConfig;
88
+
89
+ interface MicrosoftOptions {
90
+ clientId: string;
91
+ clientSecret: string;
92
+ redirectUri: string;
93
+ /**
94
+ * Microsoft tenant. Common values:
95
+ * - 'common' → both work + school and personal accounts
96
+ * - 'organizations' → work + school only
97
+ * - 'consumers' → personal Microsoft accounts only
98
+ * - '<tenant-guid>' → a specific Azure AD tenant
99
+ *
100
+ * Default: 'common'.
101
+ */
102
+ tenantId?: string;
103
+ id?: string;
104
+ scopes?: string[];
105
+ }
106
+ /**
107
+ * Microsoft Identity Platform (Azure AD / Entra ID) OpenID Connect provider.
108
+ *
109
+ * Note: the issuer for tenant 'common' is technically per-tenant at runtime
110
+ * (`https://login.microsoftonline.com/{tid}/v2.0`). We expose the multi-tenant
111
+ * issuer string; callers that need strict issuer validation should pin a
112
+ * specific tenant ID.
113
+ */
114
+ declare function MicrosoftProvider(opts: MicrosoftOptions): OIDCProviderConfig;
115
+
116
+ interface OIDCOptions {
117
+ /** Provider id (used as the `:provider` URL segment). */
118
+ id: string;
119
+ /** Display name. */
120
+ name?: string;
121
+ clientId: string;
122
+ clientSecret: string;
123
+ redirectUri: string;
124
+ /** OIDC issuer (used for ID token `iss` validation). */
125
+ issuer: string;
126
+ authorizationUrl: string;
127
+ tokenUrl: string;
128
+ userinfoUrl: string;
129
+ scopes?: string[];
130
+ }
131
+ /**
132
+ * Generic OpenID Connect provider. Use this for any spec-compliant OIDC
133
+ * Identity Provider (Keycloak, Auth0, Okta, Authentik, holeauth-as-IDP, …).
134
+ *
135
+ * Endpoint discovery is currently the caller's responsibility — fetch
136
+ * `${issuer}/.well-known/openid-configuration` and pass the resulting URLs.
137
+ */
138
+ declare function OIDCProvider(opts: OIDCOptions): OIDCProviderConfig;
139
+
79
140
  type index_AuthorizeParams = AuthorizeParams;
141
+ declare const index_DiscordProvider: typeof DiscordProvider;
80
142
  declare const index_GithubProvider: typeof GithubProvider;
81
143
  declare const index_GoogleProvider: typeof GoogleProvider;
144
+ declare const index_MicrosoftProvider: typeof MicrosoftProvider;
145
+ declare const index_OIDCProvider: typeof OIDCProvider;
82
146
  type index_TokenExchangeInput = TokenExchangeInput;
83
147
  declare const index_authorize: typeof authorize;
84
148
  declare const index_base64url: typeof base64url;
@@ -91,7 +155,7 @@ declare const index_generateNonce: typeof generateNonce;
91
155
  declare const index_generatePkcePair: typeof generatePkcePair;
92
156
  declare const index_generateState: typeof generateState;
93
157
  declare namespace index {
94
- export { type index_AuthorizeParams as AuthorizeParams, index_GithubProvider as GithubProvider, index_GoogleProvider as GoogleProvider, type index_TokenExchangeInput as TokenExchangeInput, index_authorize as authorize, index_base64url as base64url, index_buildAuthorizeUrl as buildAuthorizeUrl, index_callback as callback, index_exchangeCode as exchangeCode, index_fetchUserInfo as fetchUserInfo, index_findProvider as findProvider, index_generateNonce as generateNonce, index_generatePkcePair as generatePkcePair, index_generateState as generateState };
158
+ export { type index_AuthorizeParams as AuthorizeParams, index_DiscordProvider as DiscordProvider, index_GithubProvider as GithubProvider, index_GoogleProvider as GoogleProvider, index_MicrosoftProvider as MicrosoftProvider, index_OIDCProvider as OIDCProvider, type index_TokenExchangeInput as TokenExchangeInput, index_authorize as authorize, index_base64url as base64url, index_buildAuthorizeUrl as buildAuthorizeUrl, index_callback as callback, index_exchangeCode as exchangeCode, index_fetchUserInfo as fetchUserInfo, index_findProvider as findProvider, index_generateNonce as generateNonce, index_generatePkcePair as generatePkcePair, index_generateState as generateState };
95
159
  }
96
160
 
97
- export { type AuthorizeParams as A, GithubProvider as G, type TokenExchangeInput as T, GoogleProvider as a, authorize as b, base64url as c, buildAuthorizeUrl as d, callback as e, exchangeCode as f, fetchUserInfo as g, findProvider as h, index as i, generateNonce as j, generatePkcePair as k, generateState as l };
161
+ export { type AuthorizeParams as A, DiscordProvider as D, GithubProvider as G, MicrosoftProvider as M, OIDCProvider as O, type TokenExchangeInput as T, GoogleProvider as a, authorize as b, base64url as c, buildAuthorizeUrl as d, callback as e, exchangeCode as f, fetchUserInfo as g, findProvider as h, index as i, generateNonce as j, generatePkcePair as k, generateState as l };
@@ -1,4 +1,4 @@
1
- import { a as HoleauthConfig, u as HoleauthEvent, v as HoleauthEventType } from './index-BmYQquGs.js';
1
+ import { a as HoleauthConfig, u as HoleauthEvent, v as CoreHoleauthEventType, w as HoleauthEventType } from './index-Bt3CyLaq.js';
2
2
 
3
3
  type Handler = (e: HoleauthEvent) => void | Promise<void>;
4
4
  /** Subscribe to an event type. Use '*' to match all events. Returns an unsubscribe fn. */
@@ -14,13 +14,14 @@ declare function unsubscribe(cfg: HoleauthConfig, type: string, handler: Handler
14
14
  */
15
15
  declare function emit(cfg: HoleauthConfig, event: HoleauthEvent): Promise<void>;
16
16
 
17
+ declare const index_CoreHoleauthEventType: typeof CoreHoleauthEventType;
17
18
  declare const index_HoleauthEvent: typeof HoleauthEvent;
18
19
  declare const index_HoleauthEventType: typeof HoleauthEventType;
19
20
  declare const index_emit: typeof emit;
20
21
  declare const index_subscribe: typeof subscribe;
21
22
  declare const index_unsubscribe: typeof unsubscribe;
22
23
  declare namespace index {
23
- export { index_HoleauthEvent as HoleauthEvent, index_HoleauthEventType as HoleauthEventType, index_emit as emit, index_subscribe as subscribe, index_unsubscribe as unsubscribe };
24
+ export { index_CoreHoleauthEventType as CoreHoleauthEventType, index_HoleauthEvent as HoleauthEvent, index_HoleauthEventType as HoleauthEventType, index_emit as emit, index_subscribe as subscribe, index_unsubscribe as unsubscribe };
24
25
  }
25
26
 
26
27
  export { emit as e, index as i, subscribe as s, unsubscribe as u };
@@ -1,4 +1,4 @@
1
- import { a as HoleauthConfig, k as IssuedTokens, S as SessionData, b as HoleauthInstance } from './index-BmYQquGs.js';
1
+ import { a as HoleauthConfig, k as IssuedTokens, S as SessionData, b as HoleauthInstance } from './index-Bt3CyLaq.js';
2
2
 
3
3
  interface IssueInput {
4
4
  userId: string;
@@ -87,10 +87,52 @@ interface GetSessionOrRefreshResult {
87
87
  */
88
88
  declare function getSessionOrRefresh(instance: HoleauthInstance, input: GetSessionOrRefreshInput): Promise<GetSessionOrRefreshResult>;
89
89
 
90
+ interface RequestRefreshResult {
91
+ /** Resolved session, or null if both validation and refresh failed. */
92
+ session: SessionData | null;
93
+ /** Freshly-issued token bundle when a refresh occurred; null otherwise. */
94
+ tokens: IssuedTokens | null;
95
+ /** True when the refresh token was rotated. */
96
+ refreshed: boolean;
97
+ /**
98
+ * Ready-to-forward `Set-Cookie` header values. Empty when no rotation
99
+ * occurred. The caller must append these to its outgoing response.
100
+ */
101
+ setCookieHeaders: string[];
102
+ }
103
+ /**
104
+ * Read cookies from a Web API `Request`, validate the access token, and
105
+ * transparently rotate the refresh token when needed.
106
+ *
107
+ * **Framework-agnostic** — works wherever the Web Fetch `Request` type is
108
+ * available: Next.js App Router route handlers, Hono, plain `fetch` handlers,
109
+ * tRPC fetch adapters, Cloudflare Workers, Deno, etc.
110
+ *
111
+ * The caller is responsible for forwarding `setCookieHeaders` on the response
112
+ * when the result's `refreshed` flag is `true`.
113
+ *
114
+ * @example tRPC context (fetch adapter)
115
+ * ```ts
116
+ * import { getSessionOrRefreshFromRequest } from '@holeauth/core/session';
117
+ *
118
+ * export const createTrpcContext = createHoleauthContext(auth);
119
+ * // internally calls getSessionOrRefreshFromRequest(req, auth)
120
+ * ```
121
+ *
122
+ * @example Manual use in a route handler
123
+ * ```ts
124
+ * const { session, setCookieHeaders } = await getSessionOrRefreshFromRequest(req, auth);
125
+ * for (const c of setCookieHeaders) resHeaders.append('Set-Cookie', c);
126
+ * ```
127
+ */
128
+ declare function getSessionOrRefreshFromRequest(req: Request, instance: HoleauthInstance): Promise<RequestRefreshResult>;
129
+
90
130
  type index_GetSessionOrRefreshInput = GetSessionOrRefreshInput;
91
131
  type index_GetSessionOrRefreshResult = GetSessionOrRefreshResult;
92
132
  type index_IssueInput = IssueInput;
133
+ type index_RequestRefreshResult = RequestRefreshResult;
93
134
  declare const index_getSessionOrRefresh: typeof getSessionOrRefresh;
135
+ declare const index_getSessionOrRefreshFromRequest: typeof getSessionOrRefreshFromRequest;
94
136
  declare const index_issueSession: typeof issueSession;
95
137
  declare const index_revokeAllForUser: typeof revokeAllForUser;
96
138
  declare const index_revokeByRefresh: typeof revokeByRefresh;
@@ -99,7 +141,7 @@ declare const index_rotateRefresh: typeof rotateRefresh;
99
141
  declare const index_sha256b64url: typeof sha256b64url;
100
142
  declare const index_validateSession: typeof validateSession;
101
143
  declare namespace index {
102
- export { type index_GetSessionOrRefreshInput as GetSessionOrRefreshInput, type index_GetSessionOrRefreshResult as GetSessionOrRefreshResult, type index_IssueInput as IssueInput, index_getSessionOrRefresh as getSessionOrRefresh, index_issueSession as issueSession, index_revokeAllForUser as revokeAllForUser, index_revokeByRefresh as revokeByRefresh, index_revokeSession as revokeSession, index_rotateRefresh as rotateRefresh, index_sha256b64url as sha256b64url, index_validateSession as validateSession };
144
+ export { type index_GetSessionOrRefreshInput as GetSessionOrRefreshInput, type index_GetSessionOrRefreshResult as GetSessionOrRefreshResult, type index_IssueInput as IssueInput, type index_RequestRefreshResult as RequestRefreshResult, index_getSessionOrRefresh as getSessionOrRefresh, index_getSessionOrRefreshFromRequest as getSessionOrRefreshFromRequest, index_issueSession as issueSession, index_revokeAllForUser as revokeAllForUser, index_revokeByRefresh as revokeByRefresh, index_revokeSession as revokeSession, index_rotateRefresh as rotateRefresh, index_sha256b64url as sha256b64url, index_validateSession as validateSession };
103
145
  }
104
146
 
105
- export { type GetSessionOrRefreshInput as G, type IssueInput as I, type GetSessionOrRefreshResult as a, issueSession as b, revokeByRefresh as c, revokeSession as d, rotateRefresh as e, getSessionOrRefresh as g, index as i, revokeAllForUser as r, sha256b64url as s, validateSession as v };
147
+ export { type GetSessionOrRefreshInput as G, type IssueInput as I, type RequestRefreshResult as R, type GetSessionOrRefreshResult as a, getSessionOrRefreshFromRequest as b, issueSession as c, revokeByRefresh as d, revokeSession as e, rotateRefresh as f, getSessionOrRefresh as g, index as i, revokeAllForUser as r, sha256b64url as s, validateSession as v };
package/dist/index.d.ts CHANGED
@@ -1,17 +1,17 @@
1
- import { H as HoleauthPlugin, a as HoleauthConfig, b as HoleauthInstance, P as PluginsApi } from './index-BmYQquGs.js';
2
- export { B as BaseProviderConfig, C as ChallengeResult, c as ConsumeInviteInput, d as ConsumeInviteResult, e as CreateInviteResult, f as HoleauthAdapters, g as HoleauthHooks, h as HoleauthSecrets, I as InviteClaims, i as InviteInput, j as InviteListEntry, k as IssuedTokens, L as LoggerOptions, O as OAuth2ProviderConfig, l as OIDCProviderConfig, m as PluginContext, n as PluginCoreSurface, o as PluginEvents, p as PluginLogger, q as PluginRoute, r as PluginRouteContext, s as ProviderConfig, R as RegistrationConfig, S as SessionData, t as SignInResult, T as TokenPolicy } from './index-BmYQquGs.js';
1
+ import { H as HoleauthPlugin, a as HoleauthConfig, b as HoleauthInstance, P as PluginsApi } from './index-Bt3CyLaq.js';
2
+ export { B as BaseProviderConfig, C as ChallengeResult, c as ConsumeInviteInput, d as ConsumeInviteResult, e as CreateInviteResult, f as HoleauthAdapters, g as HoleauthHooks, h as HoleauthSecrets, I as InviteClaims, i as InviteInput, j as InviteListEntry, k as IssuedTokens, L as LoggerOptions, O as OAuth2ProviderConfig, l as OIDCProviderConfig, m as PluginContext, n as PluginCoreSurface, o as PluginEvents, p as PluginLogger, q as PluginRoute, r as PluginRouteContext, s as ProviderConfig, R as RegistrationConfig, S as SessionData, t as SignInResult, T as TokenPolicy } from './index-Bt3CyLaq.js';
3
3
  export { AccountConflictError, AdapterError, CredentialsError, CsrfError, HoleauthError, InvalidTokenError, NotSupportedError, PendingChallengeError, ProviderError, RefreshReuseError, RegistrationDisabledError, SessionExpiredError } from './errors/index.js';
4
4
  export { i as jwt } from './index-CjEXpqaW.js';
5
- export { i as session } from './index-D57PvFMN.js';
5
+ export { i as session } from './index-D0r6eY4L.js';
6
6
  export { i as password } from './index-BYtkmk9_.js';
7
7
  export { i as otp } from './index-BwEvEa8-.js';
8
- export { i as sso } from './index-CHS-socJ.js';
8
+ export { i as sso } from './index-CcMMWIEe.js';
9
9
  export { i as adapters } from './index-CNtnPdzk.js';
10
- export { i as cookies } from './index-BIXESLma.js';
11
- export { i as events } from './index-DRN-5E_H.js';
12
- export { i as flows } from './index-BbEXbI_k.js';
13
- export { d as definePlugin, i as plugins } from './index-CotvcK_b.js';
14
- import { P as PluginRegistry } from './registry-CZhM1tEB.js';
10
+ export { i as cookies } from './index-BELADTaD.js';
11
+ export { i as events } from './index-CngIcZZk.js';
12
+ export { i as flows } from './index-C3lSoShz.js';
13
+ export { d as definePlugin, i as plugins } from './index-BydOBZfs.js';
14
+ import { P as PluginRegistry } from './registry-B-I45f0J.js';
15
15
  import 'jose';
16
16
 
17
17
  /** Framework-binding helper (not part of the public API surface). */
package/dist/index.js CHANGED
@@ -112,6 +112,7 @@ function decode(token) {
112
112
  var session_exports = {};
113
113
  __export(session_exports, {
114
114
  getSessionOrRefresh: () => getSessionOrRefresh,
115
+ getSessionOrRefreshFromRequest: () => getSessionOrRefreshFromRequest,
115
116
  issueSession: () => issueSession,
116
117
  revokeAllForUser: () => revokeAllForUser,
117
118
  revokeByRefresh: () => revokeByRefresh,
@@ -446,6 +447,97 @@ async function getSessionOrRefresh(instance, input) {
446
447
  return { session, tokens, refreshed: true };
447
448
  }
448
449
 
450
+ // src/cookies/spec.ts
451
+ function cookieName(cfg, kind) {
452
+ const prefix = cfg.tokens?.cookiePrefix ?? "holeauth";
453
+ switch (kind) {
454
+ case "access":
455
+ return `${prefix}.at`;
456
+ case "refresh":
457
+ return `${prefix}.rt`;
458
+ case "csrf":
459
+ return `${prefix}.csrf`;
460
+ case "pending":
461
+ return `${prefix}.pending`;
462
+ case "oauthState":
463
+ return `${prefix}.oauth.state`;
464
+ case "oauthPkce":
465
+ return `${prefix}.oauth.pkce`;
466
+ }
467
+ }
468
+ function isProduction() {
469
+ return globalThis.process?.env?.NODE_ENV === "production";
470
+ }
471
+ function buildCookie(cfg, input) {
472
+ const httpOnly = input.httpOnly ?? input.kind !== "csrf";
473
+ const secure = cfg.tokens?.cookieSecure ?? isProduction();
474
+ return {
475
+ name: cookieName(cfg, input.kind),
476
+ value: input.value,
477
+ maxAge: input.maxAge,
478
+ httpOnly,
479
+ secure,
480
+ sameSite: input.sameSite ?? cfg.tokens?.sameSite ?? "lax",
481
+ path: input.path ?? "/",
482
+ domain: cfg.tokens?.cookieDomain
483
+ };
484
+ }
485
+ function serializeCookie(c) {
486
+ const parts = [`${c.name}=${encodeURIComponent(c.value)}`];
487
+ parts.push(`Path=${c.path}`);
488
+ if (c.domain) parts.push(`Domain=${c.domain}`);
489
+ if (c.maxAge !== void 0) {
490
+ parts.push(`Max-Age=${c.maxAge}`);
491
+ if (c.maxAge === 0) parts.push("Expires=Thu, 01 Jan 1970 00:00:00 GMT");
492
+ }
493
+ if (c.httpOnly) parts.push("HttpOnly");
494
+ if (c.secure) parts.push("Secure");
495
+ parts.push(`SameSite=${c.sameSite.charAt(0).toUpperCase()}${c.sameSite.slice(1)}`);
496
+ return parts.join("; ");
497
+ }
498
+ function deleteCookie(cfg, kind) {
499
+ return buildCookie(cfg, { kind, value: "", maxAge: 0 });
500
+ }
501
+
502
+ // src/session/request.ts
503
+ function parseCookies(header) {
504
+ const out = {};
505
+ if (!header) return out;
506
+ for (const part of header.split(";")) {
507
+ const i = part.indexOf("=");
508
+ if (i < 0) continue;
509
+ const k = part.slice(0, i).trim();
510
+ const v = decodeURIComponent(part.slice(i + 1).trim());
511
+ if (k) out[k] = v;
512
+ }
513
+ return out;
514
+ }
515
+ function buildSetCookieHeaders(cfg, tokens) {
516
+ const accessTtl = cfg.tokens?.accessTtl ?? 900;
517
+ const refreshTtl = cfg.tokens?.refreshTtl ?? 2592e3;
518
+ return [
519
+ serializeCookie(buildCookie(cfg, { kind: "access", value: tokens.accessToken, maxAge: accessTtl })),
520
+ serializeCookie(buildCookie(cfg, { kind: "refresh", value: tokens.refreshToken, maxAge: refreshTtl })),
521
+ serializeCookie(buildCookie(cfg, { kind: "csrf", value: tokens.csrfToken, maxAge: refreshTtl, httpOnly: false }))
522
+ ];
523
+ }
524
+ async function getSessionOrRefreshFromRequest(req, instance) {
525
+ const cfg = instance.config;
526
+ const jar = parseCookies(req.headers.get("cookie"));
527
+ const accessToken = jar[cookieName(cfg, "access")];
528
+ const refreshToken = jar[cookieName(cfg, "refresh")];
529
+ const ip = req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? req.headers.get("x-real-ip") ?? void 0;
530
+ const userAgent = req.headers.get("user-agent") ?? void 0;
531
+ const result = await getSessionOrRefresh(instance, {
532
+ accessToken,
533
+ refreshToken,
534
+ ip,
535
+ userAgent
536
+ });
537
+ const setCookieHeaders = result.tokens ? buildSetCookieHeaders(cfg, result.tokens) : [];
538
+ return { ...result, setCookieHeaders };
539
+ }
540
+
449
541
  // src/password/index.ts
450
542
  var password_exports = {};
451
543
  __export(password_exports, {
@@ -545,8 +637,11 @@ function isExpired(challenge) {
545
637
  // src/sso/index.ts
546
638
  var sso_exports = {};
547
639
  __export(sso_exports, {
640
+ DiscordProvider: () => DiscordProvider,
548
641
  GithubProvider: () => GithubProvider,
549
642
  GoogleProvider: () => GoogleProvider,
643
+ MicrosoftProvider: () => MicrosoftProvider,
644
+ OIDCProvider: () => OIDCProvider,
550
645
  authorize: () => authorize,
551
646
  base64url: () => base64url,
552
647
  buildAuthorizeUrl: () => buildAuthorizeUrl,
@@ -840,6 +935,69 @@ function GithubProvider(opts) {
840
935
  };
841
936
  }
842
937
 
938
+ // src/sso/providers/discord.ts
939
+ function DiscordProvider(opts) {
940
+ return {
941
+ kind: "oauth2",
942
+ id: opts.id ?? "discord",
943
+ name: "Discord",
944
+ clientId: opts.clientId,
945
+ clientSecret: opts.clientSecret,
946
+ redirectUri: opts.redirectUri,
947
+ scopes: opts.scopes ?? ["identify", "email"],
948
+ authorizationUrl: "https://discord.com/oauth2/authorize",
949
+ tokenUrl: "https://discord.com/api/oauth2/token",
950
+ userinfoUrl: "https://discord.com/api/users/@me",
951
+ profile: (raw) => {
952
+ const p = raw ?? {};
953
+ const id = String(p.id ?? "");
954
+ const image = id && p.avatar ? `https://cdn.discordapp.com/avatars/${id}/${p.avatar}.png` : null;
955
+ return {
956
+ providerAccountId: id,
957
+ email: p.email ?? "",
958
+ name: p.global_name ?? p.username ?? null,
959
+ image
960
+ };
961
+ }
962
+ };
963
+ }
964
+
965
+ // src/sso/providers/microsoft.ts
966
+ function MicrosoftProvider(opts) {
967
+ const tenant = opts.tenantId ?? "common";
968
+ const base = `https://login.microsoftonline.com/${tenant}`;
969
+ return {
970
+ kind: "oidc",
971
+ id: opts.id ?? "microsoft",
972
+ name: "Microsoft",
973
+ clientId: opts.clientId,
974
+ clientSecret: opts.clientSecret,
975
+ redirectUri: opts.redirectUri,
976
+ scopes: opts.scopes ?? ["openid", "email", "profile"],
977
+ issuer: `${base}/v2.0`,
978
+ authorizationUrl: `${base}/oauth2/v2.0/authorize`,
979
+ tokenUrl: `${base}/oauth2/v2.0/token`,
980
+ userinfoUrl: "https://graph.microsoft.com/oidc/userinfo"
981
+ };
982
+ }
983
+
984
+ // src/sso/providers/oidc.ts
985
+ function OIDCProvider(opts) {
986
+ return {
987
+ kind: "oidc",
988
+ id: opts.id,
989
+ name: opts.name ?? opts.id,
990
+ clientId: opts.clientId,
991
+ clientSecret: opts.clientSecret,
992
+ redirectUri: opts.redirectUri,
993
+ scopes: opts.scopes ?? ["openid", "email", "profile"],
994
+ issuer: opts.issuer,
995
+ authorizationUrl: opts.authorizationUrl,
996
+ tokenUrl: opts.tokenUrl,
997
+ userinfoUrl: opts.userinfoUrl
998
+ };
999
+ }
1000
+
843
1001
  // src/adapters/index.ts
844
1002
  var adapters_exports = {};
845
1003
 
@@ -856,58 +1014,6 @@ __export(cookies_exports, {
856
1014
  verifyCsrf: () => verifyCsrf
857
1015
  });
858
1016
 
859
- // src/cookies/spec.ts
860
- function cookieName(cfg, kind) {
861
- const prefix = cfg.tokens?.cookiePrefix ?? "holeauth";
862
- switch (kind) {
863
- case "access":
864
- return `${prefix}.at`;
865
- case "refresh":
866
- return `${prefix}.rt`;
867
- case "csrf":
868
- return `${prefix}.csrf`;
869
- case "pending":
870
- return `${prefix}.pending`;
871
- case "oauthState":
872
- return `${prefix}.oauth.state`;
873
- case "oauthPkce":
874
- return `${prefix}.oauth.pkce`;
875
- }
876
- }
877
- function isProduction() {
878
- return globalThis.process?.env?.NODE_ENV === "production";
879
- }
880
- function buildCookie(cfg, input) {
881
- const httpOnly = input.httpOnly ?? input.kind !== "csrf";
882
- const secure = cfg.tokens?.cookieSecure ?? isProduction();
883
- return {
884
- name: cookieName(cfg, input.kind),
885
- value: input.value,
886
- maxAge: input.maxAge,
887
- httpOnly,
888
- secure,
889
- sameSite: input.sameSite ?? cfg.tokens?.sameSite ?? "lax",
890
- path: input.path ?? "/",
891
- domain: cfg.tokens?.cookieDomain
892
- };
893
- }
894
- function serializeCookie(c) {
895
- const parts = [`${c.name}=${encodeURIComponent(c.value)}`];
896
- parts.push(`Path=${c.path}`);
897
- if (c.domain) parts.push(`Domain=${c.domain}`);
898
- if (c.maxAge !== void 0) {
899
- parts.push(`Max-Age=${c.maxAge}`);
900
- if (c.maxAge === 0) parts.push("Expires=Thu, 01 Jan 1970 00:00:00 GMT");
901
- }
902
- if (c.httpOnly) parts.push("HttpOnly");
903
- if (c.secure) parts.push("Secure");
904
- parts.push(`SameSite=${c.sameSite.charAt(0).toUpperCase()}${c.sameSite.slice(1)}`);
905
- return parts.join("; ");
906
- }
907
- function deleteCookie(cfg, kind) {
908
- return buildCookie(cfg, { kind, value: "", maxAge: 0 });
909
- }
910
-
911
1017
  // src/events/index.ts
912
1018
  var events_exports = {};
913
1019
  __export(events_exports, {
@@ -1737,6 +1843,9 @@ function defineHoleauth(config) {
1737
1843
  sso: {
1738
1844
  authorize: (providerId) => authorize(config, providerId),
1739
1845
  callback: (providerId, input) => callback(config, providerId, input)
1846
+ },
1847
+ on(type, handler) {
1848
+ return subscribe(config, type, handler);
1740
1849
  }
1741
1850
  };
1742
1851
  const merged = { ...instance, ...registry.api };