@gaballahtech/seba 0.0.0-stage → 0.2.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/ARCHITECTURE.md +242 -0
- package/BUILD_REPORT.md +195 -0
- package/CHANGELOG.md +59 -0
- package/LICENSE +21 -0
- package/README.md +282 -2
- package/RESEARCH.md +180 -0
- package/SECURITY.md +224 -0
- package/VALIDATION_REPORT.md +187 -0
- package/dist/browser/index.d.ts +9 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +9 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/platform.d.ts +14 -0
- package/dist/browser/platform.d.ts.map +1 -0
- package/dist/browser/platform.js +60 -0
- package/dist/browser/platform.js.map +1 -0
- package/dist/browser/provider-modal.d.ts +15 -0
- package/dist/browser/provider-modal.d.ts.map +1 -0
- package/dist/browser/provider-modal.js +126 -0
- package/dist/browser/provider-modal.js.map +1 -0
- package/dist/browser/storage.d.ts +32 -0
- package/dist/browser/storage.d.ts.map +1 -0
- package/dist/browser/storage.js +121 -0
- package/dist/browser/storage.js.map +1 -0
- package/dist/core/client.d.ts +49 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/client.js +371 -0
- package/dist/core/client.js.map +1 -0
- package/dist/core/discovery.d.ts +82 -0
- package/dist/core/discovery.d.ts.map +1 -0
- package/dist/core/discovery.js +243 -0
- package/dist/core/discovery.js.map +1 -0
- package/dist/core/dpop.d.ts +67 -0
- package/dist/core/dpop.d.ts.map +1 -0
- package/dist/core/dpop.js +176 -0
- package/dist/core/dpop.js.map +1 -0
- package/dist/core/errors.d.ts +86 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +104 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/http.d.ts +68 -0
- package/dist/core/http.d.ts.map +1 -0
- package/dist/core/http.js +128 -0
- package/dist/core/http.js.map +1 -0
- package/dist/core/identity.d.ts +145 -0
- package/dist/core/identity.d.ts.map +1 -0
- package/dist/core/identity.js +735 -0
- package/dist/core/identity.js.map +1 -0
- package/dist/core/oidc.d.ts +175 -0
- package/dist/core/oidc.d.ts.map +1 -0
- package/dist/core/oidc.js +413 -0
- package/dist/core/oidc.js.map +1 -0
- package/dist/core/session.d.ts +116 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +350 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/storage.d.ts +22 -0
- package/dist/core/storage.d.ts.map +1 -0
- package/dist/core/storage.js +48 -0
- package/dist/core/storage.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/node/index.d.ts +9 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +9 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/platform.d.ts +25 -0
- package/dist/node/platform.d.ts.map +1 -0
- package/dist/node/platform.js +161 -0
- package/dist/node/platform.js.map +1 -0
- package/dist/seba.d.ts +39 -0
- package/dist/seba.d.ts.map +1 -0
- package/dist/seba.js +58 -0
- package/dist/seba.js.map +1 -0
- package/dist/types.d.ts +279 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/examples/node/index.ts +61 -0
- package/examples/react/App.tsx +127 -0
- package/examples/react/index.html +12 -0
- package/examples/react/main.tsx +14 -0
- package/examples/react/vite.config.ts +27 -0
- package/package.json +83 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js platform: a temporary loopback HTTP server captures the authorization response.
|
|
3
|
+
*
|
|
4
|
+
* The server binds only the loopback interface from the configured redirect URI, accepts a
|
|
5
|
+
* single matching `GET`, and shuts down as soon as the response is delivered.
|
|
6
|
+
*/
|
|
7
|
+
import type { InteractiveLoginRequest, Platform } from "../types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Create the platform used by `Seba` in Node.js.
|
|
10
|
+
*
|
|
11
|
+
* Sessions are stored in process memory; IndexedDB is rejected with a configuration error
|
|
12
|
+
* so a missing browser API never turns into a silent login failure.
|
|
13
|
+
*/
|
|
14
|
+
export declare function createNodePlatform(): Platform;
|
|
15
|
+
/**
|
|
16
|
+
* Listen on the loopback interface, open the authorization URL, and resolve with the
|
|
17
|
+
* callback URL once the provider redirects back.
|
|
18
|
+
*
|
|
19
|
+
* @param request - Authorization URL, redirect URI and expected state.
|
|
20
|
+
* @throws {@link SebaNoUIError} when no `openAuthorizationUrl` hook was configured.
|
|
21
|
+
* @throws {@link SebaConfigError} when the redirect URI cannot be served locally.
|
|
22
|
+
* @throws {@link SebaAuthError} when no valid callback arrives within five minutes.
|
|
23
|
+
*/
|
|
24
|
+
export declare function startLoopbackCallbackServer(request: InteractiveLoginRequest): Promise<URL>;
|
|
25
|
+
//# sourceMappingURL=platform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/node/platform.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,uBAAuB,EAAE,QAAQ,EAAsC,MAAM,aAAa,CAAC;AAuBzG;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,QAAQ,CAiB7C;AAED;;;;;;;;GAQG;AACH,wBAAsB,2BAA2B,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,GAAG,CAAC,CAmHhG"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node.js platform: a temporary loopback HTTP server captures the authorization response.
|
|
3
|
+
*
|
|
4
|
+
* The server binds only the loopback interface from the configured redirect URI, accepts a
|
|
5
|
+
* single matching `GET`, and shuts down as soon as the response is delivered.
|
|
6
|
+
*/
|
|
7
|
+
import { createServer } from "node:http";
|
|
8
|
+
import { isLoopbackHost } from "../core/discovery.js";
|
|
9
|
+
import { SebaAuthError, SebaConfigError, SebaNoUIError } from "../core/errors.js";
|
|
10
|
+
import { memoryPendingAuthStore, memorySessionStorage } from "../core/storage.js";
|
|
11
|
+
/** How long an interactive login may wait for the browser (5 minutes). */
|
|
12
|
+
const LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
13
|
+
const SUCCESS_HTML = [
|
|
14
|
+
"<!doctype html>",
|
|
15
|
+
'<html lang="en"><head><meta charset="utf-8"><title>Signed in</title></head>',
|
|
16
|
+
'<body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1rem">',
|
|
17
|
+
"<h1>Signed in</h1>",
|
|
18
|
+
"<p>You can close this window and return to the terminal.</p>",
|
|
19
|
+
"</body></html>",
|
|
20
|
+
].join("");
|
|
21
|
+
const FAILURE_HTML = [
|
|
22
|
+
"<!doctype html>",
|
|
23
|
+
'<html lang="en"><head><meta charset="utf-8"><title>Sign-in failed</title></head>',
|
|
24
|
+
'<body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1rem">',
|
|
25
|
+
"<h1>Sign-in could not be completed</h1>",
|
|
26
|
+
"<p>Return to the terminal for details and start the login again.</p>",
|
|
27
|
+
"</body></html>",
|
|
28
|
+
].join("");
|
|
29
|
+
/**
|
|
30
|
+
* Create the platform used by `Seba` in Node.js.
|
|
31
|
+
*
|
|
32
|
+
* Sessions are stored in process memory; IndexedDB is rejected with a configuration error
|
|
33
|
+
* so a missing browser API never turns into a silent login failure.
|
|
34
|
+
*/
|
|
35
|
+
export function createNodePlatform() {
|
|
36
|
+
return {
|
|
37
|
+
kind: "node",
|
|
38
|
+
defaultStorage: "memory",
|
|
39
|
+
fetch: (input, init) => globalThis.fetch(input, init),
|
|
40
|
+
currentUrl: () => null,
|
|
41
|
+
pendingAuth: memoryPendingAuthStore(),
|
|
42
|
+
createStorage(kind) {
|
|
43
|
+
if (kind === "indexeddb") {
|
|
44
|
+
throw new SebaConfigError('IndexedDB is not available in Node.js. Remove storage or use storage: "memory".');
|
|
45
|
+
}
|
|
46
|
+
return memorySessionStorage();
|
|
47
|
+
},
|
|
48
|
+
beginInteractiveLogin: startLoopbackCallbackServer,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Listen on the loopback interface, open the authorization URL, and resolve with the
|
|
53
|
+
* callback URL once the provider redirects back.
|
|
54
|
+
*
|
|
55
|
+
* @param request - Authorization URL, redirect URI and expected state.
|
|
56
|
+
* @throws {@link SebaNoUIError} when no `openAuthorizationUrl` hook was configured.
|
|
57
|
+
* @throws {@link SebaConfigError} when the redirect URI cannot be served locally.
|
|
58
|
+
* @throws {@link SebaAuthError} when no valid callback arrives within five minutes.
|
|
59
|
+
*/
|
|
60
|
+
export async function startLoopbackCallbackServer(request) {
|
|
61
|
+
if (request.openAuthorizationUrl === undefined) {
|
|
62
|
+
throw new SebaNoUIError("Node.js cannot open a browser by itself. Pass openAuthorizationUrl in the Seba config, " +
|
|
63
|
+
"for example a function that prints the URL or calls the platform's browser opener.");
|
|
64
|
+
}
|
|
65
|
+
let target;
|
|
66
|
+
try {
|
|
67
|
+
target = new URL(request.redirectUri);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
throw new SebaConfigError(`The redirectUri "${request.redirectUri}" is not an absolute URL.`);
|
|
71
|
+
}
|
|
72
|
+
if (target.protocol !== "http:") {
|
|
73
|
+
throw new SebaConfigError(`Node.js serves the login callback over plain http on loopback, but the redirectUri uses "${target.protocol}". ` +
|
|
74
|
+
'Configure something like "http://127.0.0.1:3000/auth/callback" and publish it in the Client ID Document.');
|
|
75
|
+
}
|
|
76
|
+
if (!isLoopbackHost(target.hostname)) {
|
|
77
|
+
throw new SebaConfigError(`The redirectUri "${request.redirectUri}" does not point at a loopback host. ` +
|
|
78
|
+
"SEBA only listens on 127.0.0.1, ::1 or localhost.");
|
|
79
|
+
}
|
|
80
|
+
const hostname = target.hostname.replace(/^\[|\]$/g, "");
|
|
81
|
+
const port = target.port === "" ? 80 : Number(target.port);
|
|
82
|
+
const callbackPath = target.pathname;
|
|
83
|
+
return new Promise((resolve, reject) => {
|
|
84
|
+
let settled = false;
|
|
85
|
+
const server = createServer();
|
|
86
|
+
const timer = setTimeout(() => {
|
|
87
|
+
finish(new SebaAuthError("The interactive login timed out after five minutes without a callback. Start the login again."));
|
|
88
|
+
}, LOGIN_TIMEOUT_MS);
|
|
89
|
+
timer.unref();
|
|
90
|
+
server.on("request", (req, res) => {
|
|
91
|
+
const url = new URL(req.url ?? "/", "http://placeholder");
|
|
92
|
+
if (req.method !== "GET" || url.pathname !== callbackPath) {
|
|
93
|
+
respond(res, 404, FAILURE_HTML);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (url.searchParams.get("state") !== request.state) {
|
|
97
|
+
respond(res, 400, FAILURE_HTML);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const hasResponse = url.searchParams.has("code") || url.searchParams.has("error");
|
|
101
|
+
if (!hasResponse) {
|
|
102
|
+
respond(res, 400, FAILURE_HTML);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
respond(res, 200, SUCCESS_HTML);
|
|
106
|
+
const callback = new URL(`${callbackPath}${url.search}`, target.origin);
|
|
107
|
+
finish(null, callback);
|
|
108
|
+
});
|
|
109
|
+
server.on("error", (error) => {
|
|
110
|
+
if (error.code === "EADDRINUSE") {
|
|
111
|
+
finish(new SebaConfigError(`Port ${String(port)} on ${hostname} is already in use, so the login callback cannot be received. ` +
|
|
112
|
+
"Choose a different port in redirectUri and in the Client ID Document.", { cause: error }));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
finish(new SebaConfigError(`The loopback callback server could not start: ${error.message}`, {
|
|
116
|
+
cause: error,
|
|
117
|
+
}));
|
|
118
|
+
});
|
|
119
|
+
server.listen(port, hostname, () => {
|
|
120
|
+
Promise.resolve()
|
|
121
|
+
.then(() => request.openAuthorizationUrl?.(request.authorizationUrl))
|
|
122
|
+
.catch((error) => {
|
|
123
|
+
finish(new SebaAuthError("openAuthorizationUrl failed, so the login could not be started.", {
|
|
124
|
+
cause: error,
|
|
125
|
+
}));
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
function finish(error, callback) {
|
|
129
|
+
if (settled) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
settled = true;
|
|
133
|
+
clearTimeout(timer);
|
|
134
|
+
server.closeAllConnections();
|
|
135
|
+
// Settle only once the listening socket is closed, so the port is free for the next
|
|
136
|
+
// login and no request can be delivered to this server after the promise resolves.
|
|
137
|
+
server.close(() => {
|
|
138
|
+
if (error !== null) {
|
|
139
|
+
reject(error);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (callback === undefined) {
|
|
143
|
+
reject(new SebaAuthError("The loopback server finished without a callback URL."));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
resolve(callback);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function respond(res, status, html) {
|
|
152
|
+
res.writeHead(status, {
|
|
153
|
+
"content-type": "text/html; charset=utf-8",
|
|
154
|
+
"cache-control": "no-store",
|
|
155
|
+
"content-security-policy": "default-src 'none'; style-src 'unsafe-inline'",
|
|
156
|
+
"referrer-policy": "no-referrer",
|
|
157
|
+
connection: "close",
|
|
158
|
+
});
|
|
159
|
+
res.end(html);
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=platform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../../src/node/platform.ts"],"names":[],"mappings":"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"}
|
package/dist/seba.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `Seba` facade: resolves the runtime platform, then forwards to the protocol engine.
|
|
3
|
+
*
|
|
4
|
+
* The platform module is imported dynamically so a browser bundle never includes the
|
|
5
|
+
* Node.js HTTP server and vice versa.
|
|
6
|
+
*/
|
|
7
|
+
import type { LoginOptions, SebaConfig, Session } from "./types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Solid-OIDC client with four operations: {@link Seba.login}, {@link Seba.restoreSession},
|
|
10
|
+
* {@link Seba.fetch} and {@link Seba.logout}.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const seba = new Seba({
|
|
15
|
+
* clientId: "https://app.example/client-id.jsonld",
|
|
16
|
+
* redirectUri: "https://app.example/auth/callback",
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* const session = await seba.restoreSession() ?? await seba.login({ webId });
|
|
20
|
+
* const response = await seba.fetch(session.webId);
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export declare class Seba {
|
|
24
|
+
#private;
|
|
25
|
+
/**
|
|
26
|
+
* @param config - Client ID Document URL, redirect URI and optional storage settings.
|
|
27
|
+
* Configuration is validated when the first operation runs.
|
|
28
|
+
*/
|
|
29
|
+
constructor(config: SebaConfig);
|
|
30
|
+
/** Start or finish an interactive login. See {@link SebaClient.login}. */
|
|
31
|
+
login(options?: LoginOptions): Promise<Session>;
|
|
32
|
+
/** Restore a stored session, or `null` when none exists. */
|
|
33
|
+
restoreSession(): Promise<Session | null>;
|
|
34
|
+
/** DPoP-authenticated `fetch`. Never forwards credentials to another origin. */
|
|
35
|
+
fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
|
|
36
|
+
/** Sign out and revoke the refresh token when the provider supports it. */
|
|
37
|
+
logout(): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=seba.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seba.d.ts","sourceRoot":"","sources":["../src/seba.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAY,UAAU,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAY9E;;;;;;;;;;;;;;GAcG;AACH,qBAAa,IAAI;;IAGf;;;OAGG;gBACS,MAAM,EAAE,UAAU;IAI9B,0EAA0E;IACpE,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzD,4DAA4D;IACtD,cAAc,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAI/C,gFAAgF;IAC1E,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAI5E,2EAA2E;IACrE,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAG9B"}
|
package/dist/seba.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `Seba` facade: resolves the runtime platform, then forwards to the protocol engine.
|
|
3
|
+
*
|
|
4
|
+
* The platform module is imported dynamically so a browser bundle never includes the
|
|
5
|
+
* Node.js HTTP server and vice versa.
|
|
6
|
+
*/
|
|
7
|
+
import { SebaClient } from "./core/client.js";
|
|
8
|
+
async function loadPlatform() {
|
|
9
|
+
const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
10
|
+
if (isBrowser) {
|
|
11
|
+
const { createBrowserPlatform } = await import("./browser/platform.js");
|
|
12
|
+
return createBrowserPlatform();
|
|
13
|
+
}
|
|
14
|
+
const { createNodePlatform } = await import("./node/platform.js");
|
|
15
|
+
return createNodePlatform();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Solid-OIDC client with four operations: {@link Seba.login}, {@link Seba.restoreSession},
|
|
19
|
+
* {@link Seba.fetch} and {@link Seba.logout}.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const seba = new Seba({
|
|
24
|
+
* clientId: "https://app.example/client-id.jsonld",
|
|
25
|
+
* redirectUri: "https://app.example/auth/callback",
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* const session = await seba.restoreSession() ?? await seba.login({ webId });
|
|
29
|
+
* const response = await seba.fetch(session.webId);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export class Seba {
|
|
33
|
+
#client;
|
|
34
|
+
/**
|
|
35
|
+
* @param config - Client ID Document URL, redirect URI and optional storage settings.
|
|
36
|
+
* Configuration is validated when the first operation runs.
|
|
37
|
+
*/
|
|
38
|
+
constructor(config) {
|
|
39
|
+
this.#client = loadPlatform().then((platform) => new SebaClient(config, platform));
|
|
40
|
+
}
|
|
41
|
+
/** Start or finish an interactive login. See {@link SebaClient.login}. */
|
|
42
|
+
async login(options = {}) {
|
|
43
|
+
return (await this.#client).login(options);
|
|
44
|
+
}
|
|
45
|
+
/** Restore a stored session, or `null` when none exists. */
|
|
46
|
+
async restoreSession() {
|
|
47
|
+
return (await this.#client).restoreSession();
|
|
48
|
+
}
|
|
49
|
+
/** DPoP-authenticated `fetch`. Never forwards credentials to another origin. */
|
|
50
|
+
async fetch(input, init) {
|
|
51
|
+
return (await this.#client).fetch(input, init);
|
|
52
|
+
}
|
|
53
|
+
/** Sign out and revoke the refresh token when the provider supports it. */
|
|
54
|
+
async logout() {
|
|
55
|
+
return (await this.#client).logout();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=seba.js.map
|
package/dist/seba.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seba.js","sourceRoot":"","sources":["../src/seba.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,KAAK,UAAU,YAAY;IACzB,MAAM,SAAS,GAAG,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,QAAQ,KAAK,WAAW,CAAC;IACnF,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,EAAE,qBAAqB,EAAE,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC;QACxE,OAAO,qBAAqB,EAAE,CAAC;IACjC,CAAC;IACD,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAClE,OAAO,kBAAkB,EAAE,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,IAAI;IACN,OAAO,CAAsB;IAEtC;;;OAGG;IACH,YAAY,MAAkB;QAC5B,IAAI,CAAC,OAAO,GAAG,YAAY,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;IACrF,CAAC;IAED,0EAA0E;IAC1E,KAAK,CAAC,KAAK,CAAC,UAAwB,EAAE;QACpC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAED,4DAA4D;IAC5D,KAAK,CAAC,cAAc;QAClB,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,cAAc,EAAE,CAAC;IAC/C,CAAC;IAED,gFAAgF;IAChF,KAAK,CAAC,KAAK,CAAC,KAAwB,EAAE,IAAkB;QACtD,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACjD,CAAC;IAED,2EAA2E;IAC3E,KAAK,CAAC,MAAM;QACV,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,CAAC;IACvC,CAAC;CACF"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public and internal types used across SEBA.
|
|
3
|
+
*
|
|
4
|
+
* The public surface is intentionally small: {@link SebaConfig}, {@link LoginOptions},
|
|
5
|
+
* {@link Session}, and the value types below that appear in the API documentation.
|
|
6
|
+
*/
|
|
7
|
+
/** Where SEBA stores session secrets. */
|
|
8
|
+
export type StorageKind = "indexeddb" | "memory";
|
|
9
|
+
/**
|
|
10
|
+
* Configuration for the {@link Seba} class.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const config: SebaConfig = {
|
|
15
|
+
* clientId: "https://myapp.example/client-id.jsonld",
|
|
16
|
+
* redirectUri: "https://myapp.example/auth/callback",
|
|
17
|
+
* };
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export interface SebaConfig {
|
|
21
|
+
/**
|
|
22
|
+
* Absolute HTTPS URL of the application's published Solid Client ID Document.
|
|
23
|
+
* This URL is sent to the identity provider as the OAuth `client_id`.
|
|
24
|
+
*/
|
|
25
|
+
clientId: string;
|
|
26
|
+
/**
|
|
27
|
+
* Absolute URL the identity provider redirects back to. Must appear verbatim in
|
|
28
|
+
* the Client ID Document's `redirect_uris`. In Node.js it must point at a loopback
|
|
29
|
+
* interface (`http://127.0.0.1:<port>/<path>` or `http://localhost:<port>/<path>`).
|
|
30
|
+
*/
|
|
31
|
+
redirectUri: string;
|
|
32
|
+
/**
|
|
33
|
+
* Session storage backend. Defaults to `"indexeddb"` in browsers and
|
|
34
|
+
* `"memory"` in Node.js.
|
|
35
|
+
*
|
|
36
|
+
* `"indexeddb"` persists only the refresh token, the DPoP private key and
|
|
37
|
+
* non-secret session metadata. Access tokens are never persisted.
|
|
38
|
+
*/
|
|
39
|
+
storage?: StorageKind;
|
|
40
|
+
/**
|
|
41
|
+
* Emit non-sensitive diagnostics through `console.debug`. Tokens, keys,
|
|
42
|
+
* authorization codes and PKCE verifiers are never logged.
|
|
43
|
+
*
|
|
44
|
+
* @defaultValue `false`
|
|
45
|
+
*/
|
|
46
|
+
debug?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Node.js only. Receives the authorization URL during an interactive login so
|
|
49
|
+
* the application can open it in a browser or print it for the user.
|
|
50
|
+
*
|
|
51
|
+
* Without this hook Node.js cannot complete an interactive login and
|
|
52
|
+
* {@link Seba.login} throws a `SebaNoUIError`.
|
|
53
|
+
*/
|
|
54
|
+
openAuthorizationUrl?: (url: string) => void | Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
/** Optional identity hints for {@link Seba.login}. */
|
|
57
|
+
export interface LoginOptions {
|
|
58
|
+
/** WebID whose profile lists the trusted identity provider. */
|
|
59
|
+
webId?: string;
|
|
60
|
+
/** Identity provider issuer URL, used instead of WebID discovery. */
|
|
61
|
+
issuer?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A validated, authenticated Solid session.
|
|
65
|
+
*
|
|
66
|
+
* The session object contains no tokens; `Seba.fetch` uses them internally.
|
|
67
|
+
*/
|
|
68
|
+
export interface Session {
|
|
69
|
+
/** WebID proven by the validated ID token. */
|
|
70
|
+
webId: string;
|
|
71
|
+
/** Discriminant that is always `true` for an authenticated session. */
|
|
72
|
+
isLoggedIn: true;
|
|
73
|
+
/** Identity provider issuer URL that was validated for this session. */
|
|
74
|
+
provider: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* A token response that passed validation.
|
|
78
|
+
*
|
|
79
|
+
* Access tokens are held in memory only — never persisted or exposed by the
|
|
80
|
+
* public API.
|
|
81
|
+
*/
|
|
82
|
+
export interface TokenSet {
|
|
83
|
+
/** DPoP-bound access token. In memory only. */
|
|
84
|
+
accessToken: string;
|
|
85
|
+
/** Token type. SEBA fails closed unless the provider returns `DPoP`. */
|
|
86
|
+
tokenType: "DPoP";
|
|
87
|
+
/** Validated OIDC ID token. */
|
|
88
|
+
idToken: string;
|
|
89
|
+
/** Refresh token, when the provider issued one (`offline_access`). */
|
|
90
|
+
refreshToken?: string;
|
|
91
|
+
/** Access token lifetime in seconds, when advertised. */
|
|
92
|
+
expiresIn?: number;
|
|
93
|
+
/** Granted scopes, when advertised. */
|
|
94
|
+
scope?: string;
|
|
95
|
+
}
|
|
96
|
+
/** A freshly generated DPoP proof. One proof is created per HTTP request. */
|
|
97
|
+
export interface DPoPProof {
|
|
98
|
+
/** Compact-serialized, ES256-signed `dpop+jwt`. */
|
|
99
|
+
jwt: string;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* DPoP signing capability used by the protocol modules.
|
|
103
|
+
*
|
|
104
|
+
* Implemented by `core/dpop.ts`. Every call to {@link DPoPSigner.createProof} must sign a
|
|
105
|
+
* brand-new proof: implementations are forbidden from caching or reusing proofs.
|
|
106
|
+
*/
|
|
107
|
+
export interface DPoPSigner {
|
|
108
|
+
/** Create a fresh proof for one HTTP attempt. */
|
|
109
|
+
createProof(input: ProofInput): Promise<string>;
|
|
110
|
+
/** Base64url JWK SHA-256 thumbprint of the signing key. */
|
|
111
|
+
thumbprint(): Promise<string>;
|
|
112
|
+
}
|
|
113
|
+
/** Input for one DPoP proof. */
|
|
114
|
+
export interface ProofInput {
|
|
115
|
+
/** HTTP method of the request being made, in uppercase. */
|
|
116
|
+
readonly method: string;
|
|
117
|
+
/** Absolute target URL; query and fragment are removed before signing. */
|
|
118
|
+
readonly url: string;
|
|
119
|
+
/** Access token being presented (protected-resource requests only). */
|
|
120
|
+
readonly accessToken?: string;
|
|
121
|
+
/** Server-provided `DPoP-Nonce`, when one is known. */
|
|
122
|
+
readonly nonce?: string;
|
|
123
|
+
}
|
|
124
|
+
/** The subset of OpenID Provider metadata SEBA relies on. */
|
|
125
|
+
export interface OIDCConfiguration {
|
|
126
|
+
/** Validated issuer identifier (matches the requested issuer). */
|
|
127
|
+
issuer: string;
|
|
128
|
+
/** Authorization endpoint, absolute HTTPS URL. */
|
|
129
|
+
authorizationEndpoint: string;
|
|
130
|
+
/** Token endpoint, absolute HTTPS URL. */
|
|
131
|
+
tokenEndpoint: string;
|
|
132
|
+
/** JWKS endpoint, absolute HTTPS URL. */
|
|
133
|
+
jwksUri: string;
|
|
134
|
+
/** Token revocation endpoint, when advertised. */
|
|
135
|
+
revocationEndpoint?: string;
|
|
136
|
+
/** DPoP signing algorithms advertised by the provider, when present. */
|
|
137
|
+
dpopSigningAlgValuesSupported?: string[];
|
|
138
|
+
/** Scopes advertised by the provider, when present. */
|
|
139
|
+
scopesSupported?: string[];
|
|
140
|
+
/** Grant types advertised by the provider, when present. */
|
|
141
|
+
grantTypesSupported?: string[];
|
|
142
|
+
}
|
|
143
|
+
/** A validated Solid Client ID Document. */
|
|
144
|
+
export interface ClientIDDocument {
|
|
145
|
+
/** The `client_id` member; must equal the document URL. */
|
|
146
|
+
clientId: string;
|
|
147
|
+
/** Published redirect URIs. Membership is compared exactly. */
|
|
148
|
+
redirectUris: string[];
|
|
149
|
+
/** Human readable client name, when published. */
|
|
150
|
+
clientName?: string;
|
|
151
|
+
}
|
|
152
|
+
/** DPoP private key as stored for session restoration. */
|
|
153
|
+
export type PersistedKey = {
|
|
154
|
+
/** Non-extractable `CryptoKey` stored through structured clone (preferred). */
|
|
155
|
+
kind: "crypto-key";
|
|
156
|
+
key: CryptoKey;
|
|
157
|
+
/**
|
|
158
|
+
* Public half of the stored key.
|
|
159
|
+
*
|
|
160
|
+
* A non-extractable private `CryptoKey` cannot reproduce its public key through
|
|
161
|
+
* Web Crypto, so the public JWK (which contains no private material) is stored
|
|
162
|
+
* alongside it for the DPoP proof header and the JWK thumbprint.
|
|
163
|
+
*/
|
|
164
|
+
publicJwk: JsonWebKey;
|
|
165
|
+
} | {
|
|
166
|
+
/** Exported JWK fallback for runtimes that cannot structured-clone CryptoKeys. */
|
|
167
|
+
kind: "jwk";
|
|
168
|
+
jwk: JsonWebKey;
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* Non-secret session metadata plus the two secrets that must survive a reload:
|
|
172
|
+
* the refresh token and the DPoP private key.
|
|
173
|
+
*
|
|
174
|
+
* Access tokens are deliberately absent from this record.
|
|
175
|
+
*/
|
|
176
|
+
export interface PersistedSession {
|
|
177
|
+
/** Storage schema version. */
|
|
178
|
+
version: 1;
|
|
179
|
+
/** Validated WebID. */
|
|
180
|
+
webId: string;
|
|
181
|
+
/** Validated identity provider issuer. */
|
|
182
|
+
provider: string;
|
|
183
|
+
/** Client identifier the session was created for. */
|
|
184
|
+
clientId: string;
|
|
185
|
+
/** Refresh token. Never an access token. */
|
|
186
|
+
refreshToken: string;
|
|
187
|
+
/** DPoP private key bound to the refresh token. */
|
|
188
|
+
key: PersistedKey;
|
|
189
|
+
/** Granted scopes, when known. */
|
|
190
|
+
scope?: string;
|
|
191
|
+
/** Creation time (epoch milliseconds) for diagnostics. */
|
|
192
|
+
createdAt: number;
|
|
193
|
+
}
|
|
194
|
+
/** Persistence backend for {@link PersistedSession}. */
|
|
195
|
+
export interface SessionStorageAdapter {
|
|
196
|
+
/** Durably write the session record. */
|
|
197
|
+
save(session: PersistedSession): Promise<void>;
|
|
198
|
+
/** Read the session record, or `null` when none exists. */
|
|
199
|
+
load(): Promise<PersistedSession | null>;
|
|
200
|
+
/** Remove the session record. Must not throw when nothing is stored. */
|
|
201
|
+
clear(): Promise<void>;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Short-lived authorization transaction state.
|
|
205
|
+
*
|
|
206
|
+
* Contains the PKCE verifier and expected nonce, so it is kept in
|
|
207
|
+
* `sessionStorage` (browser) or process memory (Node.js) — never in
|
|
208
|
+
* `localStorage` and never in a URL.
|
|
209
|
+
*/
|
|
210
|
+
export interface PendingAuthorization {
|
|
211
|
+
/** OAuth `state` sent with the authorization request. */
|
|
212
|
+
state: string;
|
|
213
|
+
/** PKCE `code_verifier`. */
|
|
214
|
+
codeVerifier: string;
|
|
215
|
+
/** Expected OIDC `nonce`. */
|
|
216
|
+
nonce: string;
|
|
217
|
+
/** Issuer the user is being sent to (RFC 9207 `iss` validation target). */
|
|
218
|
+
issuer: string;
|
|
219
|
+
/** Client identifier used for the request. */
|
|
220
|
+
clientId: string;
|
|
221
|
+
/** Redirect URI used for the request. */
|
|
222
|
+
redirectUri: string;
|
|
223
|
+
/** WebID the user asked to sign in with, when known. */
|
|
224
|
+
webId?: string;
|
|
225
|
+
/** Creation time (epoch milliseconds); stale transactions are discarded. */
|
|
226
|
+
createdAt: number;
|
|
227
|
+
}
|
|
228
|
+
/** Persistence for {@link PendingAuthorization}. */
|
|
229
|
+
export interface PendingAuthorizationStore {
|
|
230
|
+
save(pending: PendingAuthorization): Promise<void>;
|
|
231
|
+
load(): Promise<PendingAuthorization | null>;
|
|
232
|
+
clear(): Promise<void>;
|
|
233
|
+
}
|
|
234
|
+
/** Runtime capabilities the core code needs from its host. */
|
|
235
|
+
export interface Platform {
|
|
236
|
+
/** Runtime name, used for defaults and error messages. */
|
|
237
|
+
readonly kind: "browser" | "node";
|
|
238
|
+
/** Storage backend used when the configuration does not force one. */
|
|
239
|
+
readonly defaultStorage: StorageKind;
|
|
240
|
+
/**
|
|
241
|
+
* Runtime-specific `fetch` implementation used for provider and resource requests.
|
|
242
|
+
*
|
|
243
|
+
* Injected so Node.js can apply its own timeout/error mapping without touching DOM
|
|
244
|
+
* globals, and so tests can supply a deterministic implementation.
|
|
245
|
+
*/
|
|
246
|
+
readonly fetch: typeof fetch;
|
|
247
|
+
/** Current document URL, or `null` when there is no document context. */
|
|
248
|
+
currentUrl(): URL | null;
|
|
249
|
+
/** Short-lived authorization transaction storage. */
|
|
250
|
+
readonly pendingAuth: PendingAuthorizationStore;
|
|
251
|
+
/**
|
|
252
|
+
* Perform the interactive step and resolve with the authorization response URL.
|
|
253
|
+
*
|
|
254
|
+
* Browsers navigate away, so the returned promise intentionally never settles.
|
|
255
|
+
* Node.js resolves it from the temporary loopback callback server.
|
|
256
|
+
*/
|
|
257
|
+
beginInteractiveLogin(request: InteractiveLoginRequest): Promise<URL>;
|
|
258
|
+
/** Ask the user for a WebID or issuer. Missing outside the browser. */
|
|
259
|
+
selectIdentity?(): Promise<LoginOptions>;
|
|
260
|
+
/** Create a session storage adapter for the requested backend. */
|
|
261
|
+
createStorage(kind: StorageKind): SessionStorageAdapter;
|
|
262
|
+
/**
|
|
263
|
+
* Remove authorization response parameters from the visible URL after they have been
|
|
264
|
+
* consumed. Browsers implement this with `history.replaceState`; Node.js has no URL.
|
|
265
|
+
*/
|
|
266
|
+
clearCallback?(): void;
|
|
267
|
+
}
|
|
268
|
+
/** Input for {@link Platform.beginInteractiveLogin}. */
|
|
269
|
+
export interface InteractiveLoginRequest {
|
|
270
|
+
/** Fully constructed authorization URL. */
|
|
271
|
+
authorizationUrl: string;
|
|
272
|
+
/** Redirect URI registered with the provider. */
|
|
273
|
+
redirectUri: string;
|
|
274
|
+
/** Expected OAuth state, used by the loopback server to ignore stray requests. */
|
|
275
|
+
state: string;
|
|
276
|
+
/** Open the authorization URL in a browser. Node.js only. */
|
|
277
|
+
openAuthorizationUrl?: (url: string) => void | Promise<void>;
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public and internal types used across SEBA.
|
|
3
|
+
*
|
|
4
|
+
* The public surface is intentionally small: {@link SebaConfig}, {@link LoginOptions},
|
|
5
|
+
* {@link Session}, and the value types below that appear in the API documentation.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|