@draftlab/auth 0.2.1 → 0.2.2
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.ts +3 -0
- package/dist/core.js +9 -3
- package/dist/plugin/builder.d.ts +10 -0
- package/dist/plugin/builder.js +35 -0
- package/dist/plugin/manager.d.ts +21 -0
- package/dist/plugin/manager.js +48 -0
- package/dist/plugin/plugin.d.ts +17 -0
- package/dist/plugin/plugin.js +0 -0
- package/dist/plugin/types.d.ts +40 -0
- package/dist/plugin/types.js +13 -0
- package/dist/provider/code.js +1 -1
- package/dist/provider/oauth2.js +1 -1
- package/dist/provider/passkey.js +1 -1
- package/dist/provider/password.js +5 -5
- package/package.json +2 -2
package/dist/core.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { UnknownStateError } from "./error.js";
|
|
|
3
3
|
import { Prettify } from "./util.js";
|
|
4
4
|
import { SubjectPayload, SubjectSchema } from "./subject.js";
|
|
5
5
|
import { StorageAdapter } from "./storage/storage.js";
|
|
6
|
+
import { Plugin } from "./plugin/types.js";
|
|
6
7
|
import { Provider } from "./provider/provider.js";
|
|
7
8
|
import { Theme } from "./themes/theme.js";
|
|
8
9
|
import { Router } from "@draftlab/auth-router";
|
|
@@ -79,6 +80,8 @@ interface IssuerInput<Providers extends Record<string, Provider<unknown>>, Subje
|
|
|
79
80
|
error?(error: UnknownStateError, req: Request): Promise<Response>;
|
|
80
81
|
/** Client authorization check function */
|
|
81
82
|
allow?(input: AllowCheckInput, req: Request): Promise<boolean>;
|
|
83
|
+
/** Plugin configuration */
|
|
84
|
+
plugins?: readonly Plugin[];
|
|
82
85
|
/**
|
|
83
86
|
* Refresh callback for updating user claims.
|
|
84
87
|
*
|
package/dist/core.js
CHANGED
|
@@ -5,6 +5,7 @@ import { validatePKCE } from "./pkce.js";
|
|
|
5
5
|
import { generateSecureToken } from "./random.js";
|
|
6
6
|
import { Storage } from "./storage/storage.js";
|
|
7
7
|
import { encryptionKeys, signingKeys } from "./keys.js";
|
|
8
|
+
import { PluginManager } from "./plugin/manager.js";
|
|
8
9
|
import { setTheme } from "./themes/theme.js";
|
|
9
10
|
import { Select } from "./ui/select.js";
|
|
10
11
|
import { CompactEncrypt, SignJWT, compactDecrypt } from "jose";
|
|
@@ -40,8 +41,8 @@ const issuer = (input) => {
|
|
|
40
41
|
headers: { "Content-Type": "text/plain" }
|
|
41
42
|
});
|
|
42
43
|
});
|
|
43
|
-
const ttlAccess = input.ttl?.access ??
|
|
44
|
-
const ttlRefresh = input.ttl?.refresh ??
|
|
44
|
+
const ttlAccess = input.ttl?.access ?? 3600 * 24 * 30;
|
|
45
|
+
const ttlRefresh = input.ttl?.refresh ?? 3600 * 24 * 365;
|
|
45
46
|
const ttlRefreshReuse = input.ttl?.reuse ?? 60;
|
|
46
47
|
const ttlRefreshRetention = input.ttl?.retention ?? 0;
|
|
47
48
|
if (input.theme) setTheme(input.theme);
|
|
@@ -255,6 +256,11 @@ const issuer = (input) => {
|
|
|
255
256
|
storage
|
|
256
257
|
};
|
|
257
258
|
const app = new Router({ basePath: input.basePath });
|
|
259
|
+
if (input.plugins && input.plugins.length > 0) {
|
|
260
|
+
const manager = new PluginManager(input.storage);
|
|
261
|
+
for (const plugin of input.plugins) manager.register(plugin);
|
|
262
|
+
manager.setupRoutes(app);
|
|
263
|
+
}
|
|
258
264
|
for (const [name, value] of Object.entries(input.providers)) {
|
|
259
265
|
const route = new Router();
|
|
260
266
|
route.use(async (c, next) => {
|
|
@@ -466,7 +472,7 @@ const issuer = (input) => {
|
|
|
466
472
|
redirectURI: redirect_uri,
|
|
467
473
|
audience
|
|
468
474
|
}, c.request)) throw new UnauthorizedClientError(client_id, redirect_uri);
|
|
469
|
-
await auth.set(c, "authorization",
|
|
475
|
+
await auth.set(c, "authorization", 3600 * 24, authorization);
|
|
470
476
|
if (provider) return c.redirect(`${provider}/authorize`);
|
|
471
477
|
const availableProviders = Object.keys(input.providers);
|
|
472
478
|
if (availableProviders.length === 1) return c.redirect(`${availableProviders[0]}/authorize`);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/plugin/builder.ts
|
|
2
|
+
/**
|
|
3
|
+
* Create a new plugin
|
|
4
|
+
*/
|
|
5
|
+
const plugin = (id) => {
|
|
6
|
+
const routes = [];
|
|
7
|
+
const builder = {
|
|
8
|
+
get(path, handler) {
|
|
9
|
+
routes.push({
|
|
10
|
+
method: "GET",
|
|
11
|
+
path,
|
|
12
|
+
handler
|
|
13
|
+
});
|
|
14
|
+
return builder;
|
|
15
|
+
},
|
|
16
|
+
post(path, handler) {
|
|
17
|
+
routes.push({
|
|
18
|
+
method: "POST",
|
|
19
|
+
path,
|
|
20
|
+
handler
|
|
21
|
+
});
|
|
22
|
+
return builder;
|
|
23
|
+
},
|
|
24
|
+
build() {
|
|
25
|
+
return {
|
|
26
|
+
id,
|
|
27
|
+
routes
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return builder;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
export { plugin };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { StorageAdapter } from "../storage/storage.js";
|
|
2
|
+
import { Plugin } from "./types.js";
|
|
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
|
+
* Setup plugin routes on a router
|
|
17
|
+
*/
|
|
18
|
+
setupRoutes(router: Router): void;
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { PluginManager };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { PluginError } from "./types.js";
|
|
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
|
+
* Setup plugin routes on a router
|
|
19
|
+
*/
|
|
20
|
+
setupRoutes(router) {
|
|
21
|
+
for (const plugin of this.plugins.values()) {
|
|
22
|
+
if (!plugin.routes) continue;
|
|
23
|
+
for (const route of plugin.routes) switch (route.method) {
|
|
24
|
+
case "GET":
|
|
25
|
+
router.get(route.path, async (ctx) => {
|
|
26
|
+
const pluginCtx = {
|
|
27
|
+
...ctx,
|
|
28
|
+
storage: this.storage
|
|
29
|
+
};
|
|
30
|
+
return await route.handler(pluginCtx);
|
|
31
|
+
});
|
|
32
|
+
break;
|
|
33
|
+
case "POST":
|
|
34
|
+
router.post(route.path, async (ctx) => {
|
|
35
|
+
const pluginCtx = {
|
|
36
|
+
...ctx,
|
|
37
|
+
storage: this.storage
|
|
38
|
+
};
|
|
39
|
+
return await route.handler(pluginCtx);
|
|
40
|
+
});
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
export { PluginManager };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Plugin, PluginRouteHandler } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/plugin/plugin.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Plugin builder interface for fluent API
|
|
7
|
+
*/
|
|
8
|
+
interface PluginBuilder {
|
|
9
|
+
/** Add GET route */
|
|
10
|
+
get(path: string, handler: PluginRouteHandler): PluginBuilder;
|
|
11
|
+
/** Add POST route */
|
|
12
|
+
post(path: string, handler: PluginRouteHandler): PluginBuilder;
|
|
13
|
+
/** Build the plugin */
|
|
14
|
+
build(): Plugin;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { PluginBuilder };
|
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { StorageAdapter } from "../storage/storage.js";
|
|
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
|
+
* Main plugin interface
|
|
26
|
+
*/
|
|
27
|
+
interface Plugin {
|
|
28
|
+
/** Unique plugin identifier */
|
|
29
|
+
readonly id: string;
|
|
30
|
+
/** Custom routes added by this plugin */
|
|
31
|
+
readonly routes?: readonly PluginRoute[];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Plugin error types
|
|
35
|
+
*/
|
|
36
|
+
declare class PluginError extends Error {
|
|
37
|
+
constructor(message: string, pluginId: string);
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
export { Plugin, PluginContext, PluginError, PluginRoute, PluginRouteHandler };
|
|
@@ -0,0 +1,13 @@
|
|
|
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 };
|
package/dist/provider/code.js
CHANGED
|
@@ -111,7 +111,7 @@ const CodeProvider = (config) => {
|
|
|
111
111
|
* Transitions between authentication states and renders the appropriate UI.
|
|
112
112
|
*/
|
|
113
113
|
const transition = async (c, nextState, formData, error) => {
|
|
114
|
-
await ctx.set(c, "provider",
|
|
114
|
+
await ctx.set(c, "provider", 3600 * 24, nextState);
|
|
115
115
|
const response = await config.request(c.request, nextState, formData, error);
|
|
116
116
|
return ctx.forward(c, response);
|
|
117
117
|
};
|
package/dist/provider/oauth2.js
CHANGED
|
@@ -102,7 +102,7 @@ const Oauth2Provider = (config) => {
|
|
|
102
102
|
routes.get("/authorize", async (c) => {
|
|
103
103
|
const state = generateSecureToken();
|
|
104
104
|
const pkce = config.pkce ? await generatePKCE() : void 0;
|
|
105
|
-
await ctx.set(c, "provider",
|
|
105
|
+
await ctx.set(c, "provider", 600, {
|
|
106
106
|
state,
|
|
107
107
|
redirect: getRelativeUrl(c, "./callback"),
|
|
108
108
|
codeVerifier: pkce?.verifier
|
package/dist/provider/passkey.js
CHANGED
|
@@ -88,7 +88,7 @@ const PasskeyProvider = (config) => {
|
|
|
88
88
|
return {
|
|
89
89
|
type: "passkey",
|
|
90
90
|
init(routes, ctx) {
|
|
91
|
-
const { rpName, authenticatorSelection, attestationType = "none", timeout =
|
|
91
|
+
const { rpName, authenticatorSelection, attestationType = "none", timeout = 300 * 1e3 } = config;
|
|
92
92
|
const getStoredUserById = async (userId) => {
|
|
93
93
|
return await Storage.get(ctx.storage, userKey(userId));
|
|
94
94
|
};
|
|
@@ -80,7 +80,7 @@ const PasswordProvider = (config) => {
|
|
|
80
80
|
*/
|
|
81
81
|
routes.get("/register", async (c) => {
|
|
82
82
|
const state = { type: "start" };
|
|
83
|
-
await ctx.set(c, "provider",
|
|
83
|
+
await ctx.set(c, "provider", 3600 * 24, state);
|
|
84
84
|
return ctx.forward(c, await config.register(c.request, state));
|
|
85
85
|
});
|
|
86
86
|
/**
|
|
@@ -93,12 +93,12 @@ const PasswordProvider = (config) => {
|
|
|
93
93
|
let provider = await ctx.get(c, "provider");
|
|
94
94
|
if (!provider) {
|
|
95
95
|
const state = { type: "start" };
|
|
96
|
-
await ctx.set(c, "provider",
|
|
96
|
+
await ctx.set(c, "provider", 3600 * 24, state);
|
|
97
97
|
if (action === "register") provider = state;
|
|
98
98
|
else return ctx.forward(c, await config.register(c.request, state));
|
|
99
99
|
}
|
|
100
100
|
const transition = async (next, err) => {
|
|
101
|
-
await ctx.set(c, "provider",
|
|
101
|
+
await ctx.set(c, "provider", 3600 * 24, next);
|
|
102
102
|
return ctx.forward(c, await config.register(c.request, next, formData, err));
|
|
103
103
|
};
|
|
104
104
|
if (action === "register" && provider.type === "start") {
|
|
@@ -175,7 +175,7 @@ const PasswordProvider = (config) => {
|
|
|
175
175
|
type: "start",
|
|
176
176
|
redirect
|
|
177
177
|
};
|
|
178
|
-
await ctx.set(c, "provider",
|
|
178
|
+
await ctx.set(c, "provider", 3600 * 24, state);
|
|
179
179
|
return ctx.forward(c, await config.change(c.request, state));
|
|
180
180
|
});
|
|
181
181
|
/**
|
|
@@ -187,7 +187,7 @@ const PasswordProvider = (config) => {
|
|
|
187
187
|
const provider = await ctx.get(c, "provider");
|
|
188
188
|
if (!provider) throw new UnknownStateError();
|
|
189
189
|
const transition = async (next, err) => {
|
|
190
|
-
await ctx.set(c, "provider",
|
|
190
|
+
await ctx.set(c, "provider", 3600 * 24, next);
|
|
191
191
|
return ctx.forward(c, await config.change(c.request, next, formData, err));
|
|
192
192
|
};
|
|
193
193
|
if (action === "code") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@draftlab/auth",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Core implementation for @draftlab/auth",
|
|
6
6
|
"author": "Matheus Pergoli",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
],
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@types/node": "^24.0.
|
|
40
|
+
"@types/node": "^24.0.15",
|
|
41
41
|
"tsdown": "^0.12.9",
|
|
42
42
|
"typescript": "^5.8.3",
|
|
43
43
|
"@draftlab/tsconfig": "0.1.0"
|