* change the api of domain parsing to just parseDomain and getCookieDomain * adds getBaseDomain as the method to get the domain after any official subdomains
69 lines
1.7 KiB
TypeScript
69 lines
1.7 KiB
TypeScript
import crypto from "crypto";
|
|
import { addMinutes, subMinutes } from "date-fns";
|
|
import type { Request } from "express";
|
|
import fetch from "fetch-with-proxy";
|
|
import {
|
|
StateStoreStoreCallback,
|
|
StateStoreVerifyCallback,
|
|
} from "passport-oauth2";
|
|
import { getCookieDomain } from "@shared/utils/domains";
|
|
import { OAuthStateMismatchError } from "../errors";
|
|
|
|
export class StateStore {
|
|
key = "state";
|
|
|
|
store = (ctx: Request, callback: StateStoreStoreCallback) => {
|
|
// Produce a random string as state
|
|
const state = crypto.randomBytes(8).toString("hex");
|
|
|
|
ctx.cookies.set(this.key, state, {
|
|
httpOnly: false,
|
|
expires: addMinutes(new Date(), 10),
|
|
domain: getCookieDomain(ctx.hostname),
|
|
});
|
|
|
|
callback(null, state);
|
|
};
|
|
|
|
verify = (
|
|
ctx: Request,
|
|
providedState: string,
|
|
callback: StateStoreVerifyCallback
|
|
) => {
|
|
const state = ctx.cookies.get(this.key);
|
|
|
|
if (!state) {
|
|
return callback(
|
|
OAuthStateMismatchError("State not return in OAuth flow"),
|
|
false,
|
|
state
|
|
);
|
|
}
|
|
|
|
ctx.cookies.set(this.key, "", {
|
|
httpOnly: false,
|
|
expires: subMinutes(new Date(), 1),
|
|
domain: getCookieDomain(ctx.hostname),
|
|
});
|
|
|
|
if (state !== providedState) {
|
|
return callback(OAuthStateMismatchError(), false, state);
|
|
}
|
|
|
|
// @ts-expect-error Type in library is wrong
|
|
callback(null, true, state);
|
|
};
|
|
}
|
|
|
|
export async function request(endpoint: string, accessToken: string) {
|
|
const response = await fetch(endpoint, {
|
|
method: "GET",
|
|
credentials: "same-origin",
|
|
headers: {
|
|
Authorization: `Bearer ${accessToken}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
return response.json();
|
|
}
|