@parlor/auth 0.0.0-stage → 0.1.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 +21 -0
- package/README.md +25 -2
- package/dist/account.d.ts +52 -0
- package/dist/account.d.ts.map +1 -0
- package/dist/account.js +114 -0
- package/dist/account.js.map +1 -0
- package/dist/audit.d.ts +10 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +130 -0
- package/dist/audit.js.map +1 -0
- package/dist/better-auth.d.ts +41 -0
- package/dist/better-auth.d.ts.map +1 -0
- package/dist/better-auth.js +118 -0
- package/dist/better-auth.js.map +1 -0
- package/dist/client/index.d.ts +43 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +465 -0
- package/dist/client/index.js.map +1 -0
- package/dist/database.d.ts +7 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +11 -0
- package/dist/database.js.map +1 -0
- package/dist/devices.d.ts +157 -0
- package/dist/devices.d.ts.map +1 -0
- package/dist/devices.js +398 -0
- package/dist/devices.js.map +1 -0
- package/dist/es256.d.ts +4 -0
- package/dist/es256.d.ts.map +1 -0
- package/dist/es256.js +44 -0
- package/dist/es256.js.map +1 -0
- package/dist/grants.d.ts +4 -0
- package/dist/grants.d.ts.map +1 -0
- package/dist/grants.js +6 -0
- package/dist/grants.js.map +1 -0
- package/dist/hosts.d.ts +116 -0
- package/dist/hosts.d.ts.map +1 -0
- package/dist/hosts.js +530 -0
- package/dist/hosts.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations.d.ts +3 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +17 -0
- package/dist/migrations.js.map +1 -0
- package/dist/pages.d.ts +4 -0
- package/dist/pages.d.ts.map +1 -0
- package/dist/pages.js +53 -0
- package/dist/pages.js.map +1 -0
- package/dist/provider.d.ts +74 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +286 -0
- package/dist/provider.js.map +1 -0
- package/dist/revocation.d.ts +51 -0
- package/dist/revocation.d.ts.map +1 -0
- package/dist/revocation.js +233 -0
- package/dist/revocation.js.map +1 -0
- package/dist/routes.d.ts +15 -0
- package/dist/routes.d.ts.map +1 -0
- package/dist/routes.js +116 -0
- package/dist/routes.js.map +1 -0
- package/dist/schema.d.ts +2970 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +308 -0
- package/dist/schema.js.map +1 -0
- package/dist/statements.d.ts +92 -0
- package/dist/statements.d.ts.map +1 -0
- package/dist/statements.js +199 -0
- package/dist/statements.js.map +1 -0
- package/dist/tokens.d.ts +69 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +134 -0
- package/dist/tokens.js.map +1 -0
- package/dist/turn-keys.d.ts +16 -0
- package/dist/turn-keys.d.ts.map +1 -0
- package/dist/turn-keys.js +108 -0
- package/dist/turn-keys.js.map +1 -0
- package/package.json +57 -3
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { sql } from "drizzle-orm";
|
|
2
|
+
import { rowsOf } from "./database.js";
|
|
3
|
+
export const REVOCATION_CHANNEL = "parlor_auth";
|
|
4
|
+
const keyOf = ({ kind, id }) => `${kind}:${id}`;
|
|
5
|
+
const owner = Symbol("parlor.revocation.owner");
|
|
6
|
+
/**
|
|
7
|
+
* Every instance's view of what was revoked within the horizon. It loads before it
|
|
8
|
+
* admits anyone, and whenever its LISTEN connection drops it checks the table per call
|
|
9
|
+
* until it listens again and reloads, because a NOTIFY sent meanwhile is lost.
|
|
10
|
+
*/
|
|
11
|
+
export class RevocationSet {
|
|
12
|
+
#database;
|
|
13
|
+
#horizonMs;
|
|
14
|
+
#now;
|
|
15
|
+
#relistenBaseMs;
|
|
16
|
+
#relistenMaxMs;
|
|
17
|
+
#revoked = new Map();
|
|
18
|
+
#watches = new Map();
|
|
19
|
+
#forget = new FinalizationRegistry((key) => this.#sweepWatch(key));
|
|
20
|
+
#mode = "starting";
|
|
21
|
+
#ready;
|
|
22
|
+
#generation = 0;
|
|
23
|
+
#unlisten;
|
|
24
|
+
#reconnecting = false;
|
|
25
|
+
#retry;
|
|
26
|
+
#sweptAt = 0;
|
|
27
|
+
#stopped = false;
|
|
28
|
+
constructor(options) {
|
|
29
|
+
this.#database = options.database;
|
|
30
|
+
this.#horizonMs = options.horizonMs;
|
|
31
|
+
this.#now = options.now ?? Date.now;
|
|
32
|
+
this.#relistenBaseMs = options.relistenBaseMs ?? 1000;
|
|
33
|
+
this.#relistenMaxMs = options.relistenMaxMs ?? 30_000;
|
|
34
|
+
}
|
|
35
|
+
get mode() {
|
|
36
|
+
return this.#mode;
|
|
37
|
+
}
|
|
38
|
+
/** Resolves once the set can answer; a failed start is retried by the next call. */
|
|
39
|
+
ready() {
|
|
40
|
+
this.#ready ??= this.#start().catch((error) => {
|
|
41
|
+
this.#ready = undefined;
|
|
42
|
+
throw error;
|
|
43
|
+
});
|
|
44
|
+
return this.#ready;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Revoked when a revocation of the notice is at or after `issuedAt`; without it, when
|
|
48
|
+
* any revocation of the notice is within the horizon.
|
|
49
|
+
*/
|
|
50
|
+
async isRevoked(notice, issuedAt = 0) {
|
|
51
|
+
await this.ready();
|
|
52
|
+
const at = this.#mode === "following"
|
|
53
|
+
? this.#revoked.get(keyOf(notice))
|
|
54
|
+
: await this.#lookup(notice);
|
|
55
|
+
return at !== undefined && at >= Math.max(issuedAt, this.#now() - this.#horizonMs);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Aborts when any of the notices is revoked at or after `since` (default: now). The
|
|
59
|
+
* watch is registered first and the revocations already known are checked after, so one
|
|
60
|
+
* that landed between `since` and this call still aborts it.
|
|
61
|
+
*/
|
|
62
|
+
signal(notices, since = this.#now()) {
|
|
63
|
+
const controller = new AbortController();
|
|
64
|
+
// The signal pins its controller, so a watch lives exactly as long as its holder.
|
|
65
|
+
Object.defineProperty(controller.signal, owner, { value: controller });
|
|
66
|
+
for (const notice of notices) {
|
|
67
|
+
const key = keyOf(notice);
|
|
68
|
+
const watch = this.#watches.get(key);
|
|
69
|
+
if (watch)
|
|
70
|
+
watch.since = Math.min(watch.since, since);
|
|
71
|
+
else
|
|
72
|
+
this.#watches.set(key, { controllers: new Set(), since });
|
|
73
|
+
this.#watches.get(key).controllers.add(new WeakRef(controller));
|
|
74
|
+
this.#forget.register(controller.signal, key);
|
|
75
|
+
}
|
|
76
|
+
this.#abortIfRevoked(controller, notices, since);
|
|
77
|
+
return controller.signal;
|
|
78
|
+
}
|
|
79
|
+
/** Records the revocation in the caller's transaction; the NOTIFY goes out on commit. */
|
|
80
|
+
static async revoke(tx, notice, at = Date.now()) {
|
|
81
|
+
const db = tx;
|
|
82
|
+
await db.execute(sql `
|
|
83
|
+
insert into parlor_auth_revocations (kind, id, revoked_at)
|
|
84
|
+
values (${notice.kind}, ${notice.id}, ${new Date(at).toISOString()})
|
|
85
|
+
on conflict (kind, id) do update set revoked_at = excluded.revoked_at`);
|
|
86
|
+
const payload = { kind: notice.kind, id: notice.id, at };
|
|
87
|
+
await db.execute(sql `select pg_notify(${REVOCATION_CHANNEL}, ${JSON.stringify(payload)})`);
|
|
88
|
+
}
|
|
89
|
+
async stop() {
|
|
90
|
+
this.#stopped = true;
|
|
91
|
+
this.#generation += 1;
|
|
92
|
+
if (this.#retry !== undefined)
|
|
93
|
+
clearTimeout(this.#retry);
|
|
94
|
+
const unlisten = this.#unlisten;
|
|
95
|
+
this.#unlisten = undefined;
|
|
96
|
+
await unlisten?.().catch(() => { });
|
|
97
|
+
}
|
|
98
|
+
async #start() {
|
|
99
|
+
await this.#prune();
|
|
100
|
+
if (!this.#database.listen) {
|
|
101
|
+
this.#mode = "checking";
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
await this.#connect(this.#now() - this.#horizonMs);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
console.warn("[parlor] revocations: LISTEN failed, checking per call:", error);
|
|
109
|
+
this.#mode = "checking";
|
|
110
|
+
this.#reconnect(this.#now());
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async #connect(since) {
|
|
114
|
+
const generation = ++this.#generation;
|
|
115
|
+
const unlisten = await this.#database.listen(REVOCATION_CHANNEL, (payload) => this.#receive(payload), { onLost: () => this.#lost(generation) });
|
|
116
|
+
if (generation !== this.#generation) {
|
|
117
|
+
await unlisten().catch(() => { });
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
this.#unlisten = unlisten;
|
|
121
|
+
const revocations = await this.#load(since);
|
|
122
|
+
if (generation !== this.#generation)
|
|
123
|
+
return;
|
|
124
|
+
for (const revocation of revocations)
|
|
125
|
+
this.#apply(revocation);
|
|
126
|
+
this.#mode = "following";
|
|
127
|
+
}
|
|
128
|
+
#lost(generation) {
|
|
129
|
+
if (generation !== this.#generation || this.#stopped)
|
|
130
|
+
return;
|
|
131
|
+
this.#generation += 1;
|
|
132
|
+
this.#unlisten = undefined;
|
|
133
|
+
this.#mode = "checking";
|
|
134
|
+
this.#reconnect(this.#now());
|
|
135
|
+
}
|
|
136
|
+
#reconnect(lostAt) {
|
|
137
|
+
if (this.#reconnecting)
|
|
138
|
+
return;
|
|
139
|
+
this.#reconnecting = true;
|
|
140
|
+
let delay = this.#relistenBaseMs;
|
|
141
|
+
const attempt = async () => {
|
|
142
|
+
this.#retry = undefined;
|
|
143
|
+
if (this.#stopped)
|
|
144
|
+
return;
|
|
145
|
+
try {
|
|
146
|
+
await this.#connect(Math.min(lostAt, this.#now() - this.#horizonMs));
|
|
147
|
+
this.#reconnecting = false;
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
console.warn("[parlor] revocations: LISTEN failed, retrying:", error);
|
|
151
|
+
this.#retry = setTimeout(attempt, delay);
|
|
152
|
+
delay = Math.min(delay * 2, this.#relistenMaxMs);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
this.#retry = setTimeout(attempt, 0);
|
|
156
|
+
}
|
|
157
|
+
#receive(payload) {
|
|
158
|
+
try {
|
|
159
|
+
this.#apply(JSON.parse(payload));
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
console.warn("[parlor] revocations: ignored a malformed notice");
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
#apply(revocation) {
|
|
166
|
+
const key = keyOf(revocation);
|
|
167
|
+
const known = this.#revoked.get(key);
|
|
168
|
+
if (known === undefined || revocation.at > known)
|
|
169
|
+
this.#revoked.set(key, revocation.at);
|
|
170
|
+
this.#sweepExpired();
|
|
171
|
+
const watch = this.#watches.get(key);
|
|
172
|
+
if (!watch || revocation.at < watch.since)
|
|
173
|
+
return;
|
|
174
|
+
this.#watches.delete(key);
|
|
175
|
+
for (const ref of watch.controllers)
|
|
176
|
+
ref.deref()?.abort();
|
|
177
|
+
}
|
|
178
|
+
#abortIfRevoked(controller, notices, since) {
|
|
179
|
+
const floor = Math.max(since, this.#now() - this.#horizonMs);
|
|
180
|
+
if (this.#mode === "following") {
|
|
181
|
+
const hit = notices.some((notice) => (this.#revoked.get(keyOf(notice)) ?? -1) >= floor);
|
|
182
|
+
if (hit)
|
|
183
|
+
controller.abort();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
Promise.all(notices.map((notice) => this.#lookup(notice))).then((found) => {
|
|
187
|
+
if (found.some((at) => at !== undefined && at >= floor))
|
|
188
|
+
controller.abort();
|
|
189
|
+
}, () => controller.abort());
|
|
190
|
+
}
|
|
191
|
+
#sweepExpired() {
|
|
192
|
+
const now = this.#now();
|
|
193
|
+
if (now - this.#sweptAt < this.#horizonMs)
|
|
194
|
+
return;
|
|
195
|
+
this.#sweptAt = now;
|
|
196
|
+
for (const [key, at] of this.#revoked) {
|
|
197
|
+
if (at < now - this.#horizonMs)
|
|
198
|
+
this.#revoked.delete(key);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
#sweepWatch(key) {
|
|
202
|
+
const watch = this.#watches.get(key);
|
|
203
|
+
if (!watch)
|
|
204
|
+
return;
|
|
205
|
+
for (const ref of watch.controllers)
|
|
206
|
+
if (!ref.deref())
|
|
207
|
+
watch.controllers.delete(ref);
|
|
208
|
+
if (watch.controllers.size === 0)
|
|
209
|
+
this.#watches.delete(key);
|
|
210
|
+
}
|
|
211
|
+
async #lookup(notice) {
|
|
212
|
+
const rows = rowsOf(await this.#database.read().execute(sql `
|
|
213
|
+
select revoked_at from parlor_auth_revocations
|
|
214
|
+
where kind = ${notice.kind} and id = ${notice.id}`));
|
|
215
|
+
const row = rows[0];
|
|
216
|
+
return row ? new Date(row.revoked_at).getTime() : undefined;
|
|
217
|
+
}
|
|
218
|
+
async #load(since) {
|
|
219
|
+
const rows = rowsOf(await this.#database.read().execute(sql `
|
|
220
|
+
select kind, id, revoked_at from parlor_auth_revocations
|
|
221
|
+
where revoked_at >= ${new Date(since).toISOString()}`));
|
|
222
|
+
return rows.map((row) => ({
|
|
223
|
+
kind: row.kind,
|
|
224
|
+
id: row.id,
|
|
225
|
+
at: new Date(row.revoked_at).getTime(),
|
|
226
|
+
}));
|
|
227
|
+
}
|
|
228
|
+
async #prune() {
|
|
229
|
+
const cutoff = new Date(this.#now() - this.#horizonMs).toISOString();
|
|
230
|
+
await this.#database.read().execute(sql `delete from parlor_auth_revocations where revoked_at < ${cutoff}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
//# sourceMappingURL=revocation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revocation.js","sourceRoot":"","sources":["../src/revocation.ts"],"names":[],"mappings":"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"}
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type AnyPluginRoute, type PluginHost } from "@parlor/server";
|
|
2
|
+
import { type ParlorAuthSettings } from "./better-auth.js";
|
|
3
|
+
import type { BetterAuthInstance } from "./provider.js";
|
|
4
|
+
interface RouteDeps {
|
|
5
|
+
readonly instance: BetterAuthInstance;
|
|
6
|
+
readonly settings: ParlorAuthSettings;
|
|
7
|
+
readonly host: PluginHost;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Better Auth's endpoints under `/_parlor/auth`, the OAuth discovery documents MCP clients
|
|
11
|
+
* read at the origin root, and the consent page an MCP client's authorization stops at.
|
|
12
|
+
*/
|
|
13
|
+
export declare function authRoutes({ instance, settings, host }: RouteDeps): AnyPluginRoute[];
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,UAAU,EAGhB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAA6B,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAEtF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExD,UAAU,SAAS;IACjB,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC3B;AAiBD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,SAAS,GAAG,cAAc,EAAE,CAoGpF"}
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { pluginRoute, } from "@parlor/server";
|
|
2
|
+
import { AUTH_PLUGIN, CONSENT_PATH } from "./better-auth.js";
|
|
3
|
+
import { escape, failure, page } from "./pages.js";
|
|
4
|
+
/** A client's name is self-declared; only an https client id proves who publishes it. */
|
|
5
|
+
function publisherOf(clientId) {
|
|
6
|
+
if (!clientId.startsWith("https://"))
|
|
7
|
+
return "This agent named itself; its publisher is not verified.";
|
|
8
|
+
return `Published by <strong>${escape(new URL(clientId).host)}</strong>.`;
|
|
9
|
+
}
|
|
10
|
+
function redirectOrigin(query) {
|
|
11
|
+
try {
|
|
12
|
+
return new URL(query.get("redirect_uri") ?? "").origin;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Better Auth's endpoints under `/_parlor/auth`, the OAuth discovery documents MCP clients
|
|
20
|
+
* read at the origin root, and the consent page an MCP client's authorization stops at.
|
|
21
|
+
*/
|
|
22
|
+
export function authRoutes({ instance, settings, host }) {
|
|
23
|
+
const forward = (method, path) => ({
|
|
24
|
+
method,
|
|
25
|
+
path,
|
|
26
|
+
access: "public",
|
|
27
|
+
handle: (request) => instance.handler(request),
|
|
28
|
+
});
|
|
29
|
+
const appName = () => instance.options.appName ?? new URL(settings.origin).host;
|
|
30
|
+
const consentPage = pluginRoute({
|
|
31
|
+
method: "GET",
|
|
32
|
+
path: CONSENT_PATH,
|
|
33
|
+
access: ["user"],
|
|
34
|
+
handle: async (request) => {
|
|
35
|
+
const query = new URL(request.url).searchParams;
|
|
36
|
+
const clientId = query.get("client_id");
|
|
37
|
+
const target = redirectOrigin(query);
|
|
38
|
+
if (!clientId || !target || !query.get("sig")) {
|
|
39
|
+
return failure("This authorization request is incomplete.", 400);
|
|
40
|
+
}
|
|
41
|
+
const session = await instance.api.getSession({ headers: request.headers });
|
|
42
|
+
if (!session)
|
|
43
|
+
return failure("Sign in again to continue.", 401);
|
|
44
|
+
const { adapter } = await instance.$context;
|
|
45
|
+
const client = await adapter.findOne({
|
|
46
|
+
model: "oauthClient",
|
|
47
|
+
where: [{ field: "clientId", value: clientId }],
|
|
48
|
+
});
|
|
49
|
+
if (!client)
|
|
50
|
+
return failure("This agent is not registered.", 400);
|
|
51
|
+
const agent = client.name || clientId;
|
|
52
|
+
const app = appName();
|
|
53
|
+
const offline = (query.get("scope") ?? "").split(" ").includes("offline_access");
|
|
54
|
+
const body = `
|
|
55
|
+
<h1>Allow ${escape(agent)} to use ${escape(app)}?</h1>
|
|
56
|
+
<p>${publisherOf(clientId)}</p>
|
|
57
|
+
<p>Signed in as <strong>${escape(session.user.email)}</strong>.</p>
|
|
58
|
+
<ul>
|
|
59
|
+
<li>It can do anything you can do in ${escape(app)}, as you. Actions that ask for confirmation still ask you first.</li>
|
|
60
|
+
<li>${offline ? "It stays connected until you revoke it in your account settings." : "Its access ends after a few minutes."}</li>
|
|
61
|
+
<li>You will be sent back to <strong>${escape(new URL(target).host)}</strong>.</li>
|
|
62
|
+
</ul>
|
|
63
|
+
<form method="post" action="${escape(`${host.basePath}/${AUTH_PLUGIN}${CONSENT_PATH}`)}">
|
|
64
|
+
<input type="hidden" name="oauth_query" value="${escape(query.toString())}">
|
|
65
|
+
<button type="submit" name="accept" value="false">Deny</button>
|
|
66
|
+
<button type="submit" name="accept" value="true" autofocus>Allow</button>
|
|
67
|
+
</form>`;
|
|
68
|
+
return page(`Allow ${agent}?`, body, ["'self'", target]);
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
const consentAnswer = pluginRoute({
|
|
72
|
+
method: "POST",
|
|
73
|
+
path: CONSENT_PATH,
|
|
74
|
+
access: ["user"],
|
|
75
|
+
handle: async (request) => {
|
|
76
|
+
if (request.headers.get("origin") !== settings.origin) {
|
|
77
|
+
return failure("This answer did not come from the consent page.", 403);
|
|
78
|
+
}
|
|
79
|
+
const form = new URLSearchParams(await request.text());
|
|
80
|
+
const oauthQuery = form.get("oauth_query");
|
|
81
|
+
const accept = form.get("accept");
|
|
82
|
+
if (!oauthQuery || (accept !== "true" && accept !== "false")) {
|
|
83
|
+
return failure("This answer is incomplete.", 400);
|
|
84
|
+
}
|
|
85
|
+
const headers = new Headers({
|
|
86
|
+
"content-type": "application/json",
|
|
87
|
+
accept: "application/json",
|
|
88
|
+
origin: settings.origin,
|
|
89
|
+
});
|
|
90
|
+
const cookie = request.headers.get("cookie");
|
|
91
|
+
if (cookie)
|
|
92
|
+
headers.set("cookie", cookie);
|
|
93
|
+
const answer = await instance.handler(new Request(`${settings.baseURL}/oauth2/consent`, {
|
|
94
|
+
method: "POST",
|
|
95
|
+
headers,
|
|
96
|
+
body: JSON.stringify({ accept: accept === "true", oauth_query: oauthQuery }),
|
|
97
|
+
}));
|
|
98
|
+
const result = (await answer.json().catch(() => undefined));
|
|
99
|
+
const location = result?.url ?? result?.redirect_uri;
|
|
100
|
+
if (!answer.ok || !location) {
|
|
101
|
+
return failure(result?.message ?? "The authorization could not be completed.", 400);
|
|
102
|
+
}
|
|
103
|
+
return new Response(null, { status: 303, headers: { location } });
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
return [
|
|
107
|
+
forward("GET", "/**"),
|
|
108
|
+
forward("POST", "/**"),
|
|
109
|
+
forward("GET", "/.well-known/oauth-protected-resource"),
|
|
110
|
+
forward("GET", "/.well-known/oauth-protected-resource/**"),
|
|
111
|
+
forward("GET", "/.well-known/oauth-authorization-server/**"),
|
|
112
|
+
consentPage,
|
|
113
|
+
consentAnswer,
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.ts"],"names":[],"mappings":"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"}
|