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 editor.flashNotification("Now wiping all state and logging out...");
|
||||||
await debug.cleanup();
|
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 { deleteCookie, getCookie, setCookie } from "hono/helper.ts";
|
||||||
import { cors } from "hono/middleware.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 { AssetBundle } from "$lib/asset_bundle/bundle.ts";
|
||||||
import { FileMeta } from "$sb/types.ts";
|
import { FileMeta } from "$sb/types.ts";
|
||||||
import { ShellRequest } from "$type/rpc.ts";
|
import { ShellRequest } from "$type/rpc.ts";
|
||||||
|
@ -286,25 +286,44 @@ export class HttpServer {
|
||||||
"/.auth",
|
"/.auth",
|
||||||
];
|
];
|
||||||
|
|
||||||
// Middleware handling the /.auth page and flow
|
// TODO: This should probably be a POST request
|
||||||
this.app.all("/.auth", async (c) => {
|
this.app.get("/.logout", async (c) => {
|
||||||
const url = new URL(c.req.url);
|
const url = new URL(c.req.url);
|
||||||
const req = c.req;
|
deleteCookie(c, authCookieName(url.host));
|
||||||
const host = url.host; // e.g. localhost:3000
|
|
||||||
if (url.search === "?logout") {
|
return c.redirect("/.auth");
|
||||||
deleteCookie(c, authCookieName(host));
|
});
|
||||||
|
|
||||||
|
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(
|
return { username, password };
|
||||||
this.clientAssetBundle.readTextFileSync(".client/auth.html"),
|
}),
|
||||||
);
|
async (c) => {
|
||||||
} else if (req.method === "POST") {
|
const req = c.req;
|
||||||
const values = await c.req.parseBody();
|
const url = new URL(c.req.url);
|
||||||
const username = values["username"];
|
const { username, password } = req.valid("form");
|
||||||
const password = values["password"];
|
|
||||||
const spaceServer = await this.ensureSpaceServer(req);
|
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) {
|
if (username === expectedUser && password === expectedPassword) {
|
||||||
// Generate a JWT and set it as a cookie
|
// Generate a JWT and set it as a cookie
|
||||||
const jwt = await spaceServer.jwtIssuer.createJWT(
|
const jwt = await spaceServer.jwtIssuer.createJWT(
|
||||||
|
@ -312,21 +331,23 @@ export class HttpServer {
|
||||||
authenticationExpirySeconds,
|
authenticationExpirySeconds,
|
||||||
);
|
);
|
||||||
console.log("Successful auth");
|
console.log("Successful auth");
|
||||||
setCookie(c, authCookieName(host), jwt, {
|
setCookie(c, authCookieName(url.host), jwt, {
|
||||||
expires: new Date(
|
expires: new Date(
|
||||||
Date.now() + authenticationExpirySeconds * 1000,
|
Date.now() + authenticationExpirySeconds * 1000,
|
||||||
), // in a week
|
), // in a week
|
||||||
// sameSite: "Strict",
|
// sameSite: "Strict",
|
||||||
// httpOnly: true,
|
// httpOnly: true,
|
||||||
});
|
});
|
||||||
return c.redirect("/");
|
const values = await c.req.parseBody();
|
||||||
|
const from = values["from"];
|
||||||
|
return c.redirect(typeof from === "string" ? from : "/");
|
||||||
} else {
|
} else {
|
||||||
console.error("Authentication failed, redirecting to auth page.");
|
console.error("Authentication failed, redirecting to auth page.");
|
||||||
return c.redirect("/.auth?error=1");
|
return c.redirect("/.auth?error=1");
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
|
).all(async (c) => {
|
||||||
return c.redirect("/.auth");
|
return c.redirect("/.auth");
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check auth
|
// Check auth
|
||||||
|
@ -339,6 +360,14 @@ export class HttpServer {
|
||||||
}
|
}
|
||||||
const url = new URL(req.url);
|
const url = new URL(req.url);
|
||||||
const host = url.host;
|
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)) {
|
if (!excludedPaths.includes(url.pathname)) {
|
||||||
const authCookie = getCookie(c, authCookieName(host));
|
const authCookie = getCookie(c, authCookieName(host));
|
||||||
|
|
||||||
|
@ -360,7 +389,7 @@ export class HttpServer {
|
||||||
}
|
}
|
||||||
if (!authCookie) {
|
if (!authCookie) {
|
||||||
console.log("Unauthorized access, redirecting to auth page");
|
console.log("Unauthorized access, redirecting to auth page");
|
||||||
return c.redirect("/.auth");
|
return redirectToAuth();
|
||||||
}
|
}
|
||||||
const { user: expectedUser } = spaceServer.auth!;
|
const { user: expectedUser } = spaceServer.auth!;
|
||||||
|
|
||||||
|
@ -376,7 +405,7 @@ export class HttpServer {
|
||||||
"Error verifying JWT, redirecting to auth page",
|
"Error verifying JWT, redirecting to auth page",
|
||||||
e.message,
|
e.message,
|
||||||
);
|
);
|
||||||
return c.redirect("/.auth");
|
return redirectToAuth();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return next();
|
return next();
|
||||||
|
@ -395,9 +424,7 @@ export class HttpServer {
|
||||||
);
|
);
|
||||||
|
|
||||||
// File list
|
// File list
|
||||||
this.app.get(
|
this.app.get("/index.json", async (c) => {
|
||||||
"/index.json",
|
|
||||||
async (c) => {
|
|
||||||
const req = c.req;
|
const req = c.req;
|
||||||
const spaceServer = await this.ensureSpaceServer(req);
|
const spaceServer = await this.ensureSpaceServer(req);
|
||||||
if (req.header("X-Sync-Mode")) {
|
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
|
// The reason to do this is to handle authentication systems like Authelia nicely
|
||||||
return c.redirect("/");
|
return c.redirect("/");
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
// RPC shell
|
// RPC shell
|
||||||
this.app.post("/.rpc/shell", async (c) => {
|
this.app.post("/.rpc/shell", async (c) => {
|
||||||
|
@ -466,16 +492,12 @@ export class HttpServer {
|
||||||
const filePathRegex = "/:path{[^!].*\\.[a-zA-Z]+}";
|
const filePathRegex = "/:path{[^!].*\\.[a-zA-Z]+}";
|
||||||
const mdExt = ".md";
|
const mdExt = ".md";
|
||||||
|
|
||||||
this.app.get(
|
this.app.get(filePathRegex, async (c) => {
|
||||||
filePathRegex,
|
|
||||||
async (c) => {
|
|
||||||
const req = c.req;
|
const req = c.req;
|
||||||
const name = req.param("path")!;
|
const name = req.param("path")!;
|
||||||
const spaceServer = await this.ensureSpaceServer(req);
|
const spaceServer = await this.ensureSpaceServer(req);
|
||||||
console.log(
|
console.log("Requested file", name);
|
||||||
"Requested file",
|
|
||||||
name,
|
|
||||||
);
|
|
||||||
if (
|
if (
|
||||||
name.endsWith(mdExt) &&
|
name.endsWith(mdExt) &&
|
||||||
// This header signififies the requests comes directly from the http_space_primitives client (not the browser)
|
// 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);
|
console.error("Error GETting file", name, e.message);
|
||||||
return c.notFound();
|
return c.notFound();
|
||||||
}
|
}
|
||||||
},
|
}).put(
|
||||||
).put(
|
|
||||||
async (c) => {
|
async (c) => {
|
||||||
const req = c.req;
|
const req = c.req;
|
||||||
const name = req.param("path")!;
|
const name = req.param("path")!;
|
||||||
|
|
|
@ -76,10 +76,23 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
const error = params.get('error');
|
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";
|
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>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,11 @@ self.addEventListener("fetch", (event: any) => {
|
||||||
|
|
||||||
const pathname = requestUrl.pathname;
|
const pathname = requestUrl.pathname;
|
||||||
|
|
||||||
if (pathname === "/.auth" || pathname === "/index.json") {
|
if (
|
||||||
|
pathname === "/.auth" ||
|
||||||
|
pathname === "/.logout" ||
|
||||||
|
pathname === "/index.json"
|
||||||
|
) {
|
||||||
return fetch(request);
|
return fetch(request);
|
||||||
} else if (/\/.+\.[a-zA-Z]+$/.test(pathname)) {
|
} else if (/\/.+\.[a-zA-Z]+$/.test(pathname)) {
|
||||||
// If this is a /*.* request, this can either be a plug worker load or an attachment load
|
// If this is a /*.* request, this can either be a plug worker load or an attachment load
|
||||||
|
|
Loading…
Reference in New Issue