@nekm/sveltekit-armor 0.4.3 → 0.5.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/browser/index.js +34 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/index.mjs +9 -0
- package/dist/browser/index.mjs.map +1 -0
- package/dist/chunk-XNOIZN7U.mjs +52 -0
- package/dist/chunk-XNOIZN7U.mjs.map +1 -0
- package/dist/errors.d.ts +0 -2
- package/dist/index.js +403 -298
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +504 -0
- package/dist/index.mjs.map +1 -0
- package/dist/utils/event.d.ts +1 -1
- package/package.json +12 -15
- package/src/errors.ts +0 -1
- package/src/routes/redirect-login.ts +9 -2
- package/src/utils/event.ts +2 -5
- package/dist/index.esm.js +0 -459
- package/dist/index.esm.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,33 +1,106 @@
|
|
|
1
|
-
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
var
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
10
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
|
+
var __spreadValues = (a, b) => {
|
|
12
|
+
for (var prop in b || (b = {}))
|
|
13
|
+
if (__hasOwnProp.call(b, prop))
|
|
14
|
+
__defNormalProp(a, prop, b[prop]);
|
|
15
|
+
if (__getOwnPropSymbols)
|
|
16
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
17
|
+
if (__propIsEnum.call(b, prop))
|
|
18
|
+
__defNormalProp(a, prop, b[prop]);
|
|
19
|
+
}
|
|
20
|
+
return a;
|
|
21
|
+
};
|
|
22
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
23
|
+
var __export = (target, all) => {
|
|
24
|
+
for (var name in all)
|
|
25
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
26
|
+
};
|
|
27
|
+
var __copyProps = (to, from, except, desc) => {
|
|
28
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
29
|
+
for (let key of __getOwnPropNames(from))
|
|
30
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
31
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
32
|
+
}
|
|
33
|
+
return to;
|
|
34
|
+
};
|
|
35
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
36
|
+
var __async = (__this, __arguments, generator) => {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
var fulfilled = (value) => {
|
|
39
|
+
try {
|
|
40
|
+
step(generator.next(value));
|
|
41
|
+
} catch (e) {
|
|
42
|
+
reject(e);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var rejected = (value) => {
|
|
46
|
+
try {
|
|
47
|
+
step(generator.throw(value));
|
|
48
|
+
} catch (e) {
|
|
49
|
+
reject(e);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
53
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
54
|
+
});
|
|
55
|
+
};
|
|
5
56
|
|
|
57
|
+
// src/index.ts
|
|
58
|
+
var src_exports = {};
|
|
59
|
+
__export(src_exports, {
|
|
60
|
+
armor: () => armor,
|
|
61
|
+
armorConfigFromOpenId: () => armorConfigFromOpenId,
|
|
62
|
+
armorCookieSession: () => armorCookieSession,
|
|
63
|
+
armorCookieSessionGet: () => armorCookieSessionGet,
|
|
64
|
+
armorRefreshFactory: () => armorRefreshFactory
|
|
65
|
+
});
|
|
66
|
+
module.exports = __toCommonJS(src_exports);
|
|
67
|
+
var import_kit6 = require("@sveltejs/kit");
|
|
68
|
+
|
|
69
|
+
// src/routes/login.ts
|
|
70
|
+
var import_kit2 = require("@sveltejs/kit");
|
|
71
|
+
var import_core4 = require("@nekm/core");
|
|
72
|
+
|
|
73
|
+
// src/routes/redirect-login.ts
|
|
74
|
+
var import_kit = require("@sveltejs/kit");
|
|
75
|
+
var import_core3 = require("@nekm/core");
|
|
76
|
+
var import_jose2 = require("jose");
|
|
77
|
+
|
|
78
|
+
// src/utils/utils.ts
|
|
79
|
+
var import_core = require("@nekm/core");
|
|
6
80
|
function urlConcat(origin, path) {
|
|
7
|
-
return [
|
|
81
|
+
return [(0, import_core.strTrimEnd)(origin, "/"), (0, import_core.strTrimStart)(path, "/")].join("/");
|
|
8
82
|
}
|
|
9
83
|
function isTokenExchange(value) {
|
|
10
84
|
if (typeof value !== "object" || value === null) return false;
|
|
11
85
|
const obj = value;
|
|
12
|
-
return typeof obj.access_token === "string" && obj.token_type === "Bearer" && typeof obj.expires_in === "number" &&
|
|
13
|
-
|
|
14
|
-
typeof obj.id_token === "string" || obj.id_token === undefined) && (typeof obj.refresh_token === "string" || obj.refresh_token === undefined) && (typeof obj.scope === "string" || obj.scope === undefined);
|
|
86
|
+
return typeof obj.access_token === "string" && obj.token_type === "Bearer" && typeof obj.expires_in === "number" && // Optional fields
|
|
87
|
+
(typeof obj.id_token === "string" || obj.id_token === void 0) && (typeof obj.refresh_token === "string" || obj.refresh_token === void 0) && (typeof obj.scope === "string" || obj.scope === void 0);
|
|
15
88
|
}
|
|
16
|
-
|
|
89
|
+
var MINUTES_MS = 60 * 1e3;
|
|
17
90
|
function shouldRefresh(tokens) {
|
|
18
|
-
const idExpiry = tokens.idToken.exp *
|
|
19
|
-
const accessExpiry = typeof tokens.accessToken !== "string" && tokens.accessToken.exp !==
|
|
91
|
+
const idExpiry = tokens.idToken.exp * 1e3;
|
|
92
|
+
const accessExpiry = typeof tokens.accessToken !== "string" && tokens.accessToken.exp !== void 0 ? tokens.accessToken.exp * 1e3 : Infinity;
|
|
20
93
|
return Math.min(idExpiry, accessExpiry) < Date.now() + 5 * MINUTES_MS;
|
|
21
94
|
}
|
|
22
95
|
function createExpiresAt(seconds) {
|
|
23
|
-
const now = new Date();
|
|
96
|
+
const now = /* @__PURE__ */ new Date();
|
|
24
97
|
now.setSeconds(now.getSeconds() + seconds);
|
|
25
98
|
return now;
|
|
26
99
|
}
|
|
27
100
|
function exchangeToTokens(exchange, idToken, accessToken) {
|
|
28
101
|
return {
|
|
29
102
|
exchange,
|
|
30
|
-
idToken
|
|
103
|
+
idToken,
|
|
31
104
|
// Generally, IdP's require an audience to get a JWT
|
|
32
105
|
// access token. Most cases, this doesn't matter.
|
|
33
106
|
accessToken: accessToken != null ? accessToken : exchange.access_token,
|
|
@@ -35,61 +108,65 @@ function exchangeToTokens(exchange, idToken, accessToken) {
|
|
|
35
108
|
};
|
|
36
109
|
}
|
|
37
110
|
|
|
111
|
+
// src/utils/jwt.ts
|
|
112
|
+
var import_jose = require("jose");
|
|
113
|
+
var import_core2 = require("@nekm/core");
|
|
38
114
|
function jwtIsCompactJwt(token) {
|
|
39
|
-
// Must be three base64url segments
|
|
40
115
|
const parts = token.trim().split(".");
|
|
41
|
-
return parts.length === 3 && parts.every(p => p.length > 0);
|
|
116
|
+
return parts.length === 3 && parts.every((p) => p.length > 0);
|
|
42
117
|
}
|
|
43
118
|
function jwtVerifyIdToken(config, jwks, idToken) {
|
|
44
|
-
const payload = jwtVerifyToken(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
119
|
+
const payload = jwtVerifyToken(
|
|
120
|
+
jwks,
|
|
121
|
+
{
|
|
122
|
+
issuer: config.oauth.issuer,
|
|
123
|
+
audience: config.oauth.clientId
|
|
124
|
+
},
|
|
125
|
+
idToken
|
|
126
|
+
);
|
|
127
|
+
(0, import_core2.throwIfUndefined)(payload);
|
|
50
128
|
return payload;
|
|
51
129
|
}
|
|
52
130
|
function jwtVerifyAccessToken(config, jwks, accessToken) {
|
|
53
|
-
const opts = {
|
|
54
|
-
issuer: config.oauth.issuer
|
|
55
|
-
};
|
|
131
|
+
const opts = { issuer: config.oauth.issuer };
|
|
56
132
|
if (config.oauth.audience) {
|
|
57
133
|
opts.audience = config.oauth.audience;
|
|
58
134
|
}
|
|
59
135
|
return jwtVerifyToken(jwks, opts, accessToken);
|
|
60
136
|
}
|
|
61
137
|
function isInvalidCompactJwt(error) {
|
|
62
|
-
return Boolean(
|
|
138
|
+
return Boolean(
|
|
139
|
+
typeof error === "object" && error && "message" in error && typeof error.message === "string" && /invalid compact jws/gi.test(error.message)
|
|
140
|
+
);
|
|
63
141
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
payload
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
142
|
+
function jwtVerifyToken(jwks, opts, token) {
|
|
143
|
+
return __async(this, null, function* () {
|
|
144
|
+
try {
|
|
145
|
+
if (!jwtIsCompactJwt(token)) {
|
|
146
|
+
return void 0;
|
|
147
|
+
}
|
|
148
|
+
const { payload } = yield (0, import_jose.jwtVerify)(token, jwks, opts);
|
|
149
|
+
return payload;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
if (isInvalidCompactJwt(error)) {
|
|
152
|
+
return void 0;
|
|
153
|
+
}
|
|
154
|
+
throw error;
|
|
76
155
|
}
|
|
77
|
-
|
|
78
|
-
}
|
|
156
|
+
});
|
|
79
157
|
}
|
|
80
158
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
})
|
|
86
|
-
const cookieSetOptions = Object.freeze({
|
|
87
|
-
...cookieDeleteOptions,
|
|
159
|
+
// src/utils/cookie.ts
|
|
160
|
+
var COOKIE_TOKENS = "tokens";
|
|
161
|
+
var COOKIE_STATE = "state";
|
|
162
|
+
var cookieDeleteOptions = Object.freeze({ path: "/" });
|
|
163
|
+
var cookieSetOptions = Object.freeze(__spreadProps(__spreadValues({}, cookieDeleteOptions), {
|
|
88
164
|
httpOnly: true,
|
|
89
165
|
secure: true,
|
|
90
166
|
sameSite: "lax",
|
|
91
|
-
maxAge: 1800
|
|
92
|
-
|
|
167
|
+
maxAge: 1800
|
|
168
|
+
// 30 minutes
|
|
169
|
+
}));
|
|
93
170
|
function cookieSet(cookies, key, value) {
|
|
94
171
|
cookies.set(key, JSON.stringify(value), cookieSetOptions);
|
|
95
172
|
}
|
|
@@ -102,207 +179,239 @@ function cookieGetAndDelete(cookies, key) {
|
|
|
102
179
|
}
|
|
103
180
|
function cookieGet(cookies, key) {
|
|
104
181
|
const value = cookies.get(key);
|
|
105
|
-
return !value ?
|
|
182
|
+
return !value ? void 0 : JSON.parse(value);
|
|
106
183
|
}
|
|
107
184
|
function cookieDelete(cookies, key) {
|
|
108
185
|
cookies.delete(key, cookieDeleteOptions);
|
|
109
186
|
}
|
|
110
187
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
class ArmorRefreshError extends ArmorError {}
|
|
116
|
-
|
|
117
|
-
function eventStateValidOrThrow(event) {
|
|
118
|
-
var _event$url$searchPara;
|
|
119
|
-
const state = (_event$url$searchPara = event.url.searchParams.get("state")) != null ? _event$url$searchPara : undefined;
|
|
188
|
+
// src/utils/event.ts
|
|
189
|
+
function eventStateValid(event) {
|
|
190
|
+
var _a;
|
|
191
|
+
const state = (_a = event.url.searchParams.get("state")) != null ? _a : void 0;
|
|
120
192
|
const stateCookie = cookieGetAndDelete(event.cookies, COOKIE_STATE);
|
|
121
|
-
|
|
122
|
-
throw new ArmorInvalidStateError();
|
|
123
|
-
}
|
|
193
|
+
return state !== stateCookie;
|
|
124
194
|
}
|
|
125
195
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
196
|
+
// src/routes/redirect-login.ts
|
|
197
|
+
var ROUTE_PATH_REDIRECT_LOGIN = "/_armor/redirect/login";
|
|
198
|
+
var routeRedirectLoginFactory = (config) => {
|
|
199
|
+
var _a, _b, _c;
|
|
200
|
+
const jwksUrl = new URL(
|
|
201
|
+
(_a = config.oauth.jwksEndpoint) != null ? _a : urlConcat(config.oauth.baseUrl, ".well-known/jwks.json")
|
|
202
|
+
);
|
|
203
|
+
const tokenUrl = (_b = config.oauth.tokenEndpoint) != null ? _b : urlConcat(config.oauth.baseUrl, "oauth2/token");
|
|
204
|
+
const scope = (_c = config.oauth.scope) != null ? _c : "openid profile email";
|
|
205
|
+
function exchangeCodeForToken(fetch2, origin, code) {
|
|
206
|
+
return __async(this, null, function* () {
|
|
207
|
+
const params = {
|
|
208
|
+
grant_type: "authorization_code",
|
|
209
|
+
client_id: config.oauth.clientId,
|
|
210
|
+
client_secret: config.oauth.clientSecret,
|
|
211
|
+
code,
|
|
212
|
+
redirect_uri: urlConcat(origin, ROUTE_PATH_REDIRECT_LOGIN),
|
|
213
|
+
scope
|
|
214
|
+
};
|
|
215
|
+
if (config.oauth.audience) {
|
|
216
|
+
params.audience = config.oauth.audience;
|
|
217
|
+
}
|
|
218
|
+
const response = yield fetch2(tokenUrl, {
|
|
219
|
+
method: "POST",
|
|
220
|
+
headers: {
|
|
221
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
222
|
+
Accept: "application/json"
|
|
223
|
+
},
|
|
224
|
+
body: new URLSearchParams(params).toString()
|
|
225
|
+
});
|
|
226
|
+
if (!response.ok) {
|
|
227
|
+
const error = yield response.text();
|
|
228
|
+
throw new Error(`Token exchange failed: ${error}`);
|
|
229
|
+
}
|
|
230
|
+
const token = yield response.json();
|
|
231
|
+
if (!isTokenExchange(token)) {
|
|
232
|
+
throw new Error("Response is not a valid token exchange.");
|
|
233
|
+
}
|
|
234
|
+
return token;
|
|
151
235
|
});
|
|
152
|
-
if (!response.ok) {
|
|
153
|
-
const error = await response.text();
|
|
154
|
-
throw new Error(`Token exchange failed: ${error}`);
|
|
155
|
-
}
|
|
156
|
-
const token = await response.json();
|
|
157
|
-
if (!isTokenExchange(token)) {
|
|
158
|
-
throw new Error("Response is not a valid token exchange.");
|
|
159
|
-
}
|
|
160
|
-
return token;
|
|
161
236
|
}
|
|
162
237
|
return {
|
|
163
238
|
path: ROUTE_PATH_REDIRECT_LOGIN,
|
|
164
|
-
|
|
165
|
-
event
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
error,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
if (!config.oauth.errorLoginRedirectPath) {
|
|
179
|
-
return new Response(`${error}\n${error_description}`.trimEnd(), {
|
|
180
|
-
headers: {
|
|
181
|
-
"Content-Type": "text/plain"
|
|
182
|
-
}
|
|
239
|
+
handle(_0) {
|
|
240
|
+
return __async(this, arguments, function* ({ event }) {
|
|
241
|
+
var _a2, _b2, _c2, _d2, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
|
|
242
|
+
(_b2 = (_a2 = config.logger) == null ? void 0 : _a2.debug) == null ? void 0 : _b2.call(_a2, "Handle login redirect callback.");
|
|
243
|
+
if (!eventStateValid(event)) {
|
|
244
|
+
(_d2 = (_c2 = config.logger) == null ? void 0 : _c2.warning) == null ? void 0 : _d2.call(_c2, "State missmatch");
|
|
245
|
+
throw (0, import_kit.redirect)(302, ROUTE_PATH_LOGIN);
|
|
246
|
+
}
|
|
247
|
+
const error = (_e = event.url.searchParams.get("error")) != null ? _e : void 0;
|
|
248
|
+
if (error) {
|
|
249
|
+
const error_description = (_f = event.url.searchParams.get("error_description")) != null ? _f : void 0;
|
|
250
|
+
(_h = (_g = config.logger) == null ? void 0 : _g.error) == null ? void 0 : _h.call(_g, "Login returned error.", {
|
|
251
|
+
error,
|
|
252
|
+
errorDescription: error_description
|
|
183
253
|
});
|
|
254
|
+
if (!config.oauth.errorLoginRedirectPath) {
|
|
255
|
+
return new Response(`${error}
|
|
256
|
+
${error_description}`.trimEnd(), {
|
|
257
|
+
headers: {
|
|
258
|
+
"Content-Type": "text/plain"
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
const errorParams = (0, import_core3.queryParamsCreate)({ error, error_description });
|
|
263
|
+
throw (0, import_kit.redirect)(
|
|
264
|
+
302,
|
|
265
|
+
`${config.oauth.errorLoginRedirectPath}?${errorParams}`
|
|
266
|
+
);
|
|
184
267
|
}
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
268
|
+
const code = (_i = event.url.searchParams.get("code")) != null ? _i : void 0;
|
|
269
|
+
(_k = (_j = config.logger) == null ? void 0 : _j.debug) == null ? void 0 : _k.call(_j, "Get code from query params.", { code });
|
|
270
|
+
(0, import_core3.throwIfUndefined)(code);
|
|
271
|
+
const exchange = yield exchangeCodeForToken(
|
|
272
|
+
event.fetch,
|
|
273
|
+
event.url.origin,
|
|
274
|
+
code
|
|
275
|
+
);
|
|
276
|
+
(_m = (_l = config.logger) == null ? void 0 : _l.debug) == null ? void 0 : _m.call(_l, "Exchange code for tokens.", { exchange });
|
|
277
|
+
const jwks = (0, import_jose2.createRemoteJWKSet)(jwksUrl);
|
|
278
|
+
const [idToken, accessToken] = yield Promise.all([
|
|
279
|
+
jwtVerifyIdToken(config, jwks, exchange.id_token),
|
|
280
|
+
jwtVerifyAccessToken(config, jwks, exchange.access_token)
|
|
281
|
+
]);
|
|
282
|
+
(_o = (_n = config.logger) == null ? void 0 : _n.debug) == null ? void 0 : _o.call(_n, "Extract and verify tokens.", {
|
|
283
|
+
idToken,
|
|
284
|
+
accessToken
|
|
188
285
|
});
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
});
|
|
195
|
-
core.throwIfUndefined(code);
|
|
196
|
-
const exchange = await exchangeCodeForToken(event.fetch, event.url.origin, code);
|
|
197
|
-
(_config$logger4 = config.logger) == null || _config$logger4.debug == null || _config$logger4.debug("Exchange code for tokens.", {
|
|
198
|
-
exchange
|
|
199
|
-
});
|
|
200
|
-
const jwks = jose.createRemoteJWKSet(jwksUrl);
|
|
201
|
-
const [idToken, accessToken] = await Promise.all([jwtVerifyIdToken(config, jwks, exchange.id_token), jwtVerifyAccessToken(config, jwks, exchange.access_token)]);
|
|
202
|
-
(_config$logger5 = config.logger) == null || _config$logger5.debug == null || _config$logger5.debug("Extract and verify tokens.", {
|
|
203
|
-
idToken,
|
|
204
|
-
accessToken
|
|
286
|
+
yield config.session.login(
|
|
287
|
+
event,
|
|
288
|
+
exchangeToTokens(exchange, idToken, accessToken)
|
|
289
|
+
);
|
|
290
|
+
throw (0, import_kit.redirect)(302, "/");
|
|
205
291
|
});
|
|
206
|
-
await config.session.login(event, exchangeToTokens(exchange, idToken, accessToken));
|
|
207
|
-
throw kit.redirect(302, "/");
|
|
208
292
|
}
|
|
209
293
|
};
|
|
210
294
|
};
|
|
211
295
|
|
|
212
|
-
|
|
213
|
-
|
|
296
|
+
// src/routes/login.ts
|
|
297
|
+
var import_node_crypto = require("crypto");
|
|
298
|
+
|
|
299
|
+
// src/browser/index.ts
|
|
300
|
+
var ARMOR_LOGIN = "/_armor/login";
|
|
301
|
+
var ARMOR_LOGOUT = "/_armor/logout";
|
|
214
302
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
const
|
|
303
|
+
// src/routes/login.ts
|
|
304
|
+
var ROUTE_PATH_LOGIN = ARMOR_LOGIN;
|
|
305
|
+
var routeLoginFactory = (config) => {
|
|
306
|
+
var _a, _b;
|
|
307
|
+
const authorizeEndpoint = (_a = config.oauth.authorizeEndpoint) != null ? _a : urlConcat(config.oauth.baseUrl, "oauth2/authorize");
|
|
308
|
+
const scope = (_b = config.oauth.scope) != null ? _b : "openid profile email";
|
|
220
309
|
return {
|
|
221
310
|
path: ROUTE_PATH_LOGIN,
|
|
222
|
-
|
|
223
|
-
event
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
params,
|
|
239
|
-
state
|
|
311
|
+
handle(_0) {
|
|
312
|
+
return __async(this, arguments, function* ({ event }) {
|
|
313
|
+
var _a2, _b2;
|
|
314
|
+
const state = (0, import_node_crypto.randomUUID)();
|
|
315
|
+
cookieSet(event.cookies, COOKIE_STATE, state);
|
|
316
|
+
const params = {
|
|
317
|
+
client_id: config.oauth.clientId,
|
|
318
|
+
response_type: "code",
|
|
319
|
+
redirect_uri: urlConcat(event.url.origin, ROUTE_PATH_REDIRECT_LOGIN),
|
|
320
|
+
state,
|
|
321
|
+
scope,
|
|
322
|
+
audience: config.oauth.audience
|
|
323
|
+
};
|
|
324
|
+
const paramsStr = (0, import_core4.queryParamsCreate)(params);
|
|
325
|
+
(_b2 = (_a2 = config.logger) == null ? void 0 : _a2.debug) == null ? void 0 : _b2.call(_a2, "Pre login redirect.", { params, state });
|
|
326
|
+
throw (0, import_kit2.redirect)(302, `${authorizeEndpoint}?${paramsStr}`);
|
|
240
327
|
});
|
|
241
|
-
throw kit.redirect(302, `${authorizeEndpoint}?${paramsStr}`);
|
|
242
328
|
}
|
|
243
329
|
};
|
|
244
330
|
};
|
|
245
331
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
332
|
+
// src/routes/logout.ts
|
|
333
|
+
var import_kit4 = require("@sveltejs/kit");
|
|
334
|
+
var import_core5 = require("@nekm/core");
|
|
335
|
+
|
|
336
|
+
// src/routes/redirect-logout.ts
|
|
337
|
+
var import_kit3 = require("@sveltejs/kit");
|
|
338
|
+
var ROUTE_PATH_REDIRECT_LOGOUT = "/_armor/redirect/logout";
|
|
339
|
+
var routeRedirectLogoutFactory = (config) => {
|
|
249
340
|
if (!config.oauth.logoutEndpoint) {
|
|
250
|
-
return
|
|
341
|
+
return void 0;
|
|
251
342
|
}
|
|
252
343
|
return {
|
|
253
344
|
path: ROUTE_PATH_REDIRECT_LOGOUT,
|
|
254
|
-
|
|
255
|
-
event
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
345
|
+
handle(_0) {
|
|
346
|
+
return __async(this, arguments, function* ({ event }) {
|
|
347
|
+
var _a2, _b;
|
|
348
|
+
(_b = (_a2 = config.logger) == null ? void 0 : _a2.debug) == null ? void 0 : _b.call(_a2, "Handle logout redirect callback.");
|
|
349
|
+
yield config.session.logout(event);
|
|
350
|
+
throw (0, import_kit3.redirect)(302, "/");
|
|
351
|
+
});
|
|
261
352
|
}
|
|
262
353
|
};
|
|
263
354
|
};
|
|
264
355
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
356
|
+
// src/routes/logout.ts
|
|
357
|
+
var ROUTE_PATH_LOGOUT = ARMOR_LOGOUT;
|
|
358
|
+
var routeLogoutFactory = (config) => {
|
|
359
|
+
var _a;
|
|
269
360
|
if (!config.oauth.logoutEndpoint) {
|
|
270
|
-
return
|
|
361
|
+
return void 0;
|
|
271
362
|
}
|
|
272
|
-
const returnTo = (
|
|
363
|
+
const returnTo = (_a = config.oauth.logoutReturnToParam) != null ? _a : "logout_uri";
|
|
273
364
|
return {
|
|
274
365
|
path: ROUTE_PATH_LOGOUT,
|
|
275
|
-
|
|
276
|
-
event
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
params
|
|
366
|
+
handle(_0) {
|
|
367
|
+
return __async(this, arguments, function* ({ event }) {
|
|
368
|
+
var _a2, _b2;
|
|
369
|
+
const params = {
|
|
370
|
+
[returnTo]: urlConcat(event.url.origin, ROUTE_PATH_REDIRECT_LOGOUT),
|
|
371
|
+
client_id: config.oauth.clientId
|
|
372
|
+
};
|
|
373
|
+
const paramsStr = (0, import_core5.queryParamsCreate)(params);
|
|
374
|
+
(_b2 = (_a2 = config.logger) == null ? void 0 : _a2.debug) == null ? void 0 : _b2.call(_a2, "Pre logout redirect.", { params });
|
|
375
|
+
throw (0, import_kit4.redirect)(302, `${config.oauth.logoutEndpoint}?${paramsStr}`);
|
|
286
376
|
});
|
|
287
|
-
throw kit.redirect(302, `${config.oauth.logoutEndpoint}?${paramsStr}`);
|
|
288
377
|
}
|
|
289
378
|
};
|
|
290
379
|
};
|
|
291
380
|
|
|
292
|
-
|
|
381
|
+
// src/routes/routes.ts
|
|
382
|
+
var routeFactories = Object.freeze([
|
|
383
|
+
routeLoginFactory,
|
|
384
|
+
routeLogoutFactory,
|
|
385
|
+
routeRedirectLoginFactory,
|
|
386
|
+
routeRedirectLogoutFactory
|
|
387
|
+
]);
|
|
293
388
|
function routeByPathFactory(config) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
.map(route => [route.path, route]));
|
|
389
|
+
return new Map(
|
|
390
|
+
routeFactories.map((routeFactory) => routeFactory(config)).filter((route) => Boolean(route)).map((route) => [route.path, route])
|
|
391
|
+
);
|
|
298
392
|
}
|
|
299
393
|
|
|
394
|
+
// src/errors.ts
|
|
395
|
+
var ArmorError = class extends Error {
|
|
396
|
+
};
|
|
397
|
+
var ArmorOpenIdConfigError = class extends ArmorError {
|
|
398
|
+
};
|
|
399
|
+
var ArmorAuthMissingError = class extends ArmorError {
|
|
400
|
+
};
|
|
401
|
+
var ArmorRefreshError = class extends ArmorError {
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// src/utils/refresh.ts
|
|
405
|
+
var import_jose3 = require("jose");
|
|
406
|
+
var import_kit5 = require("@sveltejs/kit");
|
|
300
407
|
function armorRefreshFactory(config) {
|
|
301
|
-
var
|
|
302
|
-
const refreshEndpoint = (
|
|
303
|
-
const jwksUrl = new URL(
|
|
304
|
-
|
|
305
|
-
|
|
408
|
+
var _a, _b;
|
|
409
|
+
const refreshEndpoint = (_a = config.oauth.refreshEndpoint) != null ? _a : urlConcat(config.oauth.baseUrl, "oauth2/token");
|
|
410
|
+
const jwksUrl = new URL(
|
|
411
|
+
(_b = config.oauth.jwksEndpoint) != null ? _b : urlConcat(config.oauth.baseUrl, ".well-known/jwks.json")
|
|
412
|
+
);
|
|
413
|
+
const refresh = (fetch2, refreshToken) => __async(null, null, function* () {
|
|
414
|
+
var _a2, _b2, _c2, _d, _e;
|
|
306
415
|
const body = new URLSearchParams({
|
|
307
416
|
grant_type: "refresh_token",
|
|
308
417
|
client_id: config.oauth.clientId,
|
|
@@ -312,7 +421,7 @@ function armorRefreshFactory(config) {
|
|
|
312
421
|
if (config.oauth.scope) {
|
|
313
422
|
body.set("scope", config.oauth.scope);
|
|
314
423
|
}
|
|
315
|
-
const response =
|
|
424
|
+
const response = yield fetch2(refreshEndpoint, {
|
|
316
425
|
method: "POST",
|
|
317
426
|
headers: {
|
|
318
427
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
@@ -321,143 +430,139 @@ function armorRefreshFactory(config) {
|
|
|
321
430
|
body: body.toString()
|
|
322
431
|
});
|
|
323
432
|
if (!response.ok) {
|
|
324
|
-
const error =
|
|
433
|
+
const error = yield response.text();
|
|
325
434
|
throw new ArmorRefreshError(`Could not refresh token: ${error}`);
|
|
326
435
|
}
|
|
327
|
-
const json =
|
|
328
|
-
const newExchange = {
|
|
329
|
-
|
|
330
|
-
refresh_token: (_json$refresh_token = json.refresh_token) != null ? _json$refresh_token : refreshToken
|
|
331
|
-
};
|
|
332
|
-
(_config$logger = config.logger) == null || _config$logger.debug == null || _config$logger.debug("Exchange code for tokens.", {
|
|
333
|
-
newExchange
|
|
436
|
+
const json = yield response.json();
|
|
437
|
+
const newExchange = __spreadProps(__spreadValues({}, json), {
|
|
438
|
+
refresh_token: (_a2 = json.refresh_token) != null ? _a2 : refreshToken
|
|
334
439
|
});
|
|
335
|
-
|
|
336
|
-
const
|
|
337
|
-
|
|
440
|
+
(_c2 = (_b2 = config.logger) == null ? void 0 : _b2.debug) == null ? void 0 : _c2.call(_b2, "Exchange code for tokens.", { newExchange });
|
|
441
|
+
const jwks = (0, import_jose3.createRemoteJWKSet)(jwksUrl);
|
|
442
|
+
const [idToken, accessToken] = yield Promise.all([
|
|
443
|
+
jwtVerifyIdToken(config, jwks, newExchange.id_token),
|
|
444
|
+
jwtVerifyAccessToken(config, jwks, newExchange.access_token)
|
|
445
|
+
]);
|
|
446
|
+
(_e = (_d = config.logger) == null ? void 0 : _d.debug) == null ? void 0 : _e.call(_d, "Extract and verify tokens.", {
|
|
338
447
|
idToken,
|
|
339
448
|
accessToken
|
|
340
449
|
});
|
|
341
|
-
const validTokens = exchangeToTokens(
|
|
450
|
+
const validTokens = exchangeToTokens(
|
|
451
|
+
newExchange,
|
|
452
|
+
idToken,
|
|
453
|
+
accessToken
|
|
454
|
+
);
|
|
342
455
|
return validTokens;
|
|
343
|
-
};
|
|
456
|
+
});
|
|
344
457
|
return {
|
|
345
458
|
refresh,
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
(
|
|
352
|
-
|
|
353
|
-
|
|
459
|
+
ensureValidToken(event, tokens, fn) {
|
|
460
|
+
return __async(this, null, function* () {
|
|
461
|
+
var _a2, _b2;
|
|
462
|
+
try {
|
|
463
|
+
let validTokens = tokens;
|
|
464
|
+
if (shouldRefresh(tokens)) {
|
|
465
|
+
(_b2 = (_a2 = config.logger) == null ? void 0 : _a2.debug) == null ? void 0 : _b2.call(_a2, "Tokens has expired. Refreshing...");
|
|
466
|
+
if (!tokens.exchange.refresh_token) {
|
|
467
|
+
throw (0, import_kit5.redirect)(302, ROUTE_PATH_LOGIN);
|
|
468
|
+
}
|
|
469
|
+
validTokens = yield refresh(fetch, tokens.exchange.refresh_token);
|
|
470
|
+
yield config.session.login(event, validTokens);
|
|
354
471
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
throw kit.redirect(302, ROUTE_PATH_LOGIN);
|
|
472
|
+
return fn(validTokens);
|
|
473
|
+
} catch (error) {
|
|
474
|
+
if (error instanceof ArmorRefreshError) {
|
|
475
|
+
throw (0, import_kit5.redirect)(302, ROUTE_PATH_LOGIN);
|
|
476
|
+
}
|
|
477
|
+
throw error;
|
|
362
478
|
}
|
|
363
|
-
|
|
364
|
-
}
|
|
479
|
+
});
|
|
365
480
|
}
|
|
366
481
|
};
|
|
367
482
|
}
|
|
368
483
|
|
|
484
|
+
// src/session/cookie.ts
|
|
369
485
|
function cookieSessionGetTokens({
|
|
370
486
|
cookies
|
|
371
487
|
}) {
|
|
372
488
|
return cookies.get(COOKIE_TOKENS);
|
|
373
489
|
}
|
|
374
|
-
function cookieSessionLogin({
|
|
375
|
-
cookies
|
|
376
|
-
}, tokens) {
|
|
490
|
+
function cookieSessionLogin({ cookies }, tokens) {
|
|
377
491
|
cookieSet(cookies, COOKIE_TOKENS, tokens);
|
|
378
492
|
}
|
|
379
|
-
function cookieSessionLogout({
|
|
380
|
-
cookies
|
|
381
|
-
}) {
|
|
493
|
+
function cookieSessionLogout({ cookies }) {
|
|
382
494
|
cookieDelete(cookies, COOKIE_TOKENS);
|
|
383
495
|
}
|
|
384
|
-
function armorCookieSessionGet({
|
|
385
|
-
cookies
|
|
386
|
-
}) {
|
|
496
|
+
function armorCookieSessionGet({ cookies }) {
|
|
387
497
|
const tokens = cookieGet(cookies, COOKIE_TOKENS);
|
|
388
498
|
if (!tokens) {
|
|
389
499
|
throw new ArmorAuthMissingError();
|
|
390
500
|
}
|
|
391
501
|
return tokens;
|
|
392
502
|
}
|
|
393
|
-
|
|
503
|
+
var armorCookieSession = {
|
|
394
504
|
getTokens: cookieSessionGetTokens,
|
|
395
505
|
login: cookieSessionLogin,
|
|
396
506
|
logout: cookieSessionLogout
|
|
397
507
|
};
|
|
398
508
|
|
|
509
|
+
// src/index.ts
|
|
399
510
|
function armor(config) {
|
|
400
511
|
const routeByPath = routeByPathFactory(config);
|
|
401
512
|
const refresh = armorRefreshFactory(config);
|
|
402
|
-
return {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}
|
|
421
|
-
return refresh.ensureValidToken(event, tokens, () => resolve(event));
|
|
513
|
+
return __spreadProps(__spreadValues({}, refresh), {
|
|
514
|
+
handle(_0) {
|
|
515
|
+
return __async(this, arguments, function* ({ event, resolve }) {
|
|
516
|
+
var _a2, _b;
|
|
517
|
+
const route = routeByPath.get(event.url.pathname);
|
|
518
|
+
if (route) {
|
|
519
|
+
return route.handle({ event, resolve });
|
|
520
|
+
}
|
|
521
|
+
const tokens = yield config.session.getTokens(event);
|
|
522
|
+
if (!tokens) {
|
|
523
|
+
(_b = (_a2 = config.logger) == null ? void 0 : _a2.warning) == null ? void 0 : _b.call(
|
|
524
|
+
_a2,
|
|
525
|
+
"Could not find tokens. Redirecting to login."
|
|
526
|
+
);
|
|
527
|
+
throw (0, import_kit6.redirect)(302, ROUTE_PATH_LOGIN);
|
|
528
|
+
}
|
|
529
|
+
return refresh.ensureValidToken(event, tokens, () => resolve(event));
|
|
530
|
+
});
|
|
422
531
|
}
|
|
423
|
-
};
|
|
532
|
+
});
|
|
424
533
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
534
|
+
function armorConfigFromOpenId(config, fetch2) {
|
|
535
|
+
return __async(this, null, function* () {
|
|
536
|
+
var _a;
|
|
537
|
+
const fetchToUse = fetch2 != null ? fetch2 : global.fetch;
|
|
538
|
+
const response = yield fetchToUse(config.oauth.openIdConfigEndpoint, {
|
|
539
|
+
headers: {
|
|
540
|
+
Accept: "application/json"
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
if (!response.ok) {
|
|
544
|
+
const text = yield response.text();
|
|
545
|
+
throw new ArmorOpenIdConfigError(text);
|
|
437
546
|
}
|
|
547
|
+
const body = yield response.json();
|
|
548
|
+
return __spreadProps(__spreadValues({}, config), {
|
|
549
|
+
oauth: __spreadProps(__spreadValues({}, config.oauth), {
|
|
550
|
+
tokenEndpoint: body.token_endpoint,
|
|
551
|
+
authorizeEndpoint: body.authorization_endpoint,
|
|
552
|
+
issuer: body.issuer,
|
|
553
|
+
jwksEndpoint: body.jwks_uri,
|
|
554
|
+
logoutEndpoint: (_a = body.end_session_endpoint) != null ? _a : void 0,
|
|
555
|
+
refreshEndpoint: body.token_endpoint
|
|
556
|
+
})
|
|
557
|
+
});
|
|
438
558
|
});
|
|
439
|
-
if (!response.ok) {
|
|
440
|
-
const text = await response.text();
|
|
441
|
-
throw new ArmorOpenIdConfigError(text);
|
|
442
|
-
}
|
|
443
|
-
const body = await response.json();
|
|
444
|
-
return {
|
|
445
|
-
...config,
|
|
446
|
-
oauth: {
|
|
447
|
-
...config.oauth,
|
|
448
|
-
tokenEndpoint: body.token_endpoint,
|
|
449
|
-
authorizeEndpoint: body.authorization_endpoint,
|
|
450
|
-
issuer: body.issuer,
|
|
451
|
-
jwksEndpoint: body.jwks_uri,
|
|
452
|
-
logoutEndpoint: (_body$end_session_end = body.end_session_endpoint) != null ? _body$end_session_end : undefined,
|
|
453
|
-
refreshEndpoint: body.token_endpoint
|
|
454
|
-
}
|
|
455
|
-
};
|
|
456
559
|
}
|
|
457
|
-
|
|
458
|
-
exports
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
560
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
561
|
+
0 && (module.exports = {
|
|
562
|
+
armor,
|
|
563
|
+
armorConfigFromOpenId,
|
|
564
|
+
armorCookieSession,
|
|
565
|
+
armorCookieSessionGet,
|
|
566
|
+
armorRefreshFactory
|
|
567
|
+
});
|
|
568
|
+
//# sourceMappingURL=index.js.map
|