@draftlab/auth 0.15.1 → 0.16.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/esm/allow.js +26 -0
- package/dist/esm/client.js +254 -0
- package/dist/esm/core.js +597 -0
- package/dist/esm/css.d.js +0 -0
- package/dist/esm/error.js +88 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/keys.js +126 -0
- package/dist/esm/mutex.js +53 -0
- package/dist/esm/pkce.js +87 -0
- package/dist/esm/provider/apple.js +15 -0
- package/dist/esm/provider/code.js +62 -0
- package/dist/esm/provider/discord.js +15 -0
- package/dist/esm/provider/facebook.js +15 -0
- package/dist/esm/provider/github.js +15 -0
- package/dist/esm/provider/gitlab.js +15 -0
- package/dist/esm/provider/google.js +16 -0
- package/dist/esm/provider/linkedin.js +15 -0
- package/dist/esm/provider/magiclink.js +83 -0
- package/dist/esm/provider/microsoft.js +15 -0
- package/dist/esm/provider/oauth2.js +130 -0
- package/dist/esm/provider/password.js +331 -0
- package/dist/esm/provider/provider.js +18 -0
- package/dist/esm/provider/reddit.js +15 -0
- package/dist/esm/provider/slack.js +15 -0
- package/dist/esm/provider/spotify.js +15 -0
- package/dist/esm/provider/twitch.js +15 -0
- package/dist/esm/provider/vercel.js +17 -0
- package/dist/esm/random.js +40 -0
- package/dist/esm/revocation.js +27 -0
- package/dist/esm/storage/memory.js +110 -0
- package/dist/esm/storage/storage.js +56 -0
- package/dist/esm/storage/turso.js +93 -0
- package/dist/esm/storage/unstorage.js +78 -0
- package/dist/esm/subject.js +7 -0
- package/dist/esm/themes/theme.js +115 -0
- package/dist/esm/toolkit/client.js +119 -0
- package/dist/esm/toolkit/index.js +25 -0
- package/dist/esm/toolkit/providers/facebook.js +11 -0
- package/dist/esm/toolkit/providers/github.js +11 -0
- package/dist/esm/toolkit/providers/google.js +11 -0
- package/dist/esm/toolkit/providers/strategy.js +0 -0
- package/dist/esm/toolkit/storage.js +81 -0
- package/dist/esm/toolkit/utils.js +18 -0
- package/dist/esm/types.js +0 -0
- package/dist/esm/ui/base.js +478 -0
- package/dist/esm/ui/code.js +186 -0
- package/dist/esm/ui/form.js +46 -0
- package/dist/esm/ui/icon.js +242 -0
- package/dist/esm/ui/magiclink.js +158 -0
- package/dist/esm/ui/password.js +435 -0
- package/dist/esm/ui/select.js +102 -0
- package/dist/esm/util.js +59 -0
- package/dist/{allow.d.mts → types/allow.d.ts} +9 -11
- package/dist/types/allow.d.ts.map +1 -0
- package/dist/types/client.d.ts +462 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/core.d.ts +113 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/{error.d.mts → types/error.d.ts} +95 -97
- package/dist/types/error.d.ts.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/{keys.d.mts → types/keys.d.ts} +20 -23
- package/dist/types/keys.d.ts.map +1 -0
- package/dist/types/mutex.d.ts +42 -0
- package/dist/types/mutex.d.ts.map +1 -0
- package/dist/{pkce.d.mts → types/pkce.d.ts} +10 -11
- package/dist/types/pkce.d.ts.map +1 -0
- package/dist/types/provider/apple.d.ts +197 -0
- package/dist/types/provider/apple.d.ts.map +1 -0
- package/dist/types/provider/code.d.ts +288 -0
- package/dist/types/provider/code.d.ts.map +1 -0
- package/dist/types/provider/discord.d.ts +206 -0
- package/dist/types/provider/discord.d.ts.map +1 -0
- package/dist/types/provider/facebook.d.ts +200 -0
- package/dist/types/provider/facebook.d.ts.map +1 -0
- package/dist/types/provider/github.d.ts +220 -0
- package/dist/types/provider/github.d.ts.map +1 -0
- package/dist/types/provider/gitlab.d.ts +180 -0
- package/dist/types/provider/gitlab.d.ts.map +1 -0
- package/dist/types/provider/google.d.ts +158 -0
- package/dist/types/provider/google.d.ts.map +1 -0
- package/dist/types/provider/linkedin.d.ts +190 -0
- package/dist/types/provider/linkedin.d.ts.map +1 -0
- package/dist/types/provider/magiclink.d.ts +141 -0
- package/dist/types/provider/magiclink.d.ts.map +1 -0
- package/dist/types/provider/microsoft.d.ts +247 -0
- package/dist/types/provider/microsoft.d.ts.map +1 -0
- package/dist/types/provider/oauth2.d.ts +229 -0
- package/dist/types/provider/oauth2.d.ts.map +1 -0
- package/dist/types/provider/password.d.ts +408 -0
- package/dist/types/provider/password.d.ts.map +1 -0
- package/dist/types/provider/provider.d.ts +226 -0
- package/dist/types/provider/provider.d.ts.map +1 -0
- package/dist/types/provider/reddit.d.ts +159 -0
- package/dist/types/provider/reddit.d.ts.map +1 -0
- package/dist/types/provider/slack.d.ts +171 -0
- package/dist/types/provider/slack.d.ts.map +1 -0
- package/dist/types/provider/spotify.d.ts +168 -0
- package/dist/types/provider/spotify.d.ts.map +1 -0
- package/dist/types/provider/twitch.d.ts +163 -0
- package/dist/types/provider/twitch.d.ts.map +1 -0
- package/dist/types/provider/vercel.d.ts +294 -0
- package/dist/types/provider/vercel.d.ts.map +1 -0
- package/dist/{random.d.mts → types/random.d.ts} +4 -6
- package/dist/types/random.d.ts.map +1 -0
- package/dist/types/revocation.d.ts +76 -0
- package/dist/types/revocation.d.ts.map +1 -0
- package/dist/{storage/memory.d.mts → types/storage/memory.d.ts} +17 -20
- package/dist/types/storage/memory.d.ts.map +1 -0
- package/dist/types/storage/storage.d.ts +177 -0
- package/dist/types/storage/storage.d.ts.map +1 -0
- package/dist/{storage/turso.d.mts → types/storage/turso.d.ts} +4 -7
- package/dist/types/storage/turso.d.ts.map +1 -0
- package/dist/{storage/unstorage.d.mts → types/storage/unstorage.d.ts} +12 -10
- package/dist/types/storage/unstorage.d.ts.map +1 -0
- package/dist/types/subject.d.ts +115 -0
- package/dist/types/subject.d.ts.map +1 -0
- package/dist/types/themes/theme.d.ts +207 -0
- package/dist/types/themes/theme.d.ts.map +1 -0
- package/dist/types/toolkit/client.d.ts +235 -0
- package/dist/types/toolkit/client.d.ts.map +1 -0
- package/dist/types/toolkit/index.d.ts +45 -0
- package/dist/types/toolkit/index.d.ts.map +1 -0
- package/dist/types/toolkit/providers/facebook.d.ts +8 -0
- package/dist/types/toolkit/providers/facebook.d.ts.map +1 -0
- package/dist/types/toolkit/providers/github.d.ts +8 -0
- package/dist/types/toolkit/providers/github.d.ts.map +1 -0
- package/dist/types/toolkit/providers/google.d.ts +8 -0
- package/dist/types/toolkit/providers/google.d.ts.map +1 -0
- package/dist/types/toolkit/providers/strategy.d.ts +38 -0
- package/dist/types/toolkit/providers/strategy.d.ts.map +1 -0
- package/dist/{toolkit/storage.d.mts → types/toolkit/storage.d.ts} +37 -39
- package/dist/types/toolkit/storage.d.ts.map +1 -0
- package/dist/{toolkit/utils.d.mts → types/toolkit/utils.d.ts} +2 -4
- package/dist/types/toolkit/utils.d.ts.map +1 -0
- package/dist/types/types.d.ts +92 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/ui/base.d.ts +18 -0
- package/dist/types/ui/base.d.ts.map +1 -0
- package/dist/types/ui/code.d.ts +43 -0
- package/dist/types/ui/code.d.ts.map +1 -0
- package/dist/types/ui/form.d.ts +24 -0
- package/dist/types/ui/form.d.ts.map +1 -0
- package/dist/types/ui/icon.d.ts +60 -0
- package/dist/types/ui/icon.d.ts.map +1 -0
- package/dist/types/ui/magiclink.d.ts +41 -0
- package/dist/types/ui/magiclink.d.ts.map +1 -0
- package/dist/types/ui/password.d.ts +43 -0
- package/dist/types/ui/password.d.ts.map +1 -0
- package/dist/types/ui/select.d.ts +33 -0
- package/dist/types/ui/select.d.ts.map +1 -0
- package/dist/{util.d.mts → types/util.d.ts} +11 -12
- package/dist/types/util.d.ts.map +1 -0
- package/package.json +10 -16
- package/dist/adapters/node.d.mts +0 -17
- package/dist/adapters/node.mjs +0 -69
- package/dist/allow.mjs +0 -63
- package/dist/client.d.mts +0 -462
- package/dist/client.mjs +0 -284
- package/dist/core.d.mts +0 -109
- package/dist/core.mjs +0 -595
- package/dist/error.mjs +0 -237
- package/dist/index.d.mts +0 -2
- package/dist/index.mjs +0 -3
- package/dist/keys.mjs +0 -146
- package/dist/mutex.d.mts +0 -44
- package/dist/mutex.mjs +0 -110
- package/dist/pkce.mjs +0 -157
- package/dist/provider/apple.d.mts +0 -110
- package/dist/provider/apple.mjs +0 -164
- package/dist/provider/code.d.mts +0 -218
- package/dist/provider/code.mjs +0 -246
- package/dist/provider/discord.d.mts +0 -145
- package/dist/provider/discord.mjs +0 -156
- package/dist/provider/facebook.d.mts +0 -141
- package/dist/provider/facebook.mjs +0 -150
- package/dist/provider/github.d.mts +0 -139
- package/dist/provider/github.mjs +0 -169
- package/dist/provider/gitlab.d.mts +0 -105
- package/dist/provider/gitlab.mjs +0 -147
- package/dist/provider/google.d.mts +0 -111
- package/dist/provider/google.mjs +0 -109
- package/dist/provider/linkedin.d.mts +0 -131
- package/dist/provider/linkedin.mjs +0 -142
- package/dist/provider/magiclink.d.mts +0 -79
- package/dist/provider/magiclink.mjs +0 -143
- package/dist/provider/microsoft.d.mts +0 -177
- package/dist/provider/microsoft.mjs +0 -177
- package/dist/provider/oauth2.d.mts +0 -175
- package/dist/provider/oauth2.mjs +0 -222
- package/dist/provider/passkey.d.mts +0 -103
- package/dist/provider/passkey.mjs +0 -320
- package/dist/provider/password.d.mts +0 -384
- package/dist/provider/password.mjs +0 -363
- package/dist/provider/provider.d.mts +0 -225
- package/dist/provider/provider.mjs +0 -44
- package/dist/provider/reddit.d.mts +0 -106
- package/dist/provider/reddit.mjs +0 -127
- package/dist/provider/slack.d.mts +0 -113
- package/dist/provider/slack.mjs +0 -138
- package/dist/provider/spotify.d.mts +0 -112
- package/dist/provider/spotify.mjs +0 -135
- package/dist/provider/totp.d.mts +0 -111
- package/dist/provider/totp.mjs +0 -191
- package/dist/provider/twitch.d.mts +0 -107
- package/dist/provider/twitch.mjs +0 -131
- package/dist/provider/vercel.d.mts +0 -176
- package/dist/provider/vercel.mjs +0 -230
- package/dist/random.mjs +0 -86
- package/dist/revocation.d.mts +0 -54
- package/dist/revocation.mjs +0 -63
- package/dist/router/context.d.mts +0 -21
- package/dist/router/context.mjs +0 -193
- package/dist/router/cookies.d.mts +0 -8
- package/dist/router/cookies.mjs +0 -13
- package/dist/router/index.d.mts +0 -21
- package/dist/router/index.mjs +0 -107
- package/dist/router/matcher.d.mts +0 -15
- package/dist/router/matcher.mjs +0 -76
- package/dist/router/middleware/cors.d.mts +0 -15
- package/dist/router/middleware/cors.mjs +0 -114
- package/dist/router/safe-request.d.mts +0 -52
- package/dist/router/safe-request.mjs +0 -160
- package/dist/router/types.d.mts +0 -67
- package/dist/router/types.mjs +0 -1
- package/dist/router/variables.d.mts +0 -12
- package/dist/router/variables.mjs +0 -20
- package/dist/storage/memory.mjs +0 -125
- package/dist/storage/storage.d.mts +0 -179
- package/dist/storage/storage.mjs +0 -104
- package/dist/storage/turso.mjs +0 -117
- package/dist/storage/unstorage.mjs +0 -103
- package/dist/subject.d.mts +0 -61
- package/dist/subject.mjs +0 -36
- package/dist/themes/theme.d.mts +0 -209
- package/dist/themes/theme.mjs +0 -120
- package/dist/toolkit/client.d.mts +0 -168
- package/dist/toolkit/client.mjs +0 -209
- package/dist/toolkit/index.d.mts +0 -9
- package/dist/toolkit/index.mjs +0 -9
- package/dist/toolkit/providers/facebook.d.mts +0 -11
- package/dist/toolkit/providers/facebook.mjs +0 -16
- package/dist/toolkit/providers/github.d.mts +0 -11
- package/dist/toolkit/providers/github.mjs +0 -16
- package/dist/toolkit/providers/google.d.mts +0 -11
- package/dist/toolkit/providers/google.mjs +0 -20
- package/dist/toolkit/providers/strategy.d.mts +0 -40
- package/dist/toolkit/providers/strategy.mjs +0 -1
- package/dist/toolkit/storage.mjs +0 -157
- package/dist/toolkit/utils.mjs +0 -30
- package/dist/types.d.mts +0 -94
- package/dist/types.mjs +0 -1
- package/dist/ui/base.d.mts +0 -29
- package/dist/ui/base.mjs +0 -407
- package/dist/ui/code.d.mts +0 -42
- package/dist/ui/code.mjs +0 -173
- package/dist/ui/form.d.mts +0 -31
- package/dist/ui/form.mjs +0 -49
- package/dist/ui/icon.d.mts +0 -57
- package/dist/ui/icon.mjs +0 -247
- package/dist/ui/magiclink.d.mts +0 -40
- package/dist/ui/magiclink.mjs +0 -152
- package/dist/ui/passkey.d.mts +0 -26
- package/dist/ui/passkey.mjs +0 -323
- package/dist/ui/password.d.mts +0 -41
- package/dist/ui/password.mjs +0 -402
- package/dist/ui/select.d.mts +0 -33
- package/dist/ui/select.mjs +0 -98
- package/dist/ui/totp.d.mts +0 -33
- package/dist/ui/totp.mjs +0 -270
- package/dist/util.mjs +0 -128
package/dist/esm/core.js
ADDED
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
// src/core.ts
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import { deleteCookie, getCookie, setCookie } from "hono/cookie";
|
|
4
|
+
import { cors } from "hono/cors";
|
|
5
|
+
import { CompactEncrypt, compactDecrypt, SignJWT } from "jose";
|
|
6
|
+
import { defaultAllowCheck } from "./allow";
|
|
7
|
+
import {
|
|
8
|
+
MissingParameterError,
|
|
9
|
+
OauthError,
|
|
10
|
+
UnauthorizedClientError,
|
|
11
|
+
UnknownStateError
|
|
12
|
+
} from "./error";
|
|
13
|
+
import { encryptionKeys, signingKeys } from "./keys";
|
|
14
|
+
import { validatePKCE } from "./pkce";
|
|
15
|
+
import { generateSecureToken } from "./random";
|
|
16
|
+
import { Revocation } from "./revocation";
|
|
17
|
+
import { Storage } from "./storage/storage";
|
|
18
|
+
import { setTheme } from "./themes/theme";
|
|
19
|
+
import { Select } from "./ui/select";
|
|
20
|
+
import { getRelativeUrl, lazy } from "./util";
|
|
21
|
+
var normalizeTimingAsync = async (fn, minTimeMs = 100) => {
|
|
22
|
+
const startTime = performance.now();
|
|
23
|
+
const result = await fn();
|
|
24
|
+
const elapsed = performance.now() - startTime;
|
|
25
|
+
const remainingTime = Math.max(0, minTimeMs - elapsed);
|
|
26
|
+
if (remainingTime > 0) {
|
|
27
|
+
const jitterBuffer = new Uint32Array(1);
|
|
28
|
+
crypto.getRandomValues(jitterBuffer);
|
|
29
|
+
const jitter = (jitterBuffer[0] ?? 0) / 4294967295 * 20;
|
|
30
|
+
const totalDelay = remainingTime + jitter;
|
|
31
|
+
await new Promise((resolve) => setTimeout(resolve, totalDelay));
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
var isHttpsRequest = (c) => {
|
|
36
|
+
return c.req.header("x-forwarded-proto") === "https" || c.req.header("x-forwarded-ssl") === "on" || c.req.url.startsWith("https://");
|
|
37
|
+
};
|
|
38
|
+
var issuer = (input) => {
|
|
39
|
+
const error = input.error ?? ((err) => {
|
|
40
|
+
return new Response(err.message, {
|
|
41
|
+
status: 400,
|
|
42
|
+
headers: { "Content-Type": "text/plain" }
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
const ttlAccess = input.ttl?.access ?? 60 * 60 * 24 * 30;
|
|
46
|
+
const ttlRefresh = input.ttl?.refresh ?? 60 * 60 * 24 * 365;
|
|
47
|
+
const ttlRefreshReuse = input.ttl?.reuse ?? 60;
|
|
48
|
+
const ttlRefreshRetention = input.ttl?.retention ?? 0;
|
|
49
|
+
if (input.theme) {
|
|
50
|
+
setTheme(input.theme);
|
|
51
|
+
}
|
|
52
|
+
const storage = input.storage;
|
|
53
|
+
const select = lazy(() => input.select ?? Select());
|
|
54
|
+
const allSigning = lazy(() => signingKeys(storage));
|
|
55
|
+
const allEncryption = lazy(() => encryptionKeys(storage));
|
|
56
|
+
const allow = lazy(() => input.allow ?? defaultAllowCheck);
|
|
57
|
+
const signingKey = lazy(() => allSigning().then((all) => all[0]));
|
|
58
|
+
const encryptionKey = lazy(() => allEncryption().then((all) => all[0]));
|
|
59
|
+
const cookiePath = input.basePath || "/";
|
|
60
|
+
const issuerUrl = (c) => {
|
|
61
|
+
const baseUrl = new URL(getRelativeUrl(c, "/"));
|
|
62
|
+
if (input.basePath) {
|
|
63
|
+
const normalizedBasePath = input.basePath.startsWith("/") ? input.basePath : `/${input.basePath}`;
|
|
64
|
+
baseUrl.pathname = normalizedBasePath.replace(/\/$/, "");
|
|
65
|
+
return baseUrl.href;
|
|
66
|
+
}
|
|
67
|
+
return baseUrl.origin;
|
|
68
|
+
};
|
|
69
|
+
const encrypt = async (value) => {
|
|
70
|
+
const key = await encryptionKey();
|
|
71
|
+
if (!key) {
|
|
72
|
+
throw new Error("Encryption key not available");
|
|
73
|
+
}
|
|
74
|
+
return await new CompactEncrypt(new TextEncoder().encode(JSON.stringify(value))).setProtectedHeader({ alg: "RSA-OAEP-512", enc: "A256GCM" }).encrypt(key.public);
|
|
75
|
+
};
|
|
76
|
+
const decrypt = async (value) => {
|
|
77
|
+
const key = await encryptionKey();
|
|
78
|
+
if (!key) {
|
|
79
|
+
throw new Error("Encryption key not available");
|
|
80
|
+
}
|
|
81
|
+
return JSON.parse(new TextDecoder().decode(await compactDecrypt(value, key.private).then((result) => result.plaintext)));
|
|
82
|
+
};
|
|
83
|
+
const resolveSubject = async (type, properties) => {
|
|
84
|
+
const jsonString = JSON.stringify(properties);
|
|
85
|
+
const encoder = new TextEncoder;
|
|
86
|
+
const data = encoder.encode(jsonString);
|
|
87
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
88
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
89
|
+
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
90
|
+
return `${type}:${hashHex.slice(0, 16)}`;
|
|
91
|
+
};
|
|
92
|
+
const generateTokens = async (c, value, opts) => {
|
|
93
|
+
const refreshToken = value.nextToken ?? generateSecureToken();
|
|
94
|
+
if (opts?.generateRefreshToken ?? true) {
|
|
95
|
+
const refreshPayload = {
|
|
96
|
+
type: value.type,
|
|
97
|
+
properties: value.properties,
|
|
98
|
+
clientID: value.clientID,
|
|
99
|
+
subject: value.subject,
|
|
100
|
+
ttl: value.ttl,
|
|
101
|
+
nextToken: generateSecureToken(),
|
|
102
|
+
timeUsed: value.timeUsed
|
|
103
|
+
};
|
|
104
|
+
const refreshKey = ["oauth:refresh", value.subject, refreshToken];
|
|
105
|
+
await Storage.set(storage, refreshKey, refreshPayload, value.ttl.refresh);
|
|
106
|
+
}
|
|
107
|
+
const signingKeyData = await signingKey();
|
|
108
|
+
if (!signingKeyData) {
|
|
109
|
+
throw new Error("Signing key not available");
|
|
110
|
+
}
|
|
111
|
+
const now = Math.floor(Date.now() / 1000);
|
|
112
|
+
if (!value.clientID.trim()) {
|
|
113
|
+
throw new Error("Invalid audience: client ID cannot be empty");
|
|
114
|
+
}
|
|
115
|
+
const accessPayload = {
|
|
116
|
+
type: value.type,
|
|
117
|
+
properties: value.properties,
|
|
118
|
+
sub: value.subject,
|
|
119
|
+
aud: value.clientID,
|
|
120
|
+
iss: issuerUrl(c),
|
|
121
|
+
exp: now + value.ttl.access,
|
|
122
|
+
iat: now,
|
|
123
|
+
mode: "access"
|
|
124
|
+
};
|
|
125
|
+
const access = await new SignJWT(accessPayload).setExpirationTime(Math.floor(now + value.ttl.access)).setProtectedHeader({
|
|
126
|
+
alg: signingKeyData.alg,
|
|
127
|
+
kid: signingKeyData.id,
|
|
128
|
+
typ: "JWT"
|
|
129
|
+
}).sign(signingKeyData.private);
|
|
130
|
+
return {
|
|
131
|
+
access,
|
|
132
|
+
refresh: [value.subject, refreshToken].join(":"),
|
|
133
|
+
expiresIn: Math.floor(now + value.ttl.access - Date.now() / 1000)
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
const getAuthorization = async (c) => {
|
|
137
|
+
const match = await auth.get(c, "authorization") || c.get("authorization");
|
|
138
|
+
if (!match) {
|
|
139
|
+
throw new UnknownStateError;
|
|
140
|
+
}
|
|
141
|
+
return match;
|
|
142
|
+
};
|
|
143
|
+
const auth = {
|
|
144
|
+
async success(c, properties, successOpts) {
|
|
145
|
+
const authorization = await getAuthorization(c);
|
|
146
|
+
const currentProvider = c.get("provider") || "unknown";
|
|
147
|
+
if (!authorization.client_id) {
|
|
148
|
+
throw new Error("client_id is required");
|
|
149
|
+
}
|
|
150
|
+
return await input.success({
|
|
151
|
+
async subject(type, properties2, subjectOpts) {
|
|
152
|
+
const subject = subjectOpts?.subject ?? await resolveSubject(type, properties2);
|
|
153
|
+
await successOpts?.invalidate?.(await resolveSubject(type, properties2));
|
|
154
|
+
if (authorization.response_type === "token") {
|
|
155
|
+
if (!authorization.redirect_uri) {
|
|
156
|
+
throw new Error("redirect_uri is required");
|
|
157
|
+
}
|
|
158
|
+
const location2 = new URL(authorization.redirect_uri);
|
|
159
|
+
if (!authorization.client_id) {
|
|
160
|
+
throw new Error("client_id is required");
|
|
161
|
+
}
|
|
162
|
+
const tokens = await generateTokens(c, {
|
|
163
|
+
type,
|
|
164
|
+
subject,
|
|
165
|
+
properties: properties2,
|
|
166
|
+
clientID: authorization.client_id,
|
|
167
|
+
ttl: {
|
|
168
|
+
access: subjectOpts?.ttl?.access ?? ttlAccess,
|
|
169
|
+
refresh: subjectOpts?.ttl?.refresh ?? ttlRefresh
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
location2.hash = new URLSearchParams({
|
|
173
|
+
access_token: tokens.access,
|
|
174
|
+
token_type: "Bearer",
|
|
175
|
+
expires_in: tokens.expiresIn.toString(),
|
|
176
|
+
...authorization.state && { state: authorization.state }
|
|
177
|
+
}).toString();
|
|
178
|
+
await auth.unset(c, "authorization");
|
|
179
|
+
return c.redirect(location2.toString(), 302);
|
|
180
|
+
}
|
|
181
|
+
if (!authorization.redirect_uri) {
|
|
182
|
+
throw new Error("redirect_uri is required");
|
|
183
|
+
}
|
|
184
|
+
if (!authorization.client_id) {
|
|
185
|
+
throw new Error("client_id is required");
|
|
186
|
+
}
|
|
187
|
+
const code = generateSecureToken();
|
|
188
|
+
const codePayload = {
|
|
189
|
+
type,
|
|
190
|
+
properties: properties2,
|
|
191
|
+
subject,
|
|
192
|
+
redirectURI: authorization.redirect_uri,
|
|
193
|
+
clientID: authorization.client_id,
|
|
194
|
+
scopes: authorization.scopes,
|
|
195
|
+
pkce: authorization.pkce,
|
|
196
|
+
ttl: {
|
|
197
|
+
access: subjectOpts?.ttl?.access ?? ttlAccess,
|
|
198
|
+
refresh: subjectOpts?.ttl?.refresh ?? ttlRefresh
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
await Storage.set(storage, ["oauth:code", code], codePayload, 60);
|
|
202
|
+
if (!authorization.redirect_uri) {
|
|
203
|
+
throw new Error("redirect_uri is required");
|
|
204
|
+
}
|
|
205
|
+
const location = new URL(authorization.redirect_uri);
|
|
206
|
+
location.searchParams.set("code", code);
|
|
207
|
+
if (authorization.state) {
|
|
208
|
+
location.searchParams.set("state", authorization.state);
|
|
209
|
+
}
|
|
210
|
+
await auth.unset(c, "authorization");
|
|
211
|
+
return c.redirect(location.toString(), 302);
|
|
212
|
+
}
|
|
213
|
+
}, {
|
|
214
|
+
provider: currentProvider,
|
|
215
|
+
...properties && typeof properties === "object" ? properties : {}
|
|
216
|
+
}, c.req.raw, authorization.client_id);
|
|
217
|
+
},
|
|
218
|
+
forward(c, response) {
|
|
219
|
+
return c.newResponse(response.body, response);
|
|
220
|
+
},
|
|
221
|
+
async set(c, key, maxAge, value) {
|
|
222
|
+
const isHttps = isHttpsRequest(c);
|
|
223
|
+
const encryptedValue = await encrypt(value);
|
|
224
|
+
setCookie(c, key, encryptedValue, {
|
|
225
|
+
maxAge,
|
|
226
|
+
httpOnly: true,
|
|
227
|
+
secure: isHttps,
|
|
228
|
+
sameSite: isHttps ? "None" : "Lax",
|
|
229
|
+
path: cookiePath
|
|
230
|
+
});
|
|
231
|
+
},
|
|
232
|
+
async get(c, key) {
|
|
233
|
+
const raw = getCookie(c, key);
|
|
234
|
+
if (!raw) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
const decrypted = await decrypt(raw);
|
|
239
|
+
return decrypted;
|
|
240
|
+
} catch {
|
|
241
|
+
deleteCookie(c, key, {
|
|
242
|
+
path: cookiePath
|
|
243
|
+
});
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
async unset(c, key) {
|
|
248
|
+
deleteCookie(c, key, {
|
|
249
|
+
path: cookiePath
|
|
250
|
+
});
|
|
251
|
+
},
|
|
252
|
+
async invalidate(subject) {
|
|
253
|
+
for await (const [key] of Storage.scan(storage, ["oauth:refresh", subject])) {
|
|
254
|
+
await Storage.remove(storage, key);
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
storage
|
|
258
|
+
};
|
|
259
|
+
const app = new Hono({
|
|
260
|
+
strict: false
|
|
261
|
+
}).basePath(input.basePath || "/");
|
|
262
|
+
for (const [name, value] of Object.entries(input.providers)) {
|
|
263
|
+
const route = new Hono;
|
|
264
|
+
route.use(async (c, next) => {
|
|
265
|
+
c.set("provider", name);
|
|
266
|
+
await next();
|
|
267
|
+
});
|
|
268
|
+
value.init(route, {
|
|
269
|
+
name,
|
|
270
|
+
...auth
|
|
271
|
+
});
|
|
272
|
+
app.route(`/${name}`, route);
|
|
273
|
+
}
|
|
274
|
+
app.get("/.well-known/jwks.json", cors({
|
|
275
|
+
origin: "*",
|
|
276
|
+
allowHeaders: ["*"],
|
|
277
|
+
allowMethods: ["GET"],
|
|
278
|
+
credentials: false
|
|
279
|
+
}), async (c) => {
|
|
280
|
+
const signingKeysData = await allSigning();
|
|
281
|
+
const jwksDocument = {
|
|
282
|
+
keys: signingKeysData.map((keyInfo) => ({
|
|
283
|
+
...keyInfo.jwk,
|
|
284
|
+
alg: keyInfo.alg,
|
|
285
|
+
exp: keyInfo.expired ? Math.floor(keyInfo.expired.getTime() / 1000) : undefined
|
|
286
|
+
}))
|
|
287
|
+
};
|
|
288
|
+
return c.json(jwksDocument);
|
|
289
|
+
});
|
|
290
|
+
app.get("/.well-known/oauth-authorization-server", cors({
|
|
291
|
+
origin: "*",
|
|
292
|
+
allowHeaders: ["*"],
|
|
293
|
+
allowMethods: ["GET"],
|
|
294
|
+
credentials: false
|
|
295
|
+
}), (c) => {
|
|
296
|
+
const iss = issuerUrl(c);
|
|
297
|
+
const oauth2Document = {
|
|
298
|
+
issuer: iss,
|
|
299
|
+
authorization_endpoint: `${iss}/authorize`,
|
|
300
|
+
token_endpoint: `${iss}/token`,
|
|
301
|
+
jwks_uri: `${iss}/.well-known/jwks.json`,
|
|
302
|
+
response_types_supported: ["code", "token"]
|
|
303
|
+
};
|
|
304
|
+
return c.json(oauth2Document);
|
|
305
|
+
});
|
|
306
|
+
app.post("/token", cors({
|
|
307
|
+
origin: "*",
|
|
308
|
+
allowHeaders: ["*"],
|
|
309
|
+
allowMethods: ["POST"],
|
|
310
|
+
credentials: false
|
|
311
|
+
}), async (c) => {
|
|
312
|
+
const form = await c.req.formData();
|
|
313
|
+
const grantType = form.get("grant_type");
|
|
314
|
+
if (grantType === "authorization_code") {
|
|
315
|
+
const code = form.get("code");
|
|
316
|
+
if (!code) {
|
|
317
|
+
const err = new OauthError("invalid_request", "Missing code");
|
|
318
|
+
return c.json(err.toJSON(), 400);
|
|
319
|
+
}
|
|
320
|
+
const key = ["oauth:code", code.toString()];
|
|
321
|
+
const { isValid, payload } = await normalizeTimingAsync(async () => {
|
|
322
|
+
const data = await Storage.get(storage, key);
|
|
323
|
+
const redirectUri = form.get("redirect_uri");
|
|
324
|
+
const clientId = form.get("client_id");
|
|
325
|
+
const valid = !!(data && data.redirectURI === redirectUri && data.clientID === clientId);
|
|
326
|
+
return {
|
|
327
|
+
isValid: valid,
|
|
328
|
+
payload: valid ? data : undefined
|
|
329
|
+
};
|
|
330
|
+
});
|
|
331
|
+
if (!isValid || !payload) {
|
|
332
|
+
const err = new OauthError("invalid_grant", "Authorization code has been used or expired");
|
|
333
|
+
return c.json(err.toJSON(), 400);
|
|
334
|
+
}
|
|
335
|
+
if (payload.pkce) {
|
|
336
|
+
const codeVerifier = form.get("code_verifier")?.toString();
|
|
337
|
+
if (!codeVerifier) {
|
|
338
|
+
const err = new OauthError("invalid_grant", "Missing code_verifier");
|
|
339
|
+
return c.json(err.toJSON(), 400);
|
|
340
|
+
}
|
|
341
|
+
if (!await validatePKCE(codeVerifier, payload.pkce.challenge, payload.pkce.method)) {
|
|
342
|
+
const err = new OauthError("invalid_grant", "Code verifier does not match");
|
|
343
|
+
return c.json(err.toJSON(), 400);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
const tokens = await generateTokens(c, payload);
|
|
347
|
+
await Storage.remove(storage, key);
|
|
348
|
+
const response = {
|
|
349
|
+
access_token: tokens.access,
|
|
350
|
+
token_type: "Bearer",
|
|
351
|
+
expires_in: tokens.expiresIn,
|
|
352
|
+
refresh_token: tokens.refresh
|
|
353
|
+
};
|
|
354
|
+
return c.json(response);
|
|
355
|
+
}
|
|
356
|
+
if (grantType === "refresh_token") {
|
|
357
|
+
const refreshToken = form.get("refresh_token");
|
|
358
|
+
if (!refreshToken) {
|
|
359
|
+
const err = new OauthError("invalid_request", "Missing refresh_token");
|
|
360
|
+
return c.json(err.toJSON(), 400);
|
|
361
|
+
}
|
|
362
|
+
const refreshTokenStr = refreshToken.toString();
|
|
363
|
+
const isRevoked = await Revocation.isRevoked(storage, refreshTokenStr);
|
|
364
|
+
if (isRevoked) {
|
|
365
|
+
const err = new OauthError("invalid_grant", "Refresh token has been revoked");
|
|
366
|
+
return c.json(err.toJSON(), 400);
|
|
367
|
+
}
|
|
368
|
+
const splits = refreshTokenStr.split(":");
|
|
369
|
+
const token = splits.pop();
|
|
370
|
+
if (!token) {
|
|
371
|
+
throw new Error("Invalid refresh token format");
|
|
372
|
+
}
|
|
373
|
+
const subject = splits.join(":");
|
|
374
|
+
const key = ["oauth:refresh", subject, token];
|
|
375
|
+
const payload = await Storage.get(storage, key);
|
|
376
|
+
if (!payload) {
|
|
377
|
+
const err = new OauthError("invalid_grant", "Refresh token has been used or expired");
|
|
378
|
+
return c.json(err.toJSON(), 400);
|
|
379
|
+
}
|
|
380
|
+
if (input.refresh) {
|
|
381
|
+
try {
|
|
382
|
+
const refreshResult = await input.refresh({
|
|
383
|
+
type: payload.type,
|
|
384
|
+
properties: payload.properties,
|
|
385
|
+
subject: payload.subject,
|
|
386
|
+
clientID: payload.clientID,
|
|
387
|
+
scopes: payload.scopes
|
|
388
|
+
}, c.req.raw);
|
|
389
|
+
if (!refreshResult) {
|
|
390
|
+
await auth.invalidate(subject);
|
|
391
|
+
return c.json({
|
|
392
|
+
error: "invalid_grant",
|
|
393
|
+
error_description: "Refresh token is no longer valid"
|
|
394
|
+
}, 400);
|
|
395
|
+
}
|
|
396
|
+
payload.type = refreshResult.type;
|
|
397
|
+
payload.properties = refreshResult.properties;
|
|
398
|
+
if (refreshResult.subject) {
|
|
399
|
+
payload.subject = refreshResult.subject;
|
|
400
|
+
}
|
|
401
|
+
if (refreshResult.scopes) {
|
|
402
|
+
payload.scopes = refreshResult.scopes;
|
|
403
|
+
}
|
|
404
|
+
} catch {
|
|
405
|
+
return c.json({
|
|
406
|
+
error: "server_error",
|
|
407
|
+
error_description: "Internal server error during token refresh"
|
|
408
|
+
}, 500);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
const generateRefreshToken = !payload.timeUsed;
|
|
412
|
+
if (ttlRefreshReuse <= 0) {
|
|
413
|
+
await Storage.remove(storage, key);
|
|
414
|
+
} else if (!payload.timeUsed) {
|
|
415
|
+
payload.timeUsed = Date.now();
|
|
416
|
+
await Storage.set(storage, key, payload, ttlRefreshReuse + ttlRefreshRetention);
|
|
417
|
+
} else if (Date.now() > payload.timeUsed + ttlRefreshReuse * 1000) {
|
|
418
|
+
await auth.invalidate(subject);
|
|
419
|
+
return c.json({
|
|
420
|
+
error: "invalid_grant",
|
|
421
|
+
error_description: "Refresh token has been used or expired"
|
|
422
|
+
}, 400);
|
|
423
|
+
}
|
|
424
|
+
const tokens = await generateTokens(c, {
|
|
425
|
+
type: payload.type,
|
|
426
|
+
properties: payload.properties,
|
|
427
|
+
subject: payload.subject,
|
|
428
|
+
clientID: payload.clientID,
|
|
429
|
+
ttl: {
|
|
430
|
+
access: ttlAccess,
|
|
431
|
+
refresh: ttlRefresh
|
|
432
|
+
}
|
|
433
|
+
}, {
|
|
434
|
+
generateRefreshToken
|
|
435
|
+
});
|
|
436
|
+
const response = {
|
|
437
|
+
access_token: tokens.access,
|
|
438
|
+
token_type: "Bearer",
|
|
439
|
+
refresh_token: tokens.refresh,
|
|
440
|
+
expires_in: tokens.expiresIn
|
|
441
|
+
};
|
|
442
|
+
return c.json(response);
|
|
443
|
+
}
|
|
444
|
+
return c.json({
|
|
445
|
+
error: "unsupported_grant_type",
|
|
446
|
+
error_description: "The authorization grant type is not supported by the authorization server"
|
|
447
|
+
}, 400);
|
|
448
|
+
});
|
|
449
|
+
app.post("/revoke", cors({
|
|
450
|
+
origin: "*",
|
|
451
|
+
allowHeaders: ["Content-Type"],
|
|
452
|
+
allowMethods: ["POST"],
|
|
453
|
+
credentials: false
|
|
454
|
+
}), async (c) => {
|
|
455
|
+
const form = await c.req.formData();
|
|
456
|
+
const token = form.get("token")?.toString();
|
|
457
|
+
const tokenTypeHint = form.get("token_type_hint")?.toString();
|
|
458
|
+
if (!token) {
|
|
459
|
+
const err = new OauthError("invalid_request", "Missing token parameter");
|
|
460
|
+
return c.json(err.toJSON(), 400);
|
|
461
|
+
}
|
|
462
|
+
try {
|
|
463
|
+
if (tokenTypeHint === "refresh_token") {
|
|
464
|
+
const splits2 = token.split(":");
|
|
465
|
+
const tokenPart2 = splits2.pop();
|
|
466
|
+
if (tokenPart2 && splits2.length > 0) {
|
|
467
|
+
const subject = splits2.join(":");
|
|
468
|
+
const key = ["oauth:refresh", subject, tokenPart2];
|
|
469
|
+
const payload = await Storage.get(storage, key);
|
|
470
|
+
if (payload) {
|
|
471
|
+
await Storage.remove(storage, key);
|
|
472
|
+
const expiresAt2 = Date.now() + ttlRefreshRetention * 1000;
|
|
473
|
+
await Revocation.revoke(storage, token, expiresAt2);
|
|
474
|
+
return c.json({});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
} else if (tokenTypeHint === "access_token") {
|
|
478
|
+
const expiresAt2 = Date.now() + ttlAccess * 1000;
|
|
479
|
+
await Revocation.revoke(storage, token, expiresAt2);
|
|
480
|
+
return c.json({});
|
|
481
|
+
}
|
|
482
|
+
const splits = token.split(":");
|
|
483
|
+
const tokenPart = splits.pop();
|
|
484
|
+
if (tokenPart && splits.length > 0) {
|
|
485
|
+
const subject = splits.join(":");
|
|
486
|
+
const key = ["oauth:refresh", subject, tokenPart];
|
|
487
|
+
const payload = await Storage.get(storage, key);
|
|
488
|
+
if (payload) {
|
|
489
|
+
await Storage.remove(storage, key);
|
|
490
|
+
const expiresAt2 = Date.now() + ttlRefreshRetention * 1000;
|
|
491
|
+
await Revocation.revoke(storage, token, expiresAt2);
|
|
492
|
+
return c.json({});
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
const expiresAt = Date.now() + ttlAccess * 1000;
|
|
496
|
+
await Revocation.revoke(storage, token, expiresAt);
|
|
497
|
+
return c.json({});
|
|
498
|
+
} catch {
|
|
499
|
+
return c.json({
|
|
500
|
+
error: "server_error",
|
|
501
|
+
error_description: "Token revocation failed"
|
|
502
|
+
}, 500);
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
app.get("/authorize", async (c) => {
|
|
506
|
+
const provider = c.req.query("provider");
|
|
507
|
+
const response_type = c.req.query("response_type");
|
|
508
|
+
const redirect_uri = c.req.query("redirect_uri");
|
|
509
|
+
const state = c.req.query("state");
|
|
510
|
+
const client_id = c.req.query("client_id");
|
|
511
|
+
const audience = c.req.query("audience");
|
|
512
|
+
const code_challenge = c.req.query("code_challenge");
|
|
513
|
+
const code_challenge_method = c.req.query("code_challenge_method");
|
|
514
|
+
const scope = c.req.query("scope");
|
|
515
|
+
const scopes = scope ? scope.split(" ").filter(Boolean) : undefined;
|
|
516
|
+
const authorization = {
|
|
517
|
+
response_type,
|
|
518
|
+
redirect_uri,
|
|
519
|
+
state,
|
|
520
|
+
client_id,
|
|
521
|
+
audience,
|
|
522
|
+
scope,
|
|
523
|
+
scopes,
|
|
524
|
+
...code_challenge && code_challenge_method && {
|
|
525
|
+
pkce: {
|
|
526
|
+
challenge: code_challenge,
|
|
527
|
+
method: code_challenge_method
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
c.set("authorization", authorization);
|
|
532
|
+
if (!redirect_uri) {
|
|
533
|
+
return c.text("Missing redirect_uri", 400);
|
|
534
|
+
}
|
|
535
|
+
try {
|
|
536
|
+
const uri = new URL(redirect_uri);
|
|
537
|
+
if (!uri.protocol || !uri.host) {
|
|
538
|
+
return c.text("Invalid redirect_uri format", 400);
|
|
539
|
+
}
|
|
540
|
+
} catch {
|
|
541
|
+
return c.text("Invalid redirect_uri format", 400);
|
|
542
|
+
}
|
|
543
|
+
if (!response_type) {
|
|
544
|
+
throw new MissingParameterError("response_type");
|
|
545
|
+
}
|
|
546
|
+
if (!client_id) {
|
|
547
|
+
throw new MissingParameterError("client_id");
|
|
548
|
+
}
|
|
549
|
+
if (input.start) {
|
|
550
|
+
await input.start(c.req.raw);
|
|
551
|
+
}
|
|
552
|
+
if (!await allow()({
|
|
553
|
+
clientID: client_id,
|
|
554
|
+
redirectURI: redirect_uri,
|
|
555
|
+
audience
|
|
556
|
+
}, c.req.raw)) {
|
|
557
|
+
throw new UnauthorizedClientError(client_id, redirect_uri);
|
|
558
|
+
}
|
|
559
|
+
await auth.set(c, "authorization", 60 * 15, authorization);
|
|
560
|
+
if (provider) {
|
|
561
|
+
return c.redirect(`${provider}/authorize`);
|
|
562
|
+
}
|
|
563
|
+
const availableProviders = Object.keys(input.providers);
|
|
564
|
+
if (availableProviders.length === 1) {
|
|
565
|
+
return c.redirect(`${availableProviders[0]}/authorize`);
|
|
566
|
+
}
|
|
567
|
+
return auth.forward(c, await select()(Object.fromEntries(Object.entries(input.providers).map(([key, value]) => [key, value.type])), c.req.raw));
|
|
568
|
+
});
|
|
569
|
+
app.onError(async (err, c) => {
|
|
570
|
+
if (err instanceof UnknownStateError) {
|
|
571
|
+
return auth.forward(c, await error(err, c.req.raw));
|
|
572
|
+
}
|
|
573
|
+
try {
|
|
574
|
+
const authorization = await getAuthorization(c);
|
|
575
|
+
if (!authorization.redirect_uri) {
|
|
576
|
+
throw new Error("redirect_uri is required");
|
|
577
|
+
}
|
|
578
|
+
const url = new URL(authorization.redirect_uri);
|
|
579
|
+
const oauth = err instanceof OauthError ? err : new OauthError("server_error", err.message);
|
|
580
|
+
url.searchParams.set("error", oauth.error);
|
|
581
|
+
url.searchParams.set("error_description", oauth.description);
|
|
582
|
+
if (authorization.state) {
|
|
583
|
+
url.searchParams.set("state", authorization.state);
|
|
584
|
+
}
|
|
585
|
+
return c.redirect(url.toString());
|
|
586
|
+
} catch {
|
|
587
|
+
return c.json({
|
|
588
|
+
error: "server_error",
|
|
589
|
+
error_description: err.message
|
|
590
|
+
}, 500);
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
return app;
|
|
594
|
+
};
|
|
595
|
+
export {
|
|
596
|
+
issuer
|
|
597
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// src/error.ts
|
|
2
|
+
class OauthError extends Error {
|
|
3
|
+
error;
|
|
4
|
+
description;
|
|
5
|
+
constructor(error, description) {
|
|
6
|
+
super(`${error} - ${description}`);
|
|
7
|
+
this.name = "OauthError";
|
|
8
|
+
this.error = error;
|
|
9
|
+
this.description = description;
|
|
10
|
+
}
|
|
11
|
+
toJSON() {
|
|
12
|
+
return {
|
|
13
|
+
error: this.error,
|
|
14
|
+
error_description: this.description
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
class MissingProviderError extends OauthError {
|
|
20
|
+
constructor() {
|
|
21
|
+
super("invalid_request", "Must specify `provider` query parameter if `select` callback on issuer is not specified");
|
|
22
|
+
this.name = "MissingProviderError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class MissingParameterError extends OauthError {
|
|
27
|
+
parameter;
|
|
28
|
+
constructor(parameter) {
|
|
29
|
+
super("invalid_request", `Missing parameter: ${parameter}`);
|
|
30
|
+
this.name = "MissingParameterError";
|
|
31
|
+
this.parameter = parameter;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
class UnauthorizedClientError extends OauthError {
|
|
36
|
+
clientID;
|
|
37
|
+
constructor(clientID, redirectURI) {
|
|
38
|
+
super("unauthorized_client", `Client ${clientID} is not authorized to use this redirect_uri: ${redirectURI}`);
|
|
39
|
+
this.name = "UnauthorizedClientError";
|
|
40
|
+
this.clientID = clientID;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
class UnknownStateError extends Error {
|
|
45
|
+
constructor() {
|
|
46
|
+
super("The browser was in an unknown state. This could be because certain cookies expired or the browser was switched in the middle of an authentication flow.");
|
|
47
|
+
this.name = "UnknownStateError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class InvalidSubjectError extends Error {
|
|
52
|
+
constructor() {
|
|
53
|
+
super("Invalid subject");
|
|
54
|
+
this.name = "InvalidSubjectError";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
class InvalidRefreshTokenError extends Error {
|
|
59
|
+
constructor() {
|
|
60
|
+
super("Invalid refresh token");
|
|
61
|
+
this.name = "InvalidRefreshTokenError";
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
class InvalidAccessTokenError extends Error {
|
|
66
|
+
constructor() {
|
|
67
|
+
super("Invalid access token");
|
|
68
|
+
this.name = "InvalidAccessTokenError";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
class InvalidAuthorizationCodeError extends Error {
|
|
73
|
+
constructor() {
|
|
74
|
+
super("Invalid authorization code");
|
|
75
|
+
this.name = "InvalidAuthorizationCodeError";
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export {
|
|
79
|
+
UnknownStateError,
|
|
80
|
+
UnauthorizedClientError,
|
|
81
|
+
OauthError,
|
|
82
|
+
MissingProviderError,
|
|
83
|
+
MissingParameterError,
|
|
84
|
+
InvalidSubjectError,
|
|
85
|
+
InvalidRefreshTokenError,
|
|
86
|
+
InvalidAuthorizationCodeError,
|
|
87
|
+
InvalidAccessTokenError
|
|
88
|
+
};
|