fix: more login/session issues addressed
This commit is contained in:
parent
cf46450f5f
commit
ee152a514c
8 changed files with 85 additions and 51 deletions
|
@ -6,12 +6,14 @@ import { SESSION, SESSIONS } from '../../../models/session.ts';
|
||||||
import { TOTP_ENTRIES } from '../../../models/totp_entry.ts';
|
import { TOTP_ENTRIES } from '../../../models/totp_entry.ts';
|
||||||
import { encodeBase64 } from 'jsr:@std/encoding/base64';
|
import { encodeBase64 } from 'jsr:@std/encoding/base64';
|
||||||
import parse_body from '../../../utils/bodyparser.ts';
|
import parse_body from '../../../utils/bodyparser.ts';
|
||||||
import { SESSION_ID_TOKEN, SESSION_SECRET_TOKEN } from '../../../utils/prechecks.ts';
|
import { get_session, get_user, PRECHECK_TABLE, require_user, SESSION_ID_TOKEN, SESSION_SECRET_TOKEN } from '../../../utils/prechecks.ts';
|
||||||
import * as bcrypt from 'jsr:@da/bcrypt';
|
import * as bcrypt from 'jsr:@da/bcrypt';
|
||||||
import { verifyTotp } from '../../../utils/totp.ts';
|
import { verifyTotp } from '../../../utils/totp.ts';
|
||||||
|
|
||||||
const DEFAULT_SESSION_TIME: number = 60 * 60 * 1_000; // 1 Hour
|
const DEFAULT_SESSION_TIME: number = 60 * 60 * 1_000; // 1 Hour
|
||||||
|
|
||||||
|
export const PRECHECKS: PRECHECK_TABLE = {};
|
||||||
|
|
||||||
// POST /api/auth - Authenticate
|
// POST /api/auth - Authenticate
|
||||||
export async function POST(req: Request, meta: Record<string, any>): Promise<Response> {
|
export async function POST(req: Request, meta: Record<string, any>): Promise<Response> {
|
||||||
try {
|
try {
|
||||||
|
@ -158,6 +160,28 @@ export type SESSION_INFO = {
|
||||||
expires: string | undefined;
|
expires: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// DELETE /api/auth - log out (delete session)
|
||||||
|
PRECHECKS.DELETE = [get_session, get_user, require_user];
|
||||||
|
const back_then = new Date(0).toISOString();
|
||||||
|
export async function DELETE(_request: Request, meta: Record<string, any>): Promise<Response> {
|
||||||
|
await SESSIONS.delete(meta.session);
|
||||||
|
|
||||||
|
const headers = new Headers();
|
||||||
|
|
||||||
|
headers.append('Set-Cookie', `${SESSION_ID_TOKEN}=; Path=/; Expires=${back_then}`);
|
||||||
|
|
||||||
|
// TODO: this wasn't really intended to be persisted in a cookie, but we are using it to
|
||||||
|
// generate the TOTP for the call to /api/users/me
|
||||||
|
headers.append('Set-Cookie', `${SESSION_SECRET_TOKEN}=; Path=/; Expires=${back_then}`);
|
||||||
|
|
||||||
|
return Response.json({
|
||||||
|
deleted: true
|
||||||
|
}, {
|
||||||
|
status: 200,
|
||||||
|
headers
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const session_secret_buffer = new Uint8Array(20);
|
const session_secret_buffer = new Uint8Array(20);
|
||||||
export async function create_new_session(session_settings: SESSION_INFO): Promise<SESSION_RESULT> {
|
export async function create_new_session(session_settings: SESSION_INFO): Promise<SESSION_RESULT> {
|
||||||
const now = new Date().toISOString();
|
const now = new Date().toISOString();
|
||||||
|
|
|
@ -131,6 +131,7 @@ button {
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
border: 1px solid var(--text);
|
border: 1px solid var(--text);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.primary {
|
button.primary {
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
console.log( 'hi');
|
console.log( 'hi');
|
||||||
const session_response = await api.fetch("/users/me");
|
const session_response = await api.fetch("/api/users/me");
|
||||||
|
|
||||||
if (!session_response.ok) {
|
if (!session_response.ok) {
|
||||||
const error_body = await session_response.json();
|
const error_body = await session_response.json();
|
||||||
|
|
|
@ -11,8 +11,8 @@ const api = {
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
"x-session_id": session_id,
|
"x-session_id": session_id ?? "",
|
||||||
"x-totp": await otp_totp(session_secret),
|
"x-totp": session_secret ? await otp_totp(session_secret) : "",
|
||||||
...(options.headers ?? {}),
|
...(options.headers ?? {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ const api = {
|
||||||
fetch_options.body = JSON.stringify(options.json);
|
fetch_options.body = JSON.stringify(options.json);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(`/api${url}`, fetch_options);
|
const response = await fetch(url, fetch_options);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,11 +7,15 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||||
form.onsubmit = async (event) => {
|
form.onsubmit = async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
|
const url = form.action;
|
||||||
|
const method = form.dataset.method ?? "POST";
|
||||||
|
|
||||||
|
const json = {};
|
||||||
|
|
||||||
const form_data = new FormData(form);
|
const form_data = new FormData(form);
|
||||||
const body = {};
|
|
||||||
for (const [key, value] of form_data.entries()) {
|
for (const [key, value] of form_data.entries()) {
|
||||||
const elements = key.split(".");
|
const elements = key.split(".");
|
||||||
let current = body;
|
let current = json;
|
||||||
for (const element of elements.slice(0, elements.length - 1)) {
|
for (const element of elements.slice(0, elements.length - 1)) {
|
||||||
current[element] = current[element] ?? {};
|
current[element] = current[element] ?? {};
|
||||||
current = current[element];
|
current = current[element];
|
||||||
|
@ -20,12 +24,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||||
current[elements.slice(elements.length - 1).shift()] = value;
|
current[elements.slice(elements.length - 1).shift()] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = form.action;
|
|
||||||
const method = form.dataset.method ?? "POST";
|
|
||||||
|
|
||||||
console.dir({
|
console.dir({
|
||||||
method,
|
method,
|
||||||
form,
|
form,
|
||||||
|
json,
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
// TODO: send session header
|
// TODO: send session header
|
||||||
|
@ -37,11 +39,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (["POST", "PUT", "PATCH"].includes(method)) {
|
if (["POST", "PUT", "PATCH"].includes(method)) {
|
||||||
options.headers["Content-Type"] = "application/json";
|
options.json = json;
|
||||||
options.body = JSON.stringify(body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch(url, options);
|
const response = await api.fetch(url, options);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const error_body = await response.json();
|
const error_body = await response.json();
|
||||||
|
|
|
@ -28,6 +28,12 @@
|
||||||
attributeFilter: ["data-user"],
|
attributeFilter: ["data-user"],
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<style>
|
||||||
|
.profile-container {
|
||||||
|
margin: 1rem auto;
|
||||||
|
max-width: 1024px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
<div id="user" class="tab">
|
<div id="user" class="tab">
|
||||||
<input
|
<input
|
||||||
type="radio"
|
type="radio"
|
||||||
|
@ -41,6 +47,7 @@
|
||||||
<div class="label">Profile</div></label
|
<div class="label">Profile</div></label
|
||||||
>
|
>
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
|
<div class="profile-container">
|
||||||
<div class="avatar-container">
|
<div class="avatar-container">
|
||||||
<img
|
<img
|
||||||
id="user-avatar"
|
id="user-avatar"
|
||||||
|
@ -54,13 +61,13 @@
|
||||||
<span class="username" data-bind-user_username></span>
|
<span class="username" data-bind-user_username></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form data-smart="true" action="/api/auth" method="DELETE">
|
<form data-smart="true" data-method="DELETE" action="/api/auth">
|
||||||
<script>
|
<script>
|
||||||
{
|
{
|
||||||
const form = document.currentScript.closest("form");
|
const form = document.currentScript.closest("form");
|
||||||
form.on_response = (response) => {
|
form.on_response = (response) => {
|
||||||
if (!response.ok) {
|
if (!response.deleted) {
|
||||||
alert("error logging out!");
|
alert("error logging out? please reload.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,4 +80,5 @@
|
||||||
<button class="primary">Log Out</button>
|
<button class="primary">Log Out</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
|
@ -133,5 +133,5 @@
|
||||||
<!-- #include file="./work.html" -->
|
<!-- #include file="./work.html" -->
|
||||||
<!-- #include file="./resources.html" -->
|
<!-- #include file="./resources.html" -->
|
||||||
<!-- #include file="./calendar.html" -->
|
<!-- #include file="./calendar.html" -->
|
||||||
<!-- #include file="./user.html" -->
|
<!-- #include file="./profile.html" -->
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -211,7 +211,7 @@
|
||||||
|
|
||||||
users[event.creator_id] =
|
users[event.creator_id] =
|
||||||
users[event.creator_id] ??
|
users[event.creator_id] ??
|
||||||
(await api.fetch(`/users/${event.creator_id}`));
|
(await api.fetch(`/api/users/${event.creator_id}`));
|
||||||
|
|
||||||
room_chat_content.insertAdjacentHTML(
|
room_chat_content.insertAdjacentHTML(
|
||||||
"beforeend",
|
"beforeend",
|
||||||
|
@ -233,7 +233,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const message_polling_url = `/rooms/${room_id}/events?type=chat&limit=100&sort=newest&wait=true${last_message_id ? `&after_id=${last_message_id}` : ""}`;
|
const message_polling_url = `/api/rooms/${room_id}/events?type=chat&limit=100&sort=newest&wait=true${last_message_id ? `&after_id=${last_message_id}` : ""}`;
|
||||||
|
|
||||||
room_polling_request_abort_controller =
|
room_polling_request_abort_controller =
|
||||||
room_polling_request_abort_controller || new AbortController();
|
room_polling_request_abort_controller || new AbortController();
|
||||||
|
@ -264,7 +264,7 @@
|
||||||
delete room_chat_content.dataset.last_message_id;
|
delete room_chat_content.dataset.last_message_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const room_response = await api.fetch(`/rooms/${room_id}`);
|
const room_response = await api.fetch(`/api/rooms/${room_id}`);
|
||||||
if (!room_response.ok) {
|
if (!room_response.ok) {
|
||||||
const error = await room_response.json();
|
const error = await room_response.json();
|
||||||
alert(error.message ?? JSON.stringify(error));
|
alert(error.message ?? JSON.stringify(error));
|
||||||
|
@ -285,7 +285,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const events_response = await api.fetch(
|
const events_response = await api.fetch(
|
||||||
`/rooms/${room_id}/events?type=chat&limit=100&sort=newest`,
|
`/api/rooms/${room_id}/events?type=chat&limit=100&sort=newest`,
|
||||||
);
|
);
|
||||||
if (!events_response.ok) {
|
if (!events_response.ok) {
|
||||||
const error = await events_response.json();
|
const error = await events_response.json();
|
||||||
|
@ -309,7 +309,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rooms_response = await api.fetch("/rooms");
|
const rooms_response = await api.fetch("/api/rooms");
|
||||||
if (rooms_response.ok) {
|
if (rooms_response.ok) {
|
||||||
const room_list = document.getElementById("room-list");
|
const room_list = document.getElementById("room-list");
|
||||||
room_list.innerHTML = "";
|
room_list.innerHTML = "";
|
||||||
|
@ -411,7 +411,7 @@
|
||||||
render_text_event(message_event, user),
|
render_text_event(message_event, user),
|
||||||
);
|
);
|
||||||
|
|
||||||
const event_response = await api.fetch(`/rooms/${room_id}/events`, {
|
const event_response = await api.fetch(`/api/rooms/${room_id}/events`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
json: message_event,
|
json: message_event,
|
||||||
});
|
});
|
||||||
|
@ -484,7 +484,7 @@
|
||||||
editable.addEventListener("focusout", async () => {
|
editable.addEventListener("focusout", async () => {
|
||||||
const name = editable.textContent ?? editable.innerText;
|
const name = editable.textContent ?? editable.innerText;
|
||||||
|
|
||||||
const new_room_response = await api.fetch("/rooms", {
|
const new_room_response = await api.fetch("/api/rooms", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
json: {
|
json: {
|
||||||
name,
|
name,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue