Redirect to initial url after authentication (#893)

pull/897/head
MrMugame 2024-06-22 12:45:23 +02:00 committed by GitHub
parent bd3e91ba65
commit b6d95ec632
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 168 additions and 130 deletions

View File

@ -10,5 +10,5 @@ export async function cleanCommand() {
}
await editor.flashNotification("Now wiping all state and logging out...");
await debug.cleanup();
await editor.openUrl("/.auth?logout", true);
await editor.openUrl("/.logout", true);
}

View File

@ -1,6 +1,6 @@
import { deleteCookie, getCookie, setCookie } from "hono/helper.ts";
import { cors } from "hono/middleware.ts";
import { type Context, Hono, type HonoRequest } from "hono/mod.ts";
import { type Context, Hono, type HonoRequest, validator } from "hono/mod.ts";
import { AssetBundle } from "$lib/asset_bundle/bundle.ts";
import { FileMeta } from "$sb/types.ts";
import { ShellRequest } from "$type/rpc.ts";
@ -286,25 +286,44 @@ export class HttpServer {
"/.auth",
];
// Middleware handling the /.auth page and flow
this.app.all("/.auth", async (c) => {
// TODO: This should probably be a POST request
this.app.get("/.logout", async (c) => {
const url = new URL(c.req.url);
const req = c.req;
const host = url.host; // e.g. localhost:3000
if (url.search === "?logout") {
deleteCookie(c, authCookieName(host));
deleteCookie(c, authCookieName(url.host));
return c.redirect("/.auth");
});
this.app.get("/.auth", async (c) => {
const html = this.clientAssetBundle.readTextFileSync(".client/auth.html");
return c.html(html);
}).post(
validator("form", (value, c) => {
const username = value["username"];
const password = value["password"];
if (
!username || typeof username !== "string" ||
!password || typeof password !== "string"
) {
return c.redirect("/.auth?error=0");
}
if (req.method === "GET") {
return c.html(
this.clientAssetBundle.readTextFileSync(".client/auth.html"),
);
} else if (req.method === "POST") {
const values = await c.req.parseBody();
const username = values["username"];
const password = values["password"];
return { username, password };
}),
async (c) => {
const req = c.req;
const url = new URL(c.req.url);
const { username, password } = req.valid("form");
const spaceServer = await this.ensureSpaceServer(req);
const { user: expectedUser, pass: expectedPassword } = spaceServer
.auth!;
const {
user: expectedUser,
pass: expectedPassword,
} = spaceServer.auth!;
if (username === expectedUser && password === expectedPassword) {
// Generate a JWT and set it as a cookie
const jwt = await spaceServer.jwtIssuer.createJWT(
@ -312,21 +331,23 @@ export class HttpServer {
authenticationExpirySeconds,
);
console.log("Successful auth");
setCookie(c, authCookieName(host), jwt, {
setCookie(c, authCookieName(url.host), jwt, {
expires: new Date(
Date.now() + authenticationExpirySeconds * 1000,
), // in a week
// sameSite: "Strict",
// httpOnly: true,
});
return c.redirect("/");
const values = await c.req.parseBody();
const from = values["from"];
return c.redirect(typeof from === "string" ? from : "/");
} else {
console.error("Authentication failed, redirecting to auth page.");
return c.redirect("/.auth?error=1");
}
} else {
},
).all(async (c) => {
return c.redirect("/.auth");
}
});
// Check auth
@ -339,6 +360,14 @@ export class HttpServer {
}
const url = new URL(req.url);
const host = url.host;
const redirectToAuth = () => {
// Try filtering api paths
if (req.path.startsWith("/.") || req.path.endsWith(".md")) {
return c.redirect("/.auth");
} else {
return c.redirect(`/.auth?from=${req.path}`);
}
};
if (!excludedPaths.includes(url.pathname)) {
const authCookie = getCookie(c, authCookieName(host));
@ -360,7 +389,7 @@ export class HttpServer {
}
if (!authCookie) {
console.log("Unauthorized access, redirecting to auth page");
return c.redirect("/.auth");
return redirectToAuth();
}
const { user: expectedUser } = spaceServer.auth!;
@ -376,7 +405,7 @@ export class HttpServer {
"Error verifying JWT, redirecting to auth page",
e.message,
);
return c.redirect("/.auth");
return redirectToAuth();
}
}
return next();
@ -395,9 +424,7 @@ export class HttpServer {
);
// File list
this.app.get(
"/index.json",
async (c) => {
this.app.get("/index.json", async (c) => {
const req = c.req;
const spaceServer = await this.ensureSpaceServer(req);
if (req.header("X-Sync-Mode")) {
@ -411,8 +438,7 @@ export class HttpServer {
// The reason to do this is to handle authentication systems like Authelia nicely
return c.redirect("/");
}
},
);
});
// RPC shell
this.app.post("/.rpc/shell", async (c) => {
@ -466,16 +492,12 @@ export class HttpServer {
const filePathRegex = "/:path{[^!].*\\.[a-zA-Z]+}";
const mdExt = ".md";
this.app.get(
filePathRegex,
async (c) => {
this.app.get(filePathRegex, async (c) => {
const req = c.req;
const name = req.param("path")!;
const spaceServer = await this.ensureSpaceServer(req);
console.log(
"Requested file",
name,
);
console.log("Requested file", name);
if (
name.endsWith(mdExt) &&
// This header signififies the requests comes directly from the http_space_primitives client (not the browser)
@ -555,8 +577,7 @@ export class HttpServer {
console.error("Error GETting file", name, e.message);
return c.notFound();
}
},
).put(
}).put(
async (c) => {
const req = c.req;
const name = req.param("path")!;

View File

@ -76,10 +76,23 @@
<script>
const params = new URLSearchParams(window.location.search);
const error = params.get('error');
if (error === "1") {
if (error === "0") {
document.querySelector('.error-message').innerText = "The sent data was invalid";
} else if (error === "1") {
document.querySelector('.error-message').innerText = "Invalid username or password";
}
const from = params.get("from");
if (from) {
var input = document.createElement("input");
input.setAttribute("type", "hidden");
input.setAttribute("name", "from");
input.setAttribute("value", from);
document.getElementById("login").appendChild(input);
}
</script>
</body>

View File

@ -97,7 +97,11 @@ self.addEventListener("fetch", (event: any) => {
const pathname = requestUrl.pathname;
if (pathname === "/.auth" || pathname === "/index.json") {
if (
pathname === "/.auth" ||
pathname === "/.logout" ||
pathname === "/index.json"
) {
return fetch(request);
} else if (/\/.+\.[a-zA-Z]+$/.test(pathname)) {
// If this is a /*.* request, this can either be a plug worker load or an attachment load