@draftlab/auth 0.10.4 → 0.11.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/dist/core.d.mts CHANGED
@@ -3,7 +3,6 @@ import { UnknownStateError } from "./error.mjs";
3
3
  import { Prettify } from "./util.mjs";
4
4
  import { SubjectPayload, SubjectSchema } from "./subject.mjs";
5
5
  import { StorageAdapter } from "./storage/storage.mjs";
6
- import { Plugin } from "./plugin/types.mjs";
7
6
  import { Provider } from "./provider/provider.mjs";
8
7
  import { Theme } from "./themes/theme.mjs";
9
8
  import { AuthorizationState } from "./types.mjs";
@@ -61,8 +60,6 @@ interface IssuerInput<Providers extends Record<string, Provider<unknown>>, Subje
61
60
  error?(error: UnknownStateError, req: Request): Promise<Response>;
62
61
  /** Client authorization check function */
63
62
  allow?(input: AllowCheckInput, req: Request): Promise<boolean>;
64
- /** Plugin configuration */
65
- plugins?: Plugin[];
66
63
  /**
67
64
  * Refresh callback for updating user claims.
68
65
  *
package/dist/core.mjs CHANGED
@@ -5,7 +5,6 @@ import { validatePKCE } from "./pkce.mjs";
5
5
  import { generateSecureToken } from "./random.mjs";
6
6
  import { Storage } from "./storage/storage.mjs";
7
7
  import { encryptionKeys, signingKeys } from "./keys.mjs";
8
- import { PluginManager } from "./plugin/manager.mjs";
9
8
  import { Revocation } from "./revocation.mjs";
10
9
  import { setTheme } from "./themes/theme.mjs";
11
10
  import { Select } from "./ui/select.mjs";
@@ -188,15 +187,6 @@ const issuer = (input) => {
188
187
  const authorization = await getAuthorization(ctx);
189
188
  const currentProvider = ctx.get("provider") || "unknown";
190
189
  if (!authorization.client_id) throw new Error("client_id is required");
191
- if (manager) try {
192
- const subjectProperties = properties && typeof properties === "object" ? properties : {};
193
- await manager.executeSuccessHooks(authorization.client_id, currentProvider, {
194
- type: currentProvider,
195
- properties: subjectProperties
196
- });
197
- } catch (error$1) {
198
- console.error("Plugin success hook failed:", error$1);
199
- }
200
190
  return await input.success({ async subject(type, properties$1, subjectOpts) {
201
191
  const subject = subjectOpts?.subject ?? await resolveSubject(type, properties$1);
202
192
  await successOpts?.invalidate?.(await resolveSubject(type, properties$1));
@@ -287,22 +277,6 @@ const issuer = (input) => {
287
277
  storage
288
278
  };
289
279
  const app = new Router({ basePath: input.basePath });
290
- const manager = input.plugins && input.plugins.length > 0 ? new PluginManager(input.storage) : null;
291
- let pluginsInitialized = false;
292
- if (manager && input.plugins) {
293
- manager.registerAll(input.plugins);
294
- manager.setupRoutes(app);
295
- app.use(async (c, next) => {
296
- if (!pluginsInitialized) try {
297
- await manager.initialize();
298
- pluginsInitialized = true;
299
- } catch (error$1) {
300
- console.error("Plugin initialization failed:", error$1);
301
- return c.newResponse("Plugin initialization failed", { status: 500 });
302
- }
303
- return await next();
304
- });
305
- }
306
280
  for (const [name, value] of Object.entries(input.providers)) {
307
281
  const route = new Router();
308
282
  route.use(async (c, next) => {
@@ -590,10 +564,6 @@ const issuer = (input) => {
590
564
  redirectURI: redirect_uri,
591
565
  audience
592
566
  }, c.request)) throw new UnauthorizedClientError(client_id, redirect_uri);
593
- if (manager) {
594
- const scopes = scope ? scope.split(" ") : void 0;
595
- await manager.executeAuthorizeHooks(client_id, provider, scopes);
596
- }
597
567
  await auth.set(c, "authorization", 900, authorization);
598
568
  if (provider) return c.redirect(`${provider}/authorize`);
599
569
  const availableProviders = Object.keys(input.providers);
@@ -601,12 +571,6 @@ const issuer = (input) => {
601
571
  return auth.forward(c, await select()(Object.fromEntries(Object.entries(input.providers).map(([key, value]) => [key, value.type])), c.request));
602
572
  });
603
573
  app.onError(async (err, c) => {
604
- if (manager) try {
605
- const errorObj = err instanceof Error ? err : new Error(String(err));
606
- await manager.executeErrorHooks(errorObj);
607
- } catch (hookError) {
608
- console.error("Plugin error hook failed:", hookError);
609
- }
610
574
  if (err instanceof UnknownStateError) return auth.forward(c, await error(err, c.request));
611
575
  try {
612
576
  const authorization = await getAuthorization(c);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@draftlab/auth",
3
- "version": "0.10.4",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "description": "Core implementation for @draftlab/auth",
6
6
  "author": "Matheus Pergoli",
@@ -39,7 +39,7 @@
39
39
  "devDependencies": {
40
40
  "@types/node": "^25.0.3",
41
41
  "@types/qrcode": "^1.5.6",
42
- "tsdown": "^0.18.2",
42
+ "tsdown": "^0.18.3",
43
43
  "typescript": "^5.9.3",
44
44
  "@draftlab/tsconfig": "0.1.0"
45
45
  },
@@ -63,7 +63,7 @@
63
63
  "preact": "^10.28.1",
64
64
  "preact-render-to-string": "^6.6.4",
65
65
  "qrcode": "^1.5.4",
66
- "@draftlab/auth-router": "0.4.1"
66
+ "@draftlab/auth-router": "0.5.0"
67
67
  },
68
68
  "engines": {
69
69
  "node": ">=18"
@@ -1,27 +0,0 @@
1
- import { PluginBuilder } from "./plugin.mjs";
2
-
3
- //#region src/plugin/builder.d.ts
4
-
5
- /**
6
- * Create a new plugin builder.
7
- * Plugins are built using a fluent API that supports routes and lifecycle hooks.
8
- *
9
- * @param id - Unique identifier for the plugin
10
- * @returns Plugin builder with chainable methods
11
- *
12
- * @example
13
- * ```ts
14
- * const analytics = plugin("analytics")
15
- * .onSuccess(async (ctx) => {
16
- * await ctx.storage.set(`success:${ctx.clientID}`, ctx.subject)
17
- * })
18
- * .post("/stats", async (ctx) => {
19
- * const stats = await ctx.pluginStorage.get("stats")
20
- * return ctx.json(stats)
21
- * })
22
- * .build()
23
- * ```
24
- */
25
- declare const plugin: (id: string) => PluginBuilder;
26
- //#endregion
27
- export { plugin };
@@ -1,93 +0,0 @@
1
- //#region src/plugin/builder.ts
2
- /**
3
- * Create a new plugin builder.
4
- * Plugins are built using a fluent API that supports routes and lifecycle hooks.
5
- *
6
- * @param id - Unique identifier for the plugin
7
- * @returns Plugin builder with chainable methods
8
- *
9
- * @example
10
- * ```ts
11
- * const analytics = plugin("analytics")
12
- * .onSuccess(async (ctx) => {
13
- * await ctx.storage.set(`success:${ctx.clientID}`, ctx.subject)
14
- * })
15
- * .post("/stats", async (ctx) => {
16
- * const stats = await ctx.pluginStorage.get("stats")
17
- * return ctx.json(stats)
18
- * })
19
- * .build()
20
- * ```
21
- */
22
- const plugin = (id) => {
23
- if (!id || typeof id !== "string") throw new Error("Plugin id must be a non-empty string");
24
- const routes = [];
25
- const registeredPaths = /* @__PURE__ */ new Set();
26
- let initHook;
27
- let authorizeHook;
28
- let successHook;
29
- let errorHook;
30
- const validatePath = (path) => {
31
- if (!path || typeof path !== "string") throw new Error("Route path must be a non-empty string");
32
- if (!path.startsWith("/")) throw new Error("Route path must start with '/'");
33
- };
34
- return {
35
- get(path, handler) {
36
- validatePath(path);
37
- const routeKey = `GET ${path}`;
38
- if (registeredPaths.has(routeKey)) throw new Error(`Route ${routeKey} already registered in plugin '${id}'`);
39
- registeredPaths.add(routeKey);
40
- routes.push({
41
- method: "GET",
42
- path,
43
- handler
44
- });
45
- return this;
46
- },
47
- post(path, handler) {
48
- validatePath(path);
49
- const routeKey = `POST ${path}`;
50
- if (registeredPaths.has(routeKey)) throw new Error(`Route ${routeKey} already registered in plugin '${id}'`);
51
- registeredPaths.add(routeKey);
52
- routes.push({
53
- method: "POST",
54
- path,
55
- handler
56
- });
57
- return this;
58
- },
59
- onInit(handler) {
60
- if (initHook) throw new Error(`onInit hook already defined for plugin '${id}'`);
61
- initHook = handler;
62
- return this;
63
- },
64
- onAuthorize(handler) {
65
- if (authorizeHook) throw new Error(`onAuthorize hook already defined for plugin '${id}'`);
66
- authorizeHook = handler;
67
- return this;
68
- },
69
- onSuccess(handler) {
70
- if (successHook) throw new Error(`onSuccess hook already defined for plugin '${id}'`);
71
- successHook = handler;
72
- return this;
73
- },
74
- onError(handler) {
75
- if (errorHook) throw new Error(`onError hook already defined for plugin '${id}'`);
76
- errorHook = handler;
77
- return this;
78
- },
79
- build() {
80
- return {
81
- id,
82
- routes: routes.length > 0 ? routes : void 0,
83
- onInit: initHook,
84
- onAuthorize: authorizeHook,
85
- onSuccess: successHook,
86
- onError: errorHook
87
- };
88
- }
89
- };
90
- };
91
-
92
- //#endregion
93
- export { plugin };
@@ -1,62 +0,0 @@
1
- import { StorageAdapter } from "../storage/storage.mjs";
2
- import { Plugin } from "./types.mjs";
3
- import { Router } from "@draftlab/auth-router";
4
-
5
- //#region src/plugin/manager.d.ts
6
-
7
- declare class PluginManager {
8
- private readonly plugins;
9
- private readonly storage;
10
- constructor(storage: StorageAdapter);
11
- /**
12
- * Register a plugin
13
- */
14
- register(plugin: Plugin): void;
15
- /**
16
- * Register multiple plugins at once
17
- */
18
- registerAll(plugins: Plugin[]): void;
19
- /**
20
- * Get all registered plugins
21
- */
22
- getAll(): Plugin[];
23
- /**
24
- * Get plugin by id
25
- */
26
- get(id: string): Plugin | undefined;
27
- /**
28
- * Initialize all plugins.
29
- * Called once during issuer setup.
30
- * Plugins can set up initial state or validate configuration.
31
- *
32
- * @throws PluginError if any plugin initialization fails
33
- */
34
- initialize(): Promise<void>;
35
- /**
36
- * Execute authorize hooks for all plugins.
37
- * Called before processing an authorization request.
38
- * Can validate, rate limit, or enhance the request.
39
- */
40
- executeAuthorizeHooks(clientID: string, provider?: string, scopes?: string[]): Promise<void>;
41
- /**
42
- * Execute success hooks for all plugins.
43
- * Called after successful authentication.
44
- * Runs in parallel for better performance.
45
- * Plugins cannot modify the response.
46
- */
47
- executeSuccessHooks(clientID: string, provider: string | undefined, subject: {
48
- type: string;
49
- properties: Record<string, unknown>;
50
- }): Promise<void>;
51
- /**
52
- * Execute error hooks for all plugins.
53
- * Called when an authentication error occurs.
54
- */
55
- executeErrorHooks(error: Error, clientID?: string, provider?: string): Promise<void>;
56
- /**
57
- * Setup plugin routes on a router
58
- */
59
- setupRoutes(router: Router): void;
60
- }
61
- //#endregion
62
- export { PluginManager };
@@ -1,160 +0,0 @@
1
- import { PluginError } from "./types.mjs";
2
-
3
- //#region src/plugin/manager.ts
4
- var PluginManager = class {
5
- plugins = /* @__PURE__ */ new Map();
6
- storage;
7
- constructor(storage) {
8
- this.storage = storage;
9
- }
10
- /**
11
- * Register a plugin
12
- */
13
- register(plugin) {
14
- if (this.plugins.has(plugin.id)) throw new PluginError(`Plugin already registered`, plugin.id);
15
- this.plugins.set(plugin.id, plugin);
16
- }
17
- /**
18
- * Register multiple plugins at once
19
- */
20
- registerAll(plugins) {
21
- for (const plugin of plugins) this.register(plugin);
22
- }
23
- /**
24
- * Get all registered plugins
25
- */
26
- getAll() {
27
- return Array.from(this.plugins.values());
28
- }
29
- /**
30
- * Get plugin by id
31
- */
32
- get(id) {
33
- return this.plugins.get(id);
34
- }
35
- /**
36
- * Initialize all plugins.
37
- * Called once during issuer setup.
38
- * Plugins can set up initial state or validate configuration.
39
- *
40
- * @throws PluginError if any plugin initialization fails
41
- */
42
- async initialize() {
43
- for (const plugin of this.plugins.values()) {
44
- if (!plugin.onInit) continue;
45
- try {
46
- const context = {
47
- pluginId: plugin.id,
48
- request: new Request("http://internal/init"),
49
- now: /* @__PURE__ */ new Date(),
50
- storage: this.storage
51
- };
52
- await plugin.onInit(context);
53
- } catch (error) {
54
- throw new PluginError(`Initialization failed: ${error instanceof Error ? error.message : String(error)}`, plugin.id);
55
- }
56
- }
57
- }
58
- /**
59
- * Execute authorize hooks for all plugins.
60
- * Called before processing an authorization request.
61
- * Can validate, rate limit, or enhance the request.
62
- */
63
- async executeAuthorizeHooks(clientID, provider, scopes) {
64
- for (const plugin of this.plugins.values()) {
65
- if (!plugin.onAuthorize) continue;
66
- try {
67
- const context = {
68
- pluginId: plugin.id,
69
- request: new Request("http://internal/authorize"),
70
- now: /* @__PURE__ */ new Date(),
71
- storage: this.storage,
72
- clientID,
73
- provider,
74
- scopes
75
- };
76
- await plugin.onAuthorize(context);
77
- } catch (error) {
78
- throw new PluginError(`Authorization hook failed: ${error instanceof Error ? error.message : String(error)}`, plugin.id);
79
- }
80
- }
81
- }
82
- /**
83
- * Execute success hooks for all plugins.
84
- * Called after successful authentication.
85
- * Runs in parallel for better performance.
86
- * Plugins cannot modify the response.
87
- */
88
- async executeSuccessHooks(clientID, provider, subject) {
89
- const hooks = Array.from(this.plugins.values()).filter((p) => p.onSuccess).map(async (plugin) => {
90
- const context = {
91
- pluginId: plugin.id,
92
- request: new Request("http://internal/success"),
93
- now: /* @__PURE__ */ new Date(),
94
- storage: this.storage,
95
- clientID,
96
- provider,
97
- subject
98
- };
99
- return plugin.onSuccess?.(context).catch((error) => {
100
- console.error(`[Plugin: ${plugin.id}] Success hook failed:`, error instanceof Error ? error.message : String(error));
101
- });
102
- });
103
- await Promise.all(hooks);
104
- }
105
- /**
106
- * Execute error hooks for all plugins.
107
- * Called when an authentication error occurs.
108
- */
109
- async executeErrorHooks(error, clientID, provider) {
110
- for (const plugin of this.plugins.values()) {
111
- if (!plugin.onError) continue;
112
- try {
113
- const context = {
114
- pluginId: plugin.id,
115
- request: new Request("http://internal/error"),
116
- now: /* @__PURE__ */ new Date(),
117
- storage: this.storage,
118
- error,
119
- clientID,
120
- provider
121
- };
122
- await plugin.onError(context);
123
- } catch (hookError) {
124
- console.error(`[Plugin: ${plugin.id}] Error hook failed:`, hookError instanceof Error ? hookError.message : String(hookError));
125
- }
126
- }
127
- }
128
- /**
129
- * Setup plugin routes on a router
130
- */
131
- setupRoutes(router) {
132
- const registeredPaths = /* @__PURE__ */ new Set();
133
- for (const plugin of this.plugins.values()) {
134
- if (!plugin.routes) continue;
135
- for (const route of plugin.routes) {
136
- const fullPath = `/plugin/${plugin.id}${route.path}`;
137
- if (registeredPaths.has(fullPath)) throw new PluginError(`Route conflict: ${fullPath} already registered`, plugin.id);
138
- registeredPaths.add(fullPath);
139
- const handler = async (ctx) => {
140
- const pluginCtx = {
141
- ...ctx,
142
- storage: this.storage
143
- };
144
- return await route.handler(pluginCtx);
145
- };
146
- switch (route.method) {
147
- case "GET":
148
- router.get(fullPath, handler);
149
- break;
150
- case "POST":
151
- router.post(fullPath, handler);
152
- break;
153
- }
154
- }
155
- }
156
- }
157
- };
158
-
159
- //#endregion
160
- export { PluginManager };
@@ -1,42 +0,0 @@
1
- import { Plugin, PluginAuthorizeHook, PluginErrorHook, PluginInitHook, PluginRouteHandler, PluginSuccessHook } from "./types.mjs";
2
-
3
- //#region src/plugin/plugin.d.ts
4
-
5
- /**
6
- * Plugin builder interface for creating plugins with a fluent API.
7
- *
8
- * The builder pattern allows for elegant plugin definition:
9
- * - Chain route definitions with lifecycle hooks
10
- * - Each method returns this for chaining
11
- * - Build finalizes the plugin definition
12
- *
13
- * @example
14
- * ```ts
15
- * const myPlugin = plugin("my-plugin")
16
- * .onInit(async (ctx) => {
17
- * console.log("Plugin initialized")
18
- * })
19
- * .post("/action", async (ctx) => {
20
- * return ctx.json({ success: true })
21
- * })
22
- * .build()
23
- * ```
24
- */
25
- interface PluginBuilder {
26
- /** Register a GET route */
27
- get(path: string, handler: PluginRouteHandler): PluginBuilder;
28
- /** Register a POST route */
29
- post(path: string, handler: PluginRouteHandler): PluginBuilder;
30
- /** Register initialization hook (called once during issuer setup) */
31
- onInit(handler: PluginInitHook): PluginBuilder;
32
- /** Register authorization hook (called before authorization request) */
33
- onAuthorize(handler: PluginAuthorizeHook): PluginBuilder;
34
- /** Register success hook (called after successful authentication) */
35
- onSuccess(handler: PluginSuccessHook): PluginBuilder;
36
- /** Register error hook (called when authentication fails) */
37
- onError(handler: PluginErrorHook): PluginBuilder;
38
- /** Build the final plugin */
39
- build(): Plugin;
40
- }
41
- //#endregion
42
- export { PluginBuilder };
@@ -1 +0,0 @@
1
- export { };
@@ -1,99 +0,0 @@
1
- import { StorageAdapter } from "../storage/storage.mjs";
2
- import { RouterContext } from "@draftlab/auth-router/types";
3
-
4
- //#region src/plugin/types.d.ts
5
-
6
- interface PluginContext extends RouterContext {
7
- storage: StorageAdapter;
8
- }
9
- /**
10
- * Plugin route handler function
11
- */
12
- type PluginRouteHandler = (context: PluginContext) => Promise<Response>;
13
- /**
14
- * Plugin route definition
15
- */
16
- interface PluginRoute {
17
- /** Route path (e.g., "/admin", "/stats") */
18
- readonly path: string;
19
- /** HTTP method */
20
- readonly method: "GET" | "POST";
21
- /** Route handler function */
22
- readonly handler: PluginRouteHandler;
23
- }
24
- /**
25
- * Lifecycle hook context provided to plugin hooks.
26
- * Contains information about the current operation and access to isolated storage.
27
- */
28
- interface PluginHookContext {
29
- /** Unique identifier for the plugin */
30
- pluginId: string;
31
- /** Raw request object */
32
- request: Request;
33
- /** Current time for consistency across hook execution */
34
- now: Date;
35
- /** Storage adapter for data persistence */
36
- storage: StorageAdapter;
37
- }
38
- /**
39
- * Hook called when the issuer is being initialized.
40
- * Useful for plugins that need to set up initial state or validate configuration.
41
- * Should complete quickly - takes place during server startup.
42
- */
43
- type PluginInitHook = (context: PluginHookContext) => Promise<void>;
44
- /**
45
- * Hook called before an authorization request is processed.
46
- * Use for validation, rate limiting, or request enhancement.
47
- */
48
- type PluginAuthorizeHook = (context: PluginHookContext & {
49
- clientID: string;
50
- provider?: string;
51
- scopes?: string[];
52
- }) => Promise<void>;
53
- /**
54
- * Hook called after successful authentication.
55
- * Use for logging, analytics, webhooks, or side effects.
56
- * Cannot modify the response - hooks run in parallel.
57
- */
58
- type PluginSuccessHook = (context: PluginHookContext & {
59
- clientID: string;
60
- provider?: string;
61
- subject: {
62
- type: string;
63
- properties: Record<string, unknown>;
64
- };
65
- }) => Promise<void>;
66
- /**
67
- * Hook called when an authentication error occurs.
68
- * Use for error logging, custom error pages, or error transformation.
69
- */
70
- type PluginErrorHook = (context: PluginHookContext & {
71
- error: Error;
72
- clientID?: string;
73
- provider?: string;
74
- }) => Promise<void>;
75
- /**
76
- * Main plugin interface with lifecycle hooks and storage isolation
77
- */
78
- interface Plugin {
79
- /** Unique plugin identifier */
80
- readonly id: string;
81
- /** Custom routes added by this plugin */
82
- readonly routes?: readonly PluginRoute[];
83
- /** Called once when the issuer initializes */
84
- readonly onInit?: PluginInitHook;
85
- /** Called before authorization request is processed */
86
- readonly onAuthorize?: PluginAuthorizeHook;
87
- /** Called after successful authentication */
88
- readonly onSuccess?: PluginSuccessHook;
89
- /** Called when an error occurs during authentication */
90
- readonly onError?: PluginErrorHook;
91
- }
92
- /**
93
- * Plugin error types
94
- */
95
- declare class PluginError extends Error {
96
- constructor(message: string, pluginId: string);
97
- }
98
- //#endregion
99
- export { Plugin, PluginAuthorizeHook, PluginContext, PluginError, PluginErrorHook, PluginHookContext, PluginInitHook, PluginRoute, PluginRouteHandler, PluginSuccessHook };
@@ -1,13 +0,0 @@
1
- //#region src/plugin/types.ts
2
- /**
3
- * Plugin error types
4
- */
5
- var PluginError = class extends Error {
6
- constructor(message, pluginId) {
7
- super(`[Plugin: ${pluginId}] ${message}`);
8
- this.name = "PluginError";
9
- }
10
- };
11
-
12
- //#endregion
13
- export { PluginError };