mirror of
https://github.com/Drop-OSS/drop.git
synced 2026-07-27 18:24:43 +10:00
Add ODIC Back-Channel Logout (#304)
* prevent returning expired sessions * add issuer to ODIC creds * get id token in ODIC * make session signin return session * working backchannel logout? * require https for ODIC provider * handle wellknown not being https * find session api progress * fix windows build * return session token on session * switch OIDC to #searchSessions * update pnpm * switch to using message on error obj * move odic callback * fix type errors * redirect old oidc callback * make redirect url a URL * remove scheduled task downloadCleanup * fix session search for oidc * fix signin result * cleanup code * ignore data dir * fix lint error
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
import prisma from "../db/database";
|
||||
import type { Session, SessionProvider } from "./types";
|
||||
import type { SessionProvider, SessionWithToken } from "./types";
|
||||
import cacheHandler from "../cache";
|
||||
import type { SessionWhereInput, JsonFilter } from "~/prisma/client/models";
|
||||
import type { InputJsonValue } from "@prisma/client/runtime/library";
|
||||
|
||||
export default function createDBSessionHandler(): SessionProvider {
|
||||
const cache = cacheHandler.createCache<Session>("DBSession");
|
||||
const cache = cacheHandler.createCache<SessionWithToken>("DBSession");
|
||||
|
||||
return {
|
||||
async setSession(token, session) {
|
||||
await cache.set(token, session);
|
||||
await cache.set(token, { ...session, token });
|
||||
|
||||
// const strData = JSON.stringify(data);
|
||||
await prisma.session.upsert({
|
||||
const result = await prisma.session.upsert({
|
||||
where: {
|
||||
token,
|
||||
},
|
||||
@@ -28,12 +29,14 @@ export default function createDBSessionHandler(): SessionProvider {
|
||||
data: session as object,
|
||||
},
|
||||
});
|
||||
return true;
|
||||
|
||||
// need to cast to Session since prisma returns different json types
|
||||
return result.data as unknown as SessionWithToken;
|
||||
},
|
||||
async updateSession(token, data) {
|
||||
return await this.setSession(token, data);
|
||||
return (await this.setSession(token, data)) !== undefined;
|
||||
},
|
||||
async getSession<T extends Session>(token: string) {
|
||||
async getSession<T extends SessionWithToken>(token: string) {
|
||||
const cached = await cache.get(token);
|
||||
if (cached !== null) return cached as T;
|
||||
|
||||
@@ -44,6 +47,10 @@ export default function createDBSessionHandler(): SessionProvider {
|
||||
});
|
||||
if (result === null) return undefined;
|
||||
|
||||
// add to cache
|
||||
// need to cast to Session since prisma returns a more specific type
|
||||
await cache.set(token, result as SessionWithToken);
|
||||
|
||||
// i hate casting
|
||||
// need to cast to unknown since result.data can be an N deep json object technically
|
||||
// ts doesn't like that be cast down to the more constraining session type
|
||||
@@ -69,5 +76,99 @@ export default function createDBSessionHandler(): SessionProvider {
|
||||
},
|
||||
});
|
||||
},
|
||||
async findSessions(options) {
|
||||
const search: SessionWhereInput[] = [];
|
||||
if (options.userId) {
|
||||
search.push({ userId: options.userId });
|
||||
}
|
||||
|
||||
// NOTE: in the DB, the entire session subject is stored in the "data" field
|
||||
// so we need to search within that JSON object for the items we want
|
||||
|
||||
if (options.data && typeof options.data === "object") {
|
||||
const entries = walkJsonPath(options.data);
|
||||
for (const { path, value } of entries) {
|
||||
const filter: JsonFilter<"Session"> = {
|
||||
// set base path to data
|
||||
path: ["data", ...path],
|
||||
equals: value as InputJsonValue,
|
||||
};
|
||||
search.push({ data: filter });
|
||||
}
|
||||
}
|
||||
if (options.oidc && typeof options.oidc === "object") {
|
||||
const entries = walkJsonPath(options.oidc);
|
||||
for (const { path, value } of entries) {
|
||||
const filter: JsonFilter<"Session"> = {
|
||||
// set base path to oidc
|
||||
path: ["oidc", ...path],
|
||||
equals: value as InputJsonValue,
|
||||
};
|
||||
search.push({ data: filter });
|
||||
}
|
||||
}
|
||||
|
||||
if (search.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// console.log("Searching sessions with:", JSON.stringify(search, null, 2));
|
||||
|
||||
const sessions = await prisma.session.findMany({
|
||||
where: {
|
||||
AND: search,
|
||||
},
|
||||
});
|
||||
const results: SessionWithToken[] = [];
|
||||
for (const session of sessions) {
|
||||
// need to cast to Session since prisma returns different json types
|
||||
results.push(session.data as unknown as SessionWithToken);
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks a JSON object and returns all paths and their corresponding values.
|
||||
* @param obj The JSON object to walk.
|
||||
* @param basePath The base path to start from (used for recursion).
|
||||
* @returns An array of objects containing the path and value.
|
||||
*/
|
||||
function walkJsonPath(
|
||||
obj: unknown,
|
||||
basePath: string[] = [],
|
||||
): Array<{ path: string[]; value: unknown }> {
|
||||
const results: Array<{ path: string[]; value: unknown }> = [];
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
const v = obj[i];
|
||||
if (v === undefined) continue;
|
||||
if (v !== null && typeof v === "object") {
|
||||
results.push(...walkJsonPath(v, [...basePath, String(i)]));
|
||||
} else {
|
||||
results.push({ path: [...basePath, String(i)], value: v });
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
if (obj !== null && typeof obj === "object") {
|
||||
for (const [k, v] of Object.entries(obj as Record<string, unknown>)) {
|
||||
if (v === undefined) continue;
|
||||
if (v !== null && typeof v === "object") {
|
||||
results.push(...walkJsonPath(v, [...basePath, k]));
|
||||
} else {
|
||||
results.push({ path: [...basePath, k], value: v });
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
if (basePath.length > 0) {
|
||||
results.push({ path: basePath, value: obj });
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user