@nekm/sveltekit-armor 0.4.1 → 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.d.ts +1 -1
- 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/index.ts +1 -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.mjs
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ARMOR_LOGIN,
|
|
3
|
+
ARMOR_LOGOUT,
|
|
4
|
+
__async,
|
|
5
|
+
__spreadProps,
|
|
6
|
+
__spreadValues
|
|
7
|
+
} from "./chunk-XNOIZN7U.mjs";
|
|
8
|
+
|
|
9
|
+
// src/index.ts
|
|
10
|
+
import { redirect as redirect6 } from "@sveltejs/kit";
|
|
11
|
+
|
|
12
|
+
// src/routes/login.ts
|
|
13
|
+
import { redirect as redirect2 } from "@sveltejs/kit";
|
|
14
|
+
import { queryParamsCreate as queryParamsCreate2 } from "@nekm/core";
|
|
15
|
+
|
|
16
|
+
// src/routes/redirect-login.ts
|
|
17
|
+
import { redirect } from "@sveltejs/kit";
|
|
18
|
+
import { queryParamsCreate, throwIfUndefined as throwIfUndefined2 } from "@nekm/core";
|
|
19
|
+
import { createRemoteJWKSet } from "jose";
|
|
20
|
+
|
|
21
|
+
// src/utils/utils.ts
|
|
22
|
+
import { strTrimEnd, strTrimStart } from "@nekm/core";
|
|
23
|
+
function urlConcat(origin, path) {
|
|
24
|
+
return [strTrimEnd(origin, "/"), strTrimStart(path, "/")].join("/");
|
|
25
|
+
}
|
|
26
|
+
function isTokenExchange(value) {
|
|
27
|
+
if (typeof value !== "object" || value === null) return false;
|
|
28
|
+
const obj = value;
|
|
29
|
+
return typeof obj.access_token === "string" && obj.token_type === "Bearer" && typeof obj.expires_in === "number" && // Optional fields
|
|
30
|
+
(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);
|
|
31
|
+
}
|
|
32
|
+
var MINUTES_MS = 60 * 1e3;
|
|
33
|
+
function shouldRefresh(tokens) {
|
|
34
|
+
const idExpiry = tokens.idToken.exp * 1e3;
|
|
35
|
+
const accessExpiry = typeof tokens.accessToken !== "string" && tokens.accessToken.exp !== void 0 ? tokens.accessToken.exp * 1e3 : Infinity;
|
|
36
|
+
return Math.min(idExpiry, accessExpiry) < Date.now() + 5 * MINUTES_MS;
|
|
37
|
+
}
|
|
38
|
+
function createExpiresAt(seconds) {
|
|
39
|
+
const now = /* @__PURE__ */ new Date();
|
|
40
|
+
now.setSeconds(now.getSeconds() + seconds);
|
|
41
|
+
return now;
|
|
42
|
+
}
|
|
43
|
+
function exchangeToTokens(exchange, idToken, accessToken) {
|
|
44
|
+
return {
|
|
45
|
+
exchange,
|
|
46
|
+
idToken,
|
|
47
|
+
// Generally, IdP's require an audience to get a JWT
|
|
48
|
+
// access token. Most cases, this doesn't matter.
|
|
49
|
+
accessToken: accessToken != null ? accessToken : exchange.access_token,
|
|
50
|
+
expiresAt: createExpiresAt(exchange.expires_in)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/utils/jwt.ts
|
|
55
|
+
import { jwtVerify } from "jose";
|
|
56
|
+
import { throwIfUndefined } from "@nekm/core";
|
|
57
|
+
function jwtIsCompactJwt(token) {
|
|
58
|
+
const parts = token.trim().split(".");
|
|
59
|
+
return parts.length === 3 && parts.every((p) => p.length > 0);
|
|
60
|
+
}
|
|
61
|
+
function jwtVerifyIdToken(config, jwks, idToken) {
|
|
62
|
+
const payload = jwtVerifyToken(
|
|
63
|
+
jwks,
|
|
64
|
+
{
|
|
65
|
+
issuer: config.oauth.issuer,
|
|
66
|
+
audience: config.oauth.clientId
|
|
67
|
+
},
|
|
68
|
+
idToken
|
|
69
|
+
);
|
|
70
|
+
throwIfUndefined(payload);
|
|
71
|
+
return payload;
|
|
72
|
+
}
|
|
73
|
+
function jwtVerifyAccessToken(config, jwks, accessToken) {
|
|
74
|
+
const opts = { issuer: config.oauth.issuer };
|
|
75
|
+
if (config.oauth.audience) {
|
|
76
|
+
opts.audience = config.oauth.audience;
|
|
77
|
+
}
|
|
78
|
+
return jwtVerifyToken(jwks, opts, accessToken);
|
|
79
|
+
}
|
|
80
|
+
function isInvalidCompactJwt(error) {
|
|
81
|
+
return Boolean(
|
|
82
|
+
typeof error === "object" && error && "message" in error && typeof error.message === "string" && /invalid compact jws/gi.test(error.message)
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
function jwtVerifyToken(jwks, opts, token) {
|
|
86
|
+
return __async(this, null, function* () {
|
|
87
|
+
try {
|
|
88
|
+
if (!jwtIsCompactJwt(token)) {
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
const { payload } = yield jwtVerify(token, jwks, opts);
|
|
92
|
+
return payload;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (isInvalidCompactJwt(error)) {
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// src/utils/cookie.ts
|
|
103
|
+
var COOKIE_TOKENS = "tokens";
|
|
104
|
+
var COOKIE_STATE = "state";
|
|
105
|
+
var cookieDeleteOptions = Object.freeze({ path: "/" });
|
|
106
|
+
var cookieSetOptions = Object.freeze(__spreadProps(__spreadValues({}, cookieDeleteOptions), {
|
|
107
|
+
httpOnly: true,
|
|
108
|
+
secure: true,
|
|
109
|
+
sameSite: "lax",
|
|
110
|
+
maxAge: 1800
|
|
111
|
+
// 30 minutes
|
|
112
|
+
}));
|
|
113
|
+
function cookieSet(cookies, key, value) {
|
|
114
|
+
cookies.set(key, JSON.stringify(value), cookieSetOptions);
|
|
115
|
+
}
|
|
116
|
+
function cookieGetAndDelete(cookies, key) {
|
|
117
|
+
const value = cookieGet(cookies, key);
|
|
118
|
+
if (value) {
|
|
119
|
+
cookies.delete(key, cookieDeleteOptions);
|
|
120
|
+
}
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
function cookieGet(cookies, key) {
|
|
124
|
+
const value = cookies.get(key);
|
|
125
|
+
return !value ? void 0 : JSON.parse(value);
|
|
126
|
+
}
|
|
127
|
+
function cookieDelete(cookies, key) {
|
|
128
|
+
cookies.delete(key, cookieDeleteOptions);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/utils/event.ts
|
|
132
|
+
function eventStateValid(event) {
|
|
133
|
+
var _a;
|
|
134
|
+
const state = (_a = event.url.searchParams.get("state")) != null ? _a : void 0;
|
|
135
|
+
const stateCookie = cookieGetAndDelete(event.cookies, COOKIE_STATE);
|
|
136
|
+
return state !== stateCookie;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/routes/redirect-login.ts
|
|
140
|
+
var ROUTE_PATH_REDIRECT_LOGIN = "/_armor/redirect/login";
|
|
141
|
+
var routeRedirectLoginFactory = (config) => {
|
|
142
|
+
var _a, _b, _c;
|
|
143
|
+
const jwksUrl = new URL(
|
|
144
|
+
(_a = config.oauth.jwksEndpoint) != null ? _a : urlConcat(config.oauth.baseUrl, ".well-known/jwks.json")
|
|
145
|
+
);
|
|
146
|
+
const tokenUrl = (_b = config.oauth.tokenEndpoint) != null ? _b : urlConcat(config.oauth.baseUrl, "oauth2/token");
|
|
147
|
+
const scope = (_c = config.oauth.scope) != null ? _c : "openid profile email";
|
|
148
|
+
function exchangeCodeForToken(fetch2, origin, code) {
|
|
149
|
+
return __async(this, null, function* () {
|
|
150
|
+
const params = {
|
|
151
|
+
grant_type: "authorization_code",
|
|
152
|
+
client_id: config.oauth.clientId,
|
|
153
|
+
client_secret: config.oauth.clientSecret,
|
|
154
|
+
code,
|
|
155
|
+
redirect_uri: urlConcat(origin, ROUTE_PATH_REDIRECT_LOGIN),
|
|
156
|
+
scope
|
|
157
|
+
};
|
|
158
|
+
if (config.oauth.audience) {
|
|
159
|
+
params.audience = config.oauth.audience;
|
|
160
|
+
}
|
|
161
|
+
const response = yield fetch2(tokenUrl, {
|
|
162
|
+
method: "POST",
|
|
163
|
+
headers: {
|
|
164
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
165
|
+
Accept: "application/json"
|
|
166
|
+
},
|
|
167
|
+
body: new URLSearchParams(params).toString()
|
|
168
|
+
});
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
const error = yield response.text();
|
|
171
|
+
throw new Error(`Token exchange failed: ${error}`);
|
|
172
|
+
}
|
|
173
|
+
const token = yield response.json();
|
|
174
|
+
if (!isTokenExchange(token)) {
|
|
175
|
+
throw new Error("Response is not a valid token exchange.");
|
|
176
|
+
}
|
|
177
|
+
return token;
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
path: ROUTE_PATH_REDIRECT_LOGIN,
|
|
182
|
+
handle(_0) {
|
|
183
|
+
return __async(this, arguments, function* ({ event }) {
|
|
184
|
+
var _a2, _b2, _c2, _d2, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
|
|
185
|
+
(_b2 = (_a2 = config.logger) == null ? void 0 : _a2.debug) == null ? void 0 : _b2.call(_a2, "Handle login redirect callback.");
|
|
186
|
+
if (!eventStateValid(event)) {
|
|
187
|
+
(_d2 = (_c2 = config.logger) == null ? void 0 : _c2.warning) == null ? void 0 : _d2.call(_c2, "State missmatch");
|
|
188
|
+
throw redirect(302, ROUTE_PATH_LOGIN);
|
|
189
|
+
}
|
|
190
|
+
const error = (_e = event.url.searchParams.get("error")) != null ? _e : void 0;
|
|
191
|
+
if (error) {
|
|
192
|
+
const error_description = (_f = event.url.searchParams.get("error_description")) != null ? _f : void 0;
|
|
193
|
+
(_h = (_g = config.logger) == null ? void 0 : _g.error) == null ? void 0 : _h.call(_g, "Login returned error.", {
|
|
194
|
+
error,
|
|
195
|
+
errorDescription: error_description
|
|
196
|
+
});
|
|
197
|
+
if (!config.oauth.errorLoginRedirectPath) {
|
|
198
|
+
return new Response(`${error}
|
|
199
|
+
${error_description}`.trimEnd(), {
|
|
200
|
+
headers: {
|
|
201
|
+
"Content-Type": "text/plain"
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
const errorParams = queryParamsCreate({ error, error_description });
|
|
206
|
+
throw redirect(
|
|
207
|
+
302,
|
|
208
|
+
`${config.oauth.errorLoginRedirectPath}?${errorParams}`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
const code = (_i = event.url.searchParams.get("code")) != null ? _i : void 0;
|
|
212
|
+
(_k = (_j = config.logger) == null ? void 0 : _j.debug) == null ? void 0 : _k.call(_j, "Get code from query params.", { code });
|
|
213
|
+
throwIfUndefined2(code);
|
|
214
|
+
const exchange = yield exchangeCodeForToken(
|
|
215
|
+
event.fetch,
|
|
216
|
+
event.url.origin,
|
|
217
|
+
code
|
|
218
|
+
);
|
|
219
|
+
(_m = (_l = config.logger) == null ? void 0 : _l.debug) == null ? void 0 : _m.call(_l, "Exchange code for tokens.", { exchange });
|
|
220
|
+
const jwks = createRemoteJWKSet(jwksUrl);
|
|
221
|
+
const [idToken, accessToken] = yield Promise.all([
|
|
222
|
+
jwtVerifyIdToken(config, jwks, exchange.id_token),
|
|
223
|
+
jwtVerifyAccessToken(config, jwks, exchange.access_token)
|
|
224
|
+
]);
|
|
225
|
+
(_o = (_n = config.logger) == null ? void 0 : _n.debug) == null ? void 0 : _o.call(_n, "Extract and verify tokens.", {
|
|
226
|
+
idToken,
|
|
227
|
+
accessToken
|
|
228
|
+
});
|
|
229
|
+
yield config.session.login(
|
|
230
|
+
event,
|
|
231
|
+
exchangeToTokens(exchange, idToken, accessToken)
|
|
232
|
+
);
|
|
233
|
+
throw redirect(302, "/");
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// src/routes/login.ts
|
|
240
|
+
import { randomUUID } from "crypto";
|
|
241
|
+
var ROUTE_PATH_LOGIN = ARMOR_LOGIN;
|
|
242
|
+
var routeLoginFactory = (config) => {
|
|
243
|
+
var _a, _b;
|
|
244
|
+
const authorizeEndpoint = (_a = config.oauth.authorizeEndpoint) != null ? _a : urlConcat(config.oauth.baseUrl, "oauth2/authorize");
|
|
245
|
+
const scope = (_b = config.oauth.scope) != null ? _b : "openid profile email";
|
|
246
|
+
return {
|
|
247
|
+
path: ROUTE_PATH_LOGIN,
|
|
248
|
+
handle(_0) {
|
|
249
|
+
return __async(this, arguments, function* ({ event }) {
|
|
250
|
+
var _a2, _b2;
|
|
251
|
+
const state = randomUUID();
|
|
252
|
+
cookieSet(event.cookies, COOKIE_STATE, state);
|
|
253
|
+
const params = {
|
|
254
|
+
client_id: config.oauth.clientId,
|
|
255
|
+
response_type: "code",
|
|
256
|
+
redirect_uri: urlConcat(event.url.origin, ROUTE_PATH_REDIRECT_LOGIN),
|
|
257
|
+
state,
|
|
258
|
+
scope,
|
|
259
|
+
audience: config.oauth.audience
|
|
260
|
+
};
|
|
261
|
+
const paramsStr = queryParamsCreate2(params);
|
|
262
|
+
(_b2 = (_a2 = config.logger) == null ? void 0 : _a2.debug) == null ? void 0 : _b2.call(_a2, "Pre login redirect.", { params, state });
|
|
263
|
+
throw redirect2(302, `${authorizeEndpoint}?${paramsStr}`);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// src/routes/logout.ts
|
|
270
|
+
import { redirect as redirect4 } from "@sveltejs/kit";
|
|
271
|
+
import { queryParamsCreate as queryParamsCreate3 } from "@nekm/core";
|
|
272
|
+
|
|
273
|
+
// src/routes/redirect-logout.ts
|
|
274
|
+
import { redirect as redirect3 } from "@sveltejs/kit";
|
|
275
|
+
var ROUTE_PATH_REDIRECT_LOGOUT = "/_armor/redirect/logout";
|
|
276
|
+
var routeRedirectLogoutFactory = (config) => {
|
|
277
|
+
if (!config.oauth.logoutEndpoint) {
|
|
278
|
+
return void 0;
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
path: ROUTE_PATH_REDIRECT_LOGOUT,
|
|
282
|
+
handle(_0) {
|
|
283
|
+
return __async(this, arguments, function* ({ event }) {
|
|
284
|
+
var _a2, _b;
|
|
285
|
+
(_b = (_a2 = config.logger) == null ? void 0 : _a2.debug) == null ? void 0 : _b.call(_a2, "Handle logout redirect callback.");
|
|
286
|
+
yield config.session.logout(event);
|
|
287
|
+
throw redirect3(302, "/");
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
// src/routes/logout.ts
|
|
294
|
+
var ROUTE_PATH_LOGOUT = ARMOR_LOGOUT;
|
|
295
|
+
var routeLogoutFactory = (config) => {
|
|
296
|
+
var _a;
|
|
297
|
+
if (!config.oauth.logoutEndpoint) {
|
|
298
|
+
return void 0;
|
|
299
|
+
}
|
|
300
|
+
const returnTo = (_a = config.oauth.logoutReturnToParam) != null ? _a : "logout_uri";
|
|
301
|
+
return {
|
|
302
|
+
path: ROUTE_PATH_LOGOUT,
|
|
303
|
+
handle(_0) {
|
|
304
|
+
return __async(this, arguments, function* ({ event }) {
|
|
305
|
+
var _a2, _b2;
|
|
306
|
+
const params = {
|
|
307
|
+
[returnTo]: urlConcat(event.url.origin, ROUTE_PATH_REDIRECT_LOGOUT),
|
|
308
|
+
client_id: config.oauth.clientId
|
|
309
|
+
};
|
|
310
|
+
const paramsStr = queryParamsCreate3(params);
|
|
311
|
+
(_b2 = (_a2 = config.logger) == null ? void 0 : _a2.debug) == null ? void 0 : _b2.call(_a2, "Pre logout redirect.", { params });
|
|
312
|
+
throw redirect4(302, `${config.oauth.logoutEndpoint}?${paramsStr}`);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// src/routes/routes.ts
|
|
319
|
+
var routeFactories = Object.freeze([
|
|
320
|
+
routeLoginFactory,
|
|
321
|
+
routeLogoutFactory,
|
|
322
|
+
routeRedirectLoginFactory,
|
|
323
|
+
routeRedirectLogoutFactory
|
|
324
|
+
]);
|
|
325
|
+
function routeByPathFactory(config) {
|
|
326
|
+
return new Map(
|
|
327
|
+
routeFactories.map((routeFactory) => routeFactory(config)).filter((route) => Boolean(route)).map((route) => [route.path, route])
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// src/errors.ts
|
|
332
|
+
var ArmorError = class extends Error {
|
|
333
|
+
};
|
|
334
|
+
var ArmorOpenIdConfigError = class extends ArmorError {
|
|
335
|
+
};
|
|
336
|
+
var ArmorAuthMissingError = class extends ArmorError {
|
|
337
|
+
};
|
|
338
|
+
var ArmorRefreshError = class extends ArmorError {
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// src/utils/refresh.ts
|
|
342
|
+
import { createRemoteJWKSet as createRemoteJWKSet2 } from "jose";
|
|
343
|
+
import { redirect as redirect5 } from "@sveltejs/kit";
|
|
344
|
+
function armorRefreshFactory(config) {
|
|
345
|
+
var _a, _b;
|
|
346
|
+
const refreshEndpoint = (_a = config.oauth.refreshEndpoint) != null ? _a : urlConcat(config.oauth.baseUrl, "oauth2/token");
|
|
347
|
+
const jwksUrl = new URL(
|
|
348
|
+
(_b = config.oauth.jwksEndpoint) != null ? _b : urlConcat(config.oauth.baseUrl, ".well-known/jwks.json")
|
|
349
|
+
);
|
|
350
|
+
const refresh = (fetch2, refreshToken) => __async(null, null, function* () {
|
|
351
|
+
var _a2, _b2, _c2, _d, _e;
|
|
352
|
+
const body = new URLSearchParams({
|
|
353
|
+
grant_type: "refresh_token",
|
|
354
|
+
client_id: config.oauth.clientId,
|
|
355
|
+
client_secret: config.oauth.clientSecret,
|
|
356
|
+
refresh_token: refreshToken
|
|
357
|
+
});
|
|
358
|
+
if (config.oauth.scope) {
|
|
359
|
+
body.set("scope", config.oauth.scope);
|
|
360
|
+
}
|
|
361
|
+
const response = yield fetch2(refreshEndpoint, {
|
|
362
|
+
method: "POST",
|
|
363
|
+
headers: {
|
|
364
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
365
|
+
Accept: "application/json"
|
|
366
|
+
},
|
|
367
|
+
body: body.toString()
|
|
368
|
+
});
|
|
369
|
+
if (!response.ok) {
|
|
370
|
+
const error = yield response.text();
|
|
371
|
+
throw new ArmorRefreshError(`Could not refresh token: ${error}`);
|
|
372
|
+
}
|
|
373
|
+
const json = yield response.json();
|
|
374
|
+
const newExchange = __spreadProps(__spreadValues({}, json), {
|
|
375
|
+
refresh_token: (_a2 = json.refresh_token) != null ? _a2 : refreshToken
|
|
376
|
+
});
|
|
377
|
+
(_c2 = (_b2 = config.logger) == null ? void 0 : _b2.debug) == null ? void 0 : _c2.call(_b2, "Exchange code for tokens.", { newExchange });
|
|
378
|
+
const jwks = createRemoteJWKSet2(jwksUrl);
|
|
379
|
+
const [idToken, accessToken] = yield Promise.all([
|
|
380
|
+
jwtVerifyIdToken(config, jwks, newExchange.id_token),
|
|
381
|
+
jwtVerifyAccessToken(config, jwks, newExchange.access_token)
|
|
382
|
+
]);
|
|
383
|
+
(_e = (_d = config.logger) == null ? void 0 : _d.debug) == null ? void 0 : _e.call(_d, "Extract and verify tokens.", {
|
|
384
|
+
idToken,
|
|
385
|
+
accessToken
|
|
386
|
+
});
|
|
387
|
+
const validTokens = exchangeToTokens(
|
|
388
|
+
newExchange,
|
|
389
|
+
idToken,
|
|
390
|
+
accessToken
|
|
391
|
+
);
|
|
392
|
+
return validTokens;
|
|
393
|
+
});
|
|
394
|
+
return {
|
|
395
|
+
refresh,
|
|
396
|
+
ensureValidToken(event, tokens, fn) {
|
|
397
|
+
return __async(this, null, function* () {
|
|
398
|
+
var _a2, _b2;
|
|
399
|
+
try {
|
|
400
|
+
let validTokens = tokens;
|
|
401
|
+
if (shouldRefresh(tokens)) {
|
|
402
|
+
(_b2 = (_a2 = config.logger) == null ? void 0 : _a2.debug) == null ? void 0 : _b2.call(_a2, "Tokens has expired. Refreshing...");
|
|
403
|
+
if (!tokens.exchange.refresh_token) {
|
|
404
|
+
throw redirect5(302, ROUTE_PATH_LOGIN);
|
|
405
|
+
}
|
|
406
|
+
validTokens = yield refresh(fetch, tokens.exchange.refresh_token);
|
|
407
|
+
yield config.session.login(event, validTokens);
|
|
408
|
+
}
|
|
409
|
+
return fn(validTokens);
|
|
410
|
+
} catch (error) {
|
|
411
|
+
if (error instanceof ArmorRefreshError) {
|
|
412
|
+
throw redirect5(302, ROUTE_PATH_LOGIN);
|
|
413
|
+
}
|
|
414
|
+
throw error;
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// src/session/cookie.ts
|
|
422
|
+
function cookieSessionGetTokens({
|
|
423
|
+
cookies
|
|
424
|
+
}) {
|
|
425
|
+
return cookies.get(COOKIE_TOKENS);
|
|
426
|
+
}
|
|
427
|
+
function cookieSessionLogin({ cookies }, tokens) {
|
|
428
|
+
cookieSet(cookies, COOKIE_TOKENS, tokens);
|
|
429
|
+
}
|
|
430
|
+
function cookieSessionLogout({ cookies }) {
|
|
431
|
+
cookieDelete(cookies, COOKIE_TOKENS);
|
|
432
|
+
}
|
|
433
|
+
function armorCookieSessionGet({ cookies }) {
|
|
434
|
+
const tokens = cookieGet(cookies, COOKIE_TOKENS);
|
|
435
|
+
if (!tokens) {
|
|
436
|
+
throw new ArmorAuthMissingError();
|
|
437
|
+
}
|
|
438
|
+
return tokens;
|
|
439
|
+
}
|
|
440
|
+
var armorCookieSession = {
|
|
441
|
+
getTokens: cookieSessionGetTokens,
|
|
442
|
+
login: cookieSessionLogin,
|
|
443
|
+
logout: cookieSessionLogout
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
// src/index.ts
|
|
447
|
+
function armor(config) {
|
|
448
|
+
const routeByPath = routeByPathFactory(config);
|
|
449
|
+
const refresh = armorRefreshFactory(config);
|
|
450
|
+
return __spreadProps(__spreadValues({}, refresh), {
|
|
451
|
+
handle(_0) {
|
|
452
|
+
return __async(this, arguments, function* ({ event, resolve }) {
|
|
453
|
+
var _a2, _b;
|
|
454
|
+
const route = routeByPath.get(event.url.pathname);
|
|
455
|
+
if (route) {
|
|
456
|
+
return route.handle({ event, resolve });
|
|
457
|
+
}
|
|
458
|
+
const tokens = yield config.session.getTokens(event);
|
|
459
|
+
if (!tokens) {
|
|
460
|
+
(_b = (_a2 = config.logger) == null ? void 0 : _a2.warning) == null ? void 0 : _b.call(
|
|
461
|
+
_a2,
|
|
462
|
+
"Could not find tokens. Redirecting to login."
|
|
463
|
+
);
|
|
464
|
+
throw redirect6(302, ROUTE_PATH_LOGIN);
|
|
465
|
+
}
|
|
466
|
+
return refresh.ensureValidToken(event, tokens, () => resolve(event));
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
function armorConfigFromOpenId(config, fetch2) {
|
|
472
|
+
return __async(this, null, function* () {
|
|
473
|
+
var _a;
|
|
474
|
+
const fetchToUse = fetch2 != null ? fetch2 : global.fetch;
|
|
475
|
+
const response = yield fetchToUse(config.oauth.openIdConfigEndpoint, {
|
|
476
|
+
headers: {
|
|
477
|
+
Accept: "application/json"
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
if (!response.ok) {
|
|
481
|
+
const text = yield response.text();
|
|
482
|
+
throw new ArmorOpenIdConfigError(text);
|
|
483
|
+
}
|
|
484
|
+
const body = yield response.json();
|
|
485
|
+
return __spreadProps(__spreadValues({}, config), {
|
|
486
|
+
oauth: __spreadProps(__spreadValues({}, config.oauth), {
|
|
487
|
+
tokenEndpoint: body.token_endpoint,
|
|
488
|
+
authorizeEndpoint: body.authorization_endpoint,
|
|
489
|
+
issuer: body.issuer,
|
|
490
|
+
jwksEndpoint: body.jwks_uri,
|
|
491
|
+
logoutEndpoint: (_a = body.end_session_endpoint) != null ? _a : void 0,
|
|
492
|
+
refreshEndpoint: body.token_endpoint
|
|
493
|
+
})
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
export {
|
|
498
|
+
armor,
|
|
499
|
+
armorConfigFromOpenId,
|
|
500
|
+
armorCookieSession,
|
|
501
|
+
armorCookieSessionGet,
|
|
502
|
+
armorRefreshFactory
|
|
503
|
+
};
|
|
504
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/routes/login.ts","../src/routes/redirect-login.ts","../src/utils/utils.ts","../src/utils/jwt.ts","../src/utils/cookie.ts","../src/utils/event.ts","../src/routes/logout.ts","../src/routes/redirect-logout.ts","../src/routes/routes.ts","../src/errors.ts","../src/utils/refresh.ts","../src/session/cookie.ts"],"sourcesContent":["import { redirect, type Handle } from \"@sveltejs/kit\";\nimport { ROUTE_PATH_LOGIN } from \"./routes/login\";\nimport type { ArmorConfig, ArmorOpenIdConfig, ArmorTokens } from \"./contracts\";\nimport { routeByPathFactory } from \"./routes/routes\";\nimport { ArmorOpenIdConfigError } from \"./errors\";\nimport { ArmorRefresh, armorRefreshFactory } from \"./utils/refresh\";\n\nexport type { ArmorConfig, ArmorTokens };\nexport { armorCookieSession, armorCookieSessionGet } from \"./session/cookie\";\nexport { armorRefreshFactory } from \"./utils/refresh\";\n\nexport interface Armor extends ArmorRefresh {\n\treadonly handle: Handle;\n}\n\nexport function armor(config: ArmorConfig): Armor {\n\tconst routeByPath = routeByPathFactory(config);\n\tconst refresh = armorRefreshFactory(config);\n\n\treturn {\n\t\t...refresh,\n\t\tasync handle({ event, resolve }) {\n\t\t\tconst route = routeByPath.get(event.url.pathname);\n\n\t\t\tif (route) {\n\t\t\t\treturn route.handle({ event, resolve });\n\t\t\t}\n\n\t\t\tconst tokens = await config.session.getTokens(event);\n\n\t\t\tif (!tokens) {\n\t\t\t\tconfig.logger?.warning?.(\n\t\t\t\t\t\"Could not find tokens. Redirecting to login.\",\n\t\t\t\t);\n\t\t\t\tthrow redirect(302, ROUTE_PATH_LOGIN);\n\t\t\t}\n\n\t\t\treturn refresh.ensureValidToken(event, tokens, () => resolve(event));\n\t\t},\n\t};\n}\n\n/**\n * Some IdP's expose a /.well-known/openid-configuration that specifies how to configure.\n * Use that to create your config.\n * @param config\n * @param fetch\n */\nexport async function armorConfigFromOpenId(\n\tconfig: ArmorOpenIdConfig,\n\tfetch?: typeof global.fetch,\n): Promise<ArmorConfig> {\n\tconst fetchToUse = fetch ?? global.fetch;\n\n\tconst response = await fetchToUse(config.oauth.openIdConfigEndpoint, {\n\t\theaders: {\n\t\t\tAccept: \"application/json\",\n\t\t},\n\t});\n\n\tif (!response.ok) {\n\t\tconst text = await response.text();\n\t\tthrow new ArmorOpenIdConfigError(text);\n\t}\n\n\tconst body = await response.json();\n\n\treturn {\n\t\t...config,\n\t\toauth: {\n\t\t\t...config.oauth,\n\t\t\ttokenEndpoint: body.token_endpoint,\n\t\t\tauthorizeEndpoint: body.authorization_endpoint,\n\t\t\tissuer: body.issuer,\n\t\t\tjwksEndpoint: body.jwks_uri,\n\t\t\tlogoutEndpoint: body.end_session_endpoint ?? undefined,\n\t\t\trefreshEndpoint: body.token_endpoint,\n\t\t},\n\t};\n}\n","import { redirect } from \"@sveltejs/kit\";\nimport type { ArmorConfig } from \"../contracts\";\nimport { queryParamsCreate } from \"@nekm/core\";\nimport { ROUTE_PATH_REDIRECT_LOGIN } from \"./redirect-login\";\nimport { randomUUID } from \"node:crypto\";\nimport type { RouteFactory } from \"./routes\";\nimport { COOKIE_STATE, cookieSet } from \"../utils/cookie\";\nimport { urlConcat } from \"../utils/utils\";\nimport { ARMOR_LOGIN } from \"../browser\";\n\nexport const ROUTE_PATH_LOGIN = ARMOR_LOGIN;\n\nexport const routeLoginFactory: RouteFactory = (config: ArmorConfig) => {\n\tconst authorizeEndpoint =\n\t\tconfig.oauth.authorizeEndpoint ??\n\t\turlConcat(config.oauth.baseUrl, \"oauth2/authorize\");\n\n\tconst scope = config.oauth.scope ?? \"openid profile email\";\n\n\treturn {\n\t\tpath: ROUTE_PATH_LOGIN,\n\t\tasync handle({ event }) {\n\t\t\tconst state = randomUUID();\n\t\t\tcookieSet(event.cookies, COOKIE_STATE, state);\n\n\t\t\tconst params = {\n\t\t\t\tclient_id: config.oauth.clientId,\n\t\t\t\tresponse_type: \"code\",\n\t\t\t\tredirect_uri: urlConcat(event.url.origin, ROUTE_PATH_REDIRECT_LOGIN),\n\t\t\t\tstate,\n\t\t\t\tscope,\n\t\t\t\taudience: config.oauth.audience,\n\t\t\t};\n\n\t\t\tconst paramsStr = queryParamsCreate(params);\n\n\t\t\tconfig.logger?.debug?.(\"Pre login redirect.\", { params, state });\n\n\t\t\tthrow redirect(302, `${authorizeEndpoint}?${paramsStr}`);\n\t\t},\n\t};\n};\n","import { redirect } from \"@sveltejs/kit\";\nimport type {\n\tArmorConfig,\n\tArmorIdToken,\n\tArmorTokenExchange,\n} from \"../contracts\";\nimport { queryParamsCreate, throwIfUndefined } from \"@nekm/core\";\nimport { createRemoteJWKSet } from \"jose\";\nimport type { RouteFactory } from \"./routes\";\nimport { urlConcat, isTokenExchange, exchangeToTokens } from \"../utils/utils\";\nimport { jwtVerifyAccessToken, jwtVerifyIdToken } from \"../utils/jwt\";\nimport { eventStateValid } from \"../utils/event\";\nimport { ROUTE_PATH_LOGIN } from \"./login\";\n\nexport const ROUTE_PATH_REDIRECT_LOGIN = \"/_armor/redirect/login\";\n\nexport const routeRedirectLoginFactory: RouteFactory = (\n\tconfig: ArmorConfig,\n) => {\n\tconst jwksUrl = new URL(\n\t\tconfig.oauth.jwksEndpoint ??\n\t\t\turlConcat(config.oauth.baseUrl, \".well-known/jwks.json\"),\n\t);\n\n\tconst tokenUrl =\n\t\tconfig.oauth.tokenEndpoint ??\n\t\turlConcat(config.oauth.baseUrl, \"oauth2/token\");\n\n\tconst scope = config.oauth.scope ?? \"openid profile email\";\n\n\tasync function exchangeCodeForToken(\n\t\tfetch: typeof global.fetch,\n\t\torigin: string,\n\t\tcode: string,\n\t): Promise<ArmorTokenExchange> {\n\t\tconst params: Record<string, string> = {\n\t\t\tgrant_type: \"authorization_code\",\n\t\t\tclient_id: config.oauth.clientId,\n\t\t\tclient_secret: config.oauth.clientSecret,\n\t\t\tcode,\n\t\t\tredirect_uri: urlConcat(origin, ROUTE_PATH_REDIRECT_LOGIN),\n\t\t\tscope,\n\t\t};\n\n\t\tif (config.oauth.audience) {\n\t\t\tparams.audience = config.oauth.audience;\n\t\t}\n\n\t\tconst response = await fetch(tokenUrl, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\tAccept: \"application/json\",\n\t\t\t},\n\t\t\tbody: new URLSearchParams(params).toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst error = await response.text();\n\t\t\tthrow new Error(`Token exchange failed: ${error}`);\n\t\t}\n\n\t\tconst token = await response.json();\n\n\t\tif (!isTokenExchange(token)) {\n\t\t\tthrow new Error(\"Response is not a valid token exchange.\");\n\t\t}\n\n\t\treturn token;\n\t}\n\n\treturn {\n\t\tpath: ROUTE_PATH_REDIRECT_LOGIN,\n\t\tasync handle({ event }) {\n\t\t\tconfig.logger?.debug?.(\"Handle login redirect callback.\");\n\n\t\t\t// If the states does not match, redirect the user to login.\n\t\t\t// People bookmark the wrong pages all the time. Lets not\n\t\t\t// do a throw here.\n\t\t\tif (!eventStateValid(event)) {\n\t\t\t\tconfig.logger?.warning?.(\"State missmatch\");\n\t\t\t\tthrow redirect(302, ROUTE_PATH_LOGIN);\n\t\t\t}\n\n\t\t\tconst error = event.url.searchParams.get(\"error\") ?? undefined;\n\n\t\t\tif (error) {\n\t\t\t\tconst error_description =\n\t\t\t\t\tevent.url.searchParams.get(\"error_description\") ?? undefined;\n\n\t\t\t\tconfig.logger?.error?.(\"Login returned error.\", {\n\t\t\t\t\terror,\n\t\t\t\t\terrorDescription: error_description,\n\t\t\t\t});\n\n\t\t\t\tif (!config.oauth.errorLoginRedirectPath) {\n\t\t\t\t\treturn new Response(`${error}\\n${error_description}`.trimEnd(), {\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t\"Content-Type\": \"text/plain\",\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tconst errorParams = queryParamsCreate({ error, error_description });\n\t\t\t\tthrow redirect(\n\t\t\t\t\t302,\n\t\t\t\t\t`${config.oauth.errorLoginRedirectPath}?${errorParams}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst code = event.url.searchParams.get(\"code\") ?? undefined;\n\t\t\tconfig.logger?.debug?.(\"Get code from query params.\", { code });\n\t\t\tthrowIfUndefined(code);\n\n\t\t\tconst exchange = await exchangeCodeForToken(\n\t\t\t\tevent.fetch,\n\t\t\t\tevent.url.origin,\n\t\t\t\tcode,\n\t\t\t);\n\n\t\t\tconfig.logger?.debug?.(\"Exchange code for tokens.\", { exchange });\n\n\t\t\tconst jwks = createRemoteJWKSet(jwksUrl);\n\n\t\t\tconst [idToken, accessToken] = await Promise.all([\n\t\t\t\tjwtVerifyIdToken(config, jwks, exchange.id_token),\n\t\t\t\tjwtVerifyAccessToken(config, jwks, exchange.access_token),\n\t\t\t]);\n\n\t\t\tconfig.logger?.debug?.(\"Extract and verify tokens.\", {\n\t\t\t\tidToken,\n\t\t\t\taccessToken,\n\t\t\t});\n\n\t\t\tawait config.session.login(\n\t\t\t\tevent,\n\t\t\t\texchangeToTokens(exchange, idToken as ArmorIdToken, accessToken),\n\t\t\t);\n\n\t\t\tthrow redirect(302, \"/\");\n\t\t},\n\t};\n};\n","import { strTrimEnd, strTrimStart } from \"@nekm/core\";\nimport type {\n\tArmorAccessToken,\n\tArmorIdToken,\n\tArmorTokenExchange,\n\tArmorTokens,\n} from \"../contracts\";\n\nexport function urlConcat(origin: string, path: string): string {\n\treturn [strTrimEnd(origin, \"/\"), strTrimStart(path, \"/\")].join(\"/\");\n}\n\nexport function isTokenExchange(value: unknown): value is ArmorTokenExchange {\n\tif (typeof value !== \"object\" || value === null) return false;\n\n\tconst obj = value as Record<string, unknown>;\n\n\treturn (\n\t\ttypeof obj.access_token === \"string\" &&\n\t\tobj.token_type === \"Bearer\" &&\n\t\ttypeof obj.expires_in === \"number\" &&\n\t\t// Optional fields\n\t\t(typeof obj.id_token === \"string\" || obj.id_token === undefined) &&\n\t\t(typeof obj.refresh_token === \"string\" ||\n\t\t\tobj.refresh_token === undefined) &&\n\t\t(typeof obj.scope === \"string\" || obj.scope === undefined)\n\t);\n}\n\nconst MINUTES_MS = 60 * 1000;\n\nexport function shouldRefresh(\n\ttokens: Pick<ArmorTokens, \"idToken\" | \"accessToken\">,\n): boolean {\n\tconst idExpiry = tokens.idToken.exp * 1000;\n\n\tconst accessExpiry =\n\t\ttypeof tokens.accessToken !== \"string\" &&\n\t\ttokens.accessToken.exp !== undefined\n\t\t\t? tokens.accessToken.exp * 1000\n\t\t\t: Infinity;\n\n\treturn Math.min(idExpiry, accessExpiry) < Date.now() + 5 * MINUTES_MS;\n}\n\nexport function createExpiresAt(seconds: number): Date {\n\tconst now = new Date();\n\tnow.setSeconds(now.getSeconds() + seconds);\n\treturn now;\n}\n\nexport function exchangeToTokens(\n\texchange: ArmorTokenExchange,\n\tidToken: ArmorIdToken,\n\taccessToken?: ArmorAccessToken,\n): ArmorTokens {\n\treturn {\n\t\texchange,\n\t\tidToken: idToken as ArmorIdToken,\n\t\t// Generally, IdP's require an audience to get a JWT\n\t\t// access token. Most cases, this doesn't matter.\n\t\taccessToken: accessToken ?? exchange.access_token,\n\t\texpiresAt: createExpiresAt(exchange.expires_in),\n\t};\n}\n","import { ArmorConfig } from \"../contracts\";\nimport { JWTPayload, jwtVerify, JWTVerifyGetKey, JWTVerifyOptions } from \"jose\";\nimport { throwIfUndefined } from \"@nekm/core\";\n\nfunction jwtIsCompactJwt(token: string): boolean {\n\t// Must be three base64url segments\n\tconst parts = token.trim().split(\".\");\n\treturn parts.length === 3 && parts.every((p) => p.length > 0);\n}\n\nexport function jwtVerifyIdToken(\n\tconfig: ArmorConfig,\n\tjwks: JWTVerifyGetKey,\n\tidToken: string,\n): Promise<JWTPayload> {\n\tconst payload = jwtVerifyToken(\n\t\tjwks,\n\t\t{\n\t\t\tissuer: config.oauth.issuer,\n\t\t\taudience: config.oauth.clientId,\n\t\t},\n\t\tidToken,\n\t);\n\tthrowIfUndefined(payload);\n\t// @ts-expect-error We're already verifying non-null above.\n\treturn payload;\n}\n\nexport function jwtVerifyAccessToken(\n\tconfig: ArmorConfig,\n\tjwks: JWTVerifyGetKey,\n\taccessToken: string,\n): Promise<JWTPayload | undefined> {\n\tconst opts: JWTVerifyOptions = { issuer: config.oauth.issuer };\n\n\tif (config.oauth.audience) {\n\t\topts.audience = config.oauth.audience;\n\t}\n\n\treturn jwtVerifyToken(jwks, opts, accessToken);\n}\n\nfunction isInvalidCompactJwt(error: unknown): boolean {\n\treturn Boolean(\n\t\ttypeof error === \"object\" &&\n\t\terror &&\n\t\t\"message\" in error &&\n\t\ttypeof error.message === \"string\" &&\n\t\t/invalid compact jws/gi.test(error.message),\n\t);\n}\n\nasync function jwtVerifyToken(\n\tjwks: JWTVerifyGetKey,\n\topts: JWTVerifyOptions,\n\ttoken: string,\n): Promise<JWTPayload | undefined> {\n\ttry {\n\t\tif (!jwtIsCompactJwt(token)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst { payload } = await jwtVerify(token, jwks, opts);\n\t\treturn payload;\n\t} catch (error) {\n\t\tif (isInvalidCompactJwt(error)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tthrow error;\n\t}\n}\n","import { Cookies } from \"@sveltejs/kit\";\n\nexport const COOKIE_TOKENS = \"tokens\" as const;\nexport const COOKIE_STATE = \"state\" as const;\n\nconst cookieDeleteOptions = Object.freeze({ path: \"/\" });\n\nconst cookieSetOptions = Object.freeze({\n\t...cookieDeleteOptions,\n\thttpOnly: true,\n\tsecure: true,\n\tsameSite: \"lax\",\n\tmaxAge: 1800, // 30 minutes\n});\n\nexport function cookieSet(\n\tcookies: Cookies,\n\tkey: string,\n\tvalue: string | object,\n) {\n\tcookies.set(key, JSON.stringify(value), cookieSetOptions);\n}\n\nexport function cookieGetAndDelete<T>(\n\tcookies: Cookies,\n\tkey: string,\n): T | undefined {\n\tconst value = cookieGet<T>(cookies, key);\n\n\tif (value) {\n\t\tcookies.delete(key, cookieDeleteOptions);\n\t}\n\n\treturn value;\n}\n\nexport function cookieGet<T>(cookies: Cookies, key: string): T | undefined {\n\tconst value = cookies.get(key);\n\n\treturn !value ? undefined : JSON.parse(value);\n}\n\nexport function cookieDelete(cookies: Cookies, key: string): void {\n\tcookies.delete(key, cookieDeleteOptions);\n}\n","import { RequestEvent } from \"@sveltejs/kit\";\nimport { COOKIE_STATE, cookieGetAndDelete } from \"./cookie\";\n\nexport function eventStateValid(event: RequestEvent): boolean {\n\tconst state = event.url.searchParams.get(\"state\") ?? undefined;\n\tconst stateCookie = cookieGetAndDelete(event.cookies, COOKIE_STATE);\n\n\treturn state !== stateCookie;\n}\n","import { redirect } from \"@sveltejs/kit\";\nimport type { ArmorConfig } from \"../contracts\";\nimport { queryParamsCreate } from \"@nekm/core\";\nimport { ROUTE_PATH_REDIRECT_LOGOUT } from \"./redirect-logout\";\nimport type { RouteFactory } from \"./routes\";\nimport { urlConcat } from \"../utils/utils\";\nimport { ARMOR_LOGOUT } from \"../browser\";\n\nexport const ROUTE_PATH_LOGOUT = ARMOR_LOGOUT;\n\nexport const routeLogoutFactory: RouteFactory = (config: ArmorConfig) => {\n\t// Check if the oauth provider supports a logout path.\n\tif (!config.oauth.logoutEndpoint) {\n\t\treturn undefined;\n\t}\n\n\tconst returnTo = config.oauth.logoutReturnToParam ?? \"logout_uri\";\n\n\treturn {\n\t\tpath: ROUTE_PATH_LOGOUT,\n\t\tasync handle({ event }) {\n\t\t\tconst params = {\n\t\t\t\t[returnTo]: urlConcat(event.url.origin, ROUTE_PATH_REDIRECT_LOGOUT),\n\t\t\t\tclient_id: config.oauth.clientId,\n\t\t\t};\n\n\t\t\tconst paramsStr = queryParamsCreate(params);\n\n\t\t\tconfig.logger?.debug?.(\"Pre logout redirect.\", { params });\n\n\t\t\tthrow redirect(302, `${config.oauth.logoutEndpoint}?${paramsStr}`);\n\t\t},\n\t};\n};\n","import { redirect } from \"@sveltejs/kit\";\nimport type { ArmorConfig } from \"../contracts\";\nimport type { RouteFactory } from \"./routes\";\n\nexport const ROUTE_PATH_REDIRECT_LOGOUT = \"/_armor/redirect/logout\";\n\nexport const routeRedirectLogoutFactory: RouteFactory = (\n\tconfig: ArmorConfig,\n) => {\n\t// Check if the oauth provider supports a logout path.\n\tif (!config.oauth.logoutEndpoint) {\n\t\treturn undefined;\n\t}\n\n\treturn {\n\t\tpath: ROUTE_PATH_REDIRECT_LOGOUT,\n\t\tasync handle({ event }) {\n\t\t\tconfig.logger?.debug?.(\"Handle logout redirect callback.\");\n\n\t\t\tawait config.session.logout(event);\n\n\t\t\tthrow redirect(302, \"/\");\n\t\t},\n\t};\n};\n","import type { Handle } from \"@sveltejs/kit\";\nimport type { ArmorConfig } from \"../contracts\";\nimport { routeLoginFactory } from \"./login\";\nimport { routeLogoutFactory } from \"./logout\";\nimport { routeRedirectLogoutFactory } from \"./redirect-logout\";\nimport { routeRedirectLoginFactory } from \"./redirect-login\";\n\nexport interface Route {\n\treadonly path: string;\n\treadonly handle: Handle;\n}\n\nexport type RouteFactory = (config: ArmorConfig) => Route | undefined;\n\nconst routeFactories = Object.freeze([\n\trouteLoginFactory,\n\trouteLogoutFactory,\n\trouteRedirectLoginFactory,\n\trouteRedirectLogoutFactory,\n]);\n\nexport function routeByPathFactory(config: ArmorConfig): Map<string, Route> {\n\t// @ts-expect-error Incorrect typing error.\n\treturn new Map(\n\t\trouteFactories\n\t\t\t.map((routeFactory) => routeFactory(config))\n\t\t\t.filter((route) => Boolean(route))\n\t\t\t// @ts-expect-error Incorrect typing error.\n\t\t\t.map((route) => [route.path, route]),\n\t);\n}\n","export class ArmorError extends Error {}\nexport class ArmorOpenIdConfigError extends ArmorError {}\nexport class ArmorAuthMissingError extends ArmorError {}\nexport class ArmorRefreshError extends ArmorError {}\n","import { createRemoteJWKSet } from \"jose\";\nimport {\n\tArmorConfig,\n\tArmorIdToken,\n\tArmorTokenExchange,\n\tArmorTokens,\n} from \"../contracts\";\nimport { ArmorRefreshError } from \"../errors\";\nimport { exchangeToTokens, shouldRefresh, urlConcat } from \"./utils\";\nimport { jwtVerifyAccessToken, jwtVerifyIdToken } from \"./jwt\";\nimport { redirect, RequestEvent } from \"@sveltejs/kit\";\nimport { ROUTE_PATH_LOGIN } from \"../routes/login\";\n\nexport interface ArmorRefresh {\n\treadonly refresh: (\n\t\tfetch: typeof global.fetch,\n\t\trefreshToken: string,\n\t) => Promise<ArmorTokens>;\n\treadonly ensureValidToken: <T>(\n\t\tevent: RequestEvent,\n\t\ttokens: ArmorTokens,\n\t\tfn: (tokens: ArmorTokens) => T | Promise<T>,\n\t) => Promise<T>;\n}\n\nexport function armorRefreshFactory(config: ArmorConfig): ArmorRefresh {\n\tconst refreshEndpoint =\n\t\tconfig.oauth.refreshEndpoint ??\n\t\turlConcat(config.oauth.baseUrl, \"oauth2/token\");\n\n\tconst jwksUrl = new URL(\n\t\tconfig.oauth.jwksEndpoint ??\n\t\t\turlConcat(config.oauth.baseUrl, \".well-known/jwks.json\"),\n\t);\n\n\tconst refresh = async (\n\t\tfetch: typeof global.fetch,\n\t\trefreshToken: string,\n\t): Promise<ArmorTokens> => {\n\t\tconst body = new URLSearchParams({\n\t\t\tgrant_type: \"refresh_token\",\n\t\t\tclient_id: config.oauth.clientId,\n\t\t\tclient_secret: config.oauth.clientSecret,\n\t\t\trefresh_token: refreshToken,\n\t\t});\n\n\t\tif (config.oauth.scope) {\n\t\t\tbody.set(\"scope\", config.oauth.scope);\n\t\t}\n\n\t\tconst response = await fetch(refreshEndpoint, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: {\n\t\t\t\t\"Content-Type\": \"application/x-www-form-urlencoded\",\n\t\t\t\tAccept: \"application/json\",\n\t\t\t},\n\t\t\tbody: body.toString(),\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tconst error = await response.text();\n\t\t\tthrow new ArmorRefreshError(`Could not refresh token: ${error}`);\n\t\t}\n\n\t\tconst json: ArmorTokenExchange = await response.json();\n\n\t\tconst newExchange = {\n\t\t\t...json,\n\t\t\trefresh_token: json.refresh_token ?? refreshToken,\n\t\t};\n\n\t\tconfig.logger?.debug?.(\"Exchange code for tokens.\", { newExchange });\n\n\t\tconst jwks = createRemoteJWKSet(jwksUrl);\n\n\t\tconst [idToken, accessToken] = await Promise.all([\n\t\t\tjwtVerifyIdToken(config, jwks, newExchange.id_token),\n\t\t\tjwtVerifyAccessToken(config, jwks, newExchange.access_token),\n\t\t]);\n\n\t\tconfig.logger?.debug?.(\"Extract and verify tokens.\", {\n\t\t\tidToken,\n\t\t\taccessToken,\n\t\t});\n\n\t\tconst validTokens = exchangeToTokens(\n\t\t\tnewExchange,\n\t\t\tidToken as ArmorIdToken,\n\t\t\taccessToken,\n\t\t);\n\n\t\treturn validTokens;\n\t};\n\n\treturn {\n\t\trefresh,\n\t\tasync ensureValidToken<T>(\n\t\t\tevent: RequestEvent,\n\t\t\ttokens: ArmorTokens,\n\t\t\tfn: (tokens: ArmorTokens) => T | Promise<T>,\n\t\t): Promise<T> {\n\t\t\ttry {\n\t\t\t\tlet validTokens = tokens;\n\n\t\t\t\tif (shouldRefresh(tokens)) {\n\t\t\t\t\tconfig.logger?.debug?.(\"Tokens has expired. Refreshing...\");\n\n\t\t\t\t\tif (!tokens.exchange.refresh_token) {\n\t\t\t\t\t\tthrow redirect(302, ROUTE_PATH_LOGIN);\n\t\t\t\t\t}\n\n\t\t\t\t\tvalidTokens = await refresh(fetch, tokens.exchange.refresh_token);\n\n\t\t\t\t\tawait config.session.login(event, validTokens);\n\t\t\t\t}\n\n\t\t\t\treturn fn(validTokens);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof ArmorRefreshError) {\n\t\t\t\t\tthrow redirect(302, ROUTE_PATH_LOGIN);\n\t\t\t\t}\n\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t};\n}\n","import { RequestEvent } from \"@sveltejs/kit\";\nimport {\n\tCOOKIE_TOKENS,\n\tcookieDelete,\n\tcookieGet,\n\tcookieSet,\n} from \"../utils/cookie\";\nimport { ArmorConfig, ArmorTokens } from \"../contracts\";\nimport { ArmorAuthMissingError } from \"../errors\";\n\nfunction cookieSessionGetTokens({\n\tcookies,\n}: RequestEvent): ArmorTokens | undefined {\n\treturn cookies.get(COOKIE_TOKENS) as ArmorTokens | undefined;\n}\n\nexport function cookieSessionLogin(\n\t{ cookies }: RequestEvent,\n\ttokens: ArmorTokens,\n): void {\n\tcookieSet(cookies, COOKIE_TOKENS, tokens);\n}\n\nfunction cookieSessionLogout({ cookies }: RequestEvent): void {\n\tcookieDelete(cookies, COOKIE_TOKENS);\n}\n\nexport function armorCookieSessionGet({ cookies }: RequestEvent): ArmorTokens {\n\tconst tokens = cookieGet<ArmorTokens>(cookies, COOKIE_TOKENS);\n\n\tif (!tokens) {\n\t\tthrow new ArmorAuthMissingError();\n\t}\n\n\treturn tokens;\n}\n\nexport const armorCookieSession: ArmorConfig[\"session\"] = {\n\tgetTokens: cookieSessionGetTokens,\n\tlogin: cookieSessionLogin,\n\tlogout: cookieSessionLogout,\n};\n"],"mappings":"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","names":["redirect","redirect","queryParamsCreate","throwIfUndefined","fetch","_a","_b","_c","_d","throwIfUndefined","_a","_b","queryParamsCreate","redirect","redirect","queryParamsCreate","redirect","_a","redirect","_a","_b","queryParamsCreate","redirect","createRemoteJWKSet","redirect","fetch","_a","_b","_c","createRemoteJWKSet","redirect","_a","redirect","fetch"]}
|
package/dist/utils/event.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { RequestEvent } from "@sveltejs/kit";
|
|
2
|
-
export declare function
|
|
2
|
+
export declare function eventStateValid(event: RequestEvent): boolean;
|
package/package.json
CHANGED
|
@@ -1,28 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nekm/sveltekit-armor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Zero-config OAuth protection for SvelteKit",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"source": "./src/index.ts",
|
|
7
6
|
"types": "./dist/index.d.ts",
|
|
8
7
|
"exports": {
|
|
9
8
|
".": {
|
|
10
|
-
"
|
|
11
|
-
"import": "./dist/index.esm.js",
|
|
9
|
+
"import": "./dist/index.mjs",
|
|
12
10
|
"types": "./dist/index.d.ts",
|
|
13
11
|
"default": "./dist/index.js"
|
|
14
12
|
},
|
|
15
13
|
"./browser": {
|
|
16
|
-
"
|
|
17
|
-
"import": "./dist/browser/index.esm.js",
|
|
14
|
+
"import": "./dist/browser/index.mjs",
|
|
18
15
|
"types": "./dist/browser/index.d.ts",
|
|
19
16
|
"default": "./dist/browser/index.js"
|
|
20
17
|
}
|
|
21
18
|
},
|
|
22
19
|
"main": "./dist/index.js",
|
|
23
|
-
"module": "./dist/index.
|
|
20
|
+
"module": "./dist/index.mjs",
|
|
24
21
|
"scripts": {
|
|
25
|
-
"build": "
|
|
22
|
+
"build": "tsup && tsc --declaration --emitDeclarationOnly",
|
|
26
23
|
"test": "vitest run",
|
|
27
24
|
"format": "eslint --fix --max-warnings=0 --cache src && prettier --log-level warn --write src"
|
|
28
25
|
},
|
|
@@ -68,19 +65,19 @@
|
|
|
68
65
|
"sideEffects": false,
|
|
69
66
|
"devDependencies": {
|
|
70
67
|
"@eslint/js": "^10.0.1",
|
|
71
|
-
"@sveltejs/kit": "^2.
|
|
68
|
+
"@sveltejs/kit": "^2.56.1",
|
|
72
69
|
"@tsconfig/recommended": "^1.0.13",
|
|
73
70
|
"@types/node": "^24",
|
|
74
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
75
|
-
"@typescript-eslint/parser": "^8.
|
|
71
|
+
"@typescript-eslint/eslint-plugin": "^8.58.0",
|
|
72
|
+
"@typescript-eslint/parser": "^8.58.0",
|
|
76
73
|
"eslint": "^10",
|
|
77
74
|
"globals": "^17.4.0",
|
|
78
|
-
"microbundle": "^0.15.1",
|
|
79
75
|
"prettier": "^3.8.1",
|
|
80
|
-
"ts-jest": "^29.4.
|
|
76
|
+
"ts-jest": "^29.4.9",
|
|
81
77
|
"ts-node": "^10.9.2",
|
|
82
|
-
"
|
|
83
|
-
"
|
|
78
|
+
"tsup": "^8",
|
|
79
|
+
"typescript": "^6.0.2",
|
|
80
|
+
"vitest": "^4.1.3"
|
|
84
81
|
},
|
|
85
82
|
"dependencies": {
|
|
86
83
|
"@nekm/core": "^1",
|
package/src/errors.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export class ArmorError extends Error {}
|
|
2
2
|
export class ArmorOpenIdConfigError extends ArmorError {}
|
|
3
|
-
export class ArmorInvalidStateError extends ArmorError {}
|
|
4
3
|
export class ArmorAuthMissingError extends ArmorError {}
|
|
5
4
|
export class ArmorRefreshError extends ArmorError {}
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type { ArmorConfig, ArmorTokens };
|
|
|
9
9
|
export { armorCookieSession, armorCookieSessionGet } from "./session/cookie";
|
|
10
10
|
export { armorRefreshFactory } from "./utils/refresh";
|
|
11
11
|
|
|
12
|
-
interface Armor extends ArmorRefresh {
|
|
12
|
+
export interface Armor extends ArmorRefresh {
|
|
13
13
|
readonly handle: Handle;
|
|
14
14
|
}
|
|
15
15
|
|