Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04f00993db | ||
|
|
81239dc6ab |
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@resuely/astro-oidc-rp",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@resuely/astro-oidc-rp",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jose": "^5.0.0"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@resuely/astro-oidc-rp",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"description": "Astro integration providing OIDC relying-party routes, middleware, and types.",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
@@ -21,6 +21,11 @@ function escapeRegex(str: string): string {
|
||||
}
|
||||
|
||||
function globToRegExp(pattern: string): RegExp {
|
||||
if (pattern.endsWith("/*")) {
|
||||
const base = pattern.slice(0, -2);
|
||||
const baseEscaped = escapeRegex(base);
|
||||
return new RegExp(`^${baseEscaped}(?:/.*)?$`);
|
||||
}
|
||||
const escaped = pattern.split("*").map(escapeRegex).join(".*");
|
||||
return new RegExp(`^${escaped}$`);
|
||||
}
|
||||
@@ -38,12 +43,17 @@ export const onRequest: MiddlewareHandler = async (context, next) => {
|
||||
|
||||
locals.user = null;
|
||||
if (token) {
|
||||
const res = await verifyAndDecode<{ sub: string; email?: string }>(
|
||||
token,
|
||||
options.cookie.signingSecret,
|
||||
);
|
||||
const res = await verifyAndDecode<{
|
||||
sub: string;
|
||||
email?: string;
|
||||
firstName?: string;
|
||||
}>(token, options.cookie.signingSecret);
|
||||
if (res.valid && res.payload && typeof res.payload.sub === "string") {
|
||||
locals.user = { sub: res.payload.sub, email: res.payload.email };
|
||||
locals.user = {
|
||||
sub: res.payload.sub,
|
||||
email: res.payload.email,
|
||||
firstName: res.payload.firstName,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -127,9 +127,13 @@ export async function GET(ctx: APIContext) {
|
||||
audience: options.clientId,
|
||||
});
|
||||
|
||||
const givenName =
|
||||
typeof payload["given_name"] === "string" ? payload["given_name"] : undefined;
|
||||
|
||||
const session = {
|
||||
sub: String(payload.sub),
|
||||
email: typeof payload.email === "string" ? payload.email : undefined,
|
||||
firstName: givenName,
|
||||
};
|
||||
const signed = await signPayload(session, options.cookie.signingSecret);
|
||||
|
||||
|
||||
@@ -42,6 +42,39 @@ function safeReturnTo(reqUrl: URL, returnTo: string | undefined): string {
|
||||
}
|
||||
}
|
||||
|
||||
function escapeRegex(str: string): string {
|
||||
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
}
|
||||
|
||||
function globToRegExp(pattern: string): RegExp {
|
||||
if (pattern.endsWith("/*")) {
|
||||
const base = pattern.slice(0, -2);
|
||||
const baseEscaped = escapeRegex(base);
|
||||
return new RegExp(`^${baseEscaped}(?:/.*)?$`);
|
||||
}
|
||||
const escaped = pattern.split("*").map(escapeRegex).join(".*");
|
||||
return new RegExp(`^${escaped}$`);
|
||||
}
|
||||
|
||||
function isProtected(pathname: string, patterns: string[]): boolean {
|
||||
return patterns.some((p) => globToRegExp(p).test(pathname));
|
||||
}
|
||||
|
||||
function finalizePostLogoutReturnTo(
|
||||
options: ReturnType<typeof getOptions>,
|
||||
reqUrl: URL,
|
||||
returnTo: string | undefined,
|
||||
): string {
|
||||
const safe = safeReturnTo(reqUrl, returnTo);
|
||||
try {
|
||||
const u = new URL(safe, reqUrl);
|
||||
if (isProtected(u.pathname, options.protected)) return new URL("/", reqUrl).toString();
|
||||
return u.toString();
|
||||
} catch {
|
||||
return new URL("/", reqUrl).toString();
|
||||
}
|
||||
}
|
||||
|
||||
function inferPostLogoutRedirectUri(
|
||||
options: ReturnType<typeof getOptions>,
|
||||
reqUrl: URL,
|
||||
@@ -88,7 +121,9 @@ export async function GET(ctx: APIContext) {
|
||||
const parsed = cookie ? parseLogoutCookie(cookie) : null;
|
||||
|
||||
const ok = parsed && parsed.state === state;
|
||||
const location = ok ? safeReturnTo(url, parsed.returnTo) : "/";
|
||||
const location = ok
|
||||
? finalizePostLogoutReturnTo(options, url, parsed.returnTo)
|
||||
: "/";
|
||||
|
||||
const headers = new Headers();
|
||||
headers.set("Location", location);
|
||||
@@ -115,7 +150,11 @@ export async function GET(ctx: APIContext) {
|
||||
disco.end_session_endpoint || new URL("/logout", options.issuer).toString();
|
||||
|
||||
const state = generateState();
|
||||
const returnTo = url.searchParams.get("return_to") || undefined;
|
||||
const returnTo = finalizePostLogoutReturnTo(
|
||||
options,
|
||||
url,
|
||||
url.searchParams.get("return_to") || undefined,
|
||||
);
|
||||
const cookieValue = JSON.stringify({ state, returnTo });
|
||||
const setLogout = serializeCookie(logoutCookieName, cookieValue, {
|
||||
path: options.cookie.path,
|
||||
|
||||
2
types/astro.locals.d.ts
vendored
2
types/astro.locals.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
declare global {
|
||||
namespace App {
|
||||
interface Locals {
|
||||
user?: { sub: string; email?: string } | null;
|
||||
user?: { sub: string; email?: string; firstName?: string } | null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user