Redirect to initial url after authentication (#893)
parent
bd3e91ba65
commit
b6d95ec632
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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")!;
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue