Fix: login cookies and refresh token
This commit is contained in:
parent
1b2673fc93
commit
76fa4872f1
7 changed files with 80 additions and 55 deletions
34
src/lib/accessToken.ts
Normal file
34
src/lib/accessToken.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import db from "~/drizzle";
|
||||||
|
import { discordTokens } from "~/drizzle/schema";
|
||||||
|
import { discord } from "./auth";
|
||||||
|
|
||||||
|
const getAccessToken = async (userId: string) => {
|
||||||
|
let tokens = await db.query.discordTokens
|
||||||
|
.findFirst({
|
||||||
|
where: eq(discordTokens.userId, userId),
|
||||||
|
})
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
if (tokens && new Date() >= tokens.expiresAt) {
|
||||||
|
const newTokens = await discord.refreshAccessToken(tokens.refreshToken);
|
||||||
|
|
||||||
|
tokens = (
|
||||||
|
await db
|
||||||
|
.update(discordTokens)
|
||||||
|
.set({
|
||||||
|
accessToken: newTokens.accessToken,
|
||||||
|
expiresAt: newTokens.accessTokenExpiresAt,
|
||||||
|
refreshToken: newTokens.refreshToken,
|
||||||
|
})
|
||||||
|
.where(eq(discordTokens.userId, userId))
|
||||||
|
.returning()
|
||||||
|
.execute()
|
||||||
|
)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tokens) return tokens;
|
||||||
|
return tokens;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getAccessToken;
|
|
@ -2,7 +2,7 @@ import { createMiddleware } from "@solidjs/start/middleware";
|
||||||
import colors from "colors";
|
import colors from "colors";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { verifyRequestOrigin } from "lucia";
|
import { verifyRequestOrigin } from "lucia";
|
||||||
import { appendHeader, getCookie, getHeader } from "vinxi/http";
|
import { getCookie, getHeader, setCookie } from "vinxi/http";
|
||||||
import { lucia } from "./lib/auth";
|
import { lucia } from "./lib/auth";
|
||||||
|
|
||||||
colors.enable();
|
colors.enable();
|
||||||
|
@ -12,8 +12,8 @@ let started: boolean = false;
|
||||||
export default createMiddleware({
|
export default createMiddleware({
|
||||||
onRequest: async (event) => {
|
onRequest: async (event) => {
|
||||||
if (event.nativeEvent.node.req.method !== "GET") {
|
if (event.nativeEvent.node.req.method !== "GET") {
|
||||||
const originHeader = getHeader(event.nativeEvent, "Origin") ?? null;
|
const originHeader = getHeader("Origin") ?? null;
|
||||||
const hostHeader = getHeader(event.nativeEvent, "Host") ?? null;
|
const hostHeader = getHeader("Host") ?? null;
|
||||||
if (
|
if (
|
||||||
!originHeader ||
|
!originHeader ||
|
||||||
!hostHeader ||
|
!hostHeader ||
|
||||||
|
@ -24,8 +24,7 @@ export default createMiddleware({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sessionId =
|
const sessionId = getCookie(lucia.sessionCookieName) ?? null;
|
||||||
getCookie(event.nativeEvent, lucia.sessionCookieName) ?? null;
|
|
||||||
if (!sessionId) {
|
if (!sessionId) {
|
||||||
event.nativeEvent.context.session = null;
|
event.nativeEvent.context.session = null;
|
||||||
event.nativeEvent.context.user = null;
|
event.nativeEvent.context.user = null;
|
||||||
|
@ -34,20 +33,22 @@ export default createMiddleware({
|
||||||
|
|
||||||
const { session, user } = await lucia.validateSession(sessionId);
|
const { session, user } = await lucia.validateSession(sessionId);
|
||||||
if (session && session.fresh) {
|
if (session && session.fresh) {
|
||||||
appendHeader(
|
const sessionCookie = lucia.createSessionCookie(session.id);
|
||||||
event.nativeEvent,
|
setCookie(
|
||||||
"Set-Cookie",
|
sessionCookie.name,
|
||||||
lucia.createSessionCookie(session.id).serialize(),
|
sessionCookie.value,
|
||||||
|
sessionCookie.attributes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!session) {
|
if (!session) {
|
||||||
appendHeader(
|
const sessionCookie = lucia.createBlankSessionCookie();
|
||||||
event.nativeEvent,
|
setCookie(
|
||||||
"Set-Cookie",
|
sessionCookie.name,
|
||||||
lucia.createBlankSessionCookie().serialize(),
|
sessionCookie.value,
|
||||||
|
sessionCookie.attributes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
console.log(3);
|
|
||||||
event.nativeEvent.context.session = session;
|
event.nativeEvent.context.session = session;
|
||||||
event.nativeEvent.context.user = user;
|
event.nativeEvent.context.user = user;
|
||||||
},
|
},
|
||||||
|
|
|
@ -96,7 +96,6 @@ export async function GET(event: APIEvent): Promise<Response> {
|
||||||
expiresAt: tokens.accessTokenExpiresAt,
|
expiresAt: tokens.accessTokenExpiresAt,
|
||||||
refreshToken: tokens.refreshToken,
|
refreshToken: tokens.refreshToken,
|
||||||
})
|
})
|
||||||
.returning()
|
|
||||||
.execute();
|
.execute();
|
||||||
const session = await lucia.createSession(
|
const session = await lucia.createSession(
|
||||||
userId,
|
userId,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { APIEvent } from "@solidjs/start/server";
|
|
||||||
import { generateState } from "arctic";
|
import { generateState } from "arctic";
|
||||||
import httpStatus from "http-status";
|
import httpStatus from "http-status";
|
||||||
import { setCookie } from "vinxi/http";
|
import { setCookie } from "vinxi/http";
|
||||||
|
@ -7,13 +6,13 @@ import { discord } from "~/lib/auth";
|
||||||
if (typeof import.meta.env.PROD === "undefined")
|
if (typeof import.meta.env.PROD === "undefined")
|
||||||
throw new Error("No env PROD found!");
|
throw new Error("No env PROD found!");
|
||||||
|
|
||||||
export async function GET(event: APIEvent) {
|
export async function GET() {
|
||||||
const state = generateState();
|
const state = generateState();
|
||||||
const url = await discord.createAuthorizationURL(state, {
|
const url = await discord.createAuthorizationURL(state, {
|
||||||
scopes: ["identify", "guilds", "guilds.members.read"],
|
scopes: ["identify", "guilds", "guilds.members.read"],
|
||||||
});
|
});
|
||||||
|
|
||||||
setCookie(event, "discord_oauth_state", state, {
|
setCookie("discord_oauth_state", state, {
|
||||||
path: "/",
|
path: "/",
|
||||||
secure: import.meta.env.PROD,
|
secure: import.meta.env.PROD,
|
||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
|
|
|
@ -4,15 +4,12 @@ import { appendHeader } from "vinxi/http";
|
||||||
import { lucia } from "~/lib/auth";
|
import { lucia } from "~/lib/auth";
|
||||||
|
|
||||||
export const GET = async (event: APIEvent) => {
|
export const GET = async (event: APIEvent) => {
|
||||||
if (!event.nativeEvent.context.session) {
|
const { session } = event.nativeEvent.context;
|
||||||
return new Error("Unauthorized");
|
|
||||||
}
|
if (!session) return new Error("Unauthorized");
|
||||||
await lucia.invalidateSession(event.nativeEvent.context.session.id);
|
|
||||||
appendHeader(
|
await lucia.invalidateSession(session.id);
|
||||||
event,
|
appendHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
|
||||||
"Set-Cookie",
|
|
||||||
lucia.createBlankSessionCookie().serialize(),
|
|
||||||
);
|
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
status: httpStatus.FOUND,
|
status: httpStatus.FOUND,
|
||||||
headers: { Location: "/" },
|
headers: { Location: "/" },
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { faToggleOff, faToggleOn } from "@fortawesome/pro-regular-svg-icons";
|
import { faToggleOff, faToggleOn } from "@fortawesome/pro-regular-svg-icons";
|
||||||
import { useLocation, useNavigate, useParams } from "@solidjs/router";
|
import { useLocation, useNavigate, useParams } from "@solidjs/router";
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import createClient from "openapi-fetch";
|
import createClient from "openapi-fetch";
|
||||||
import {
|
import {
|
||||||
|
@ -14,8 +13,7 @@ import { createStore } from "solid-js/store";
|
||||||
import { getRequestEvent } from "solid-js/web";
|
import { getRequestEvent } from "solid-js/web";
|
||||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon";
|
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon";
|
||||||
import Layout from "~/components/Layout";
|
import Layout from "~/components/Layout";
|
||||||
import db from "~/drizzle";
|
import getAccessToken from "~/lib/accessToken";
|
||||||
import { discordTokens } from "~/drizzle/schema";
|
|
||||||
import { paths } from "~/types/discord";
|
import { paths } from "~/types/discord";
|
||||||
import "../../styles/pages/config.scss";
|
import "../../styles/pages/config.scss";
|
||||||
|
|
||||||
|
@ -51,36 +49,39 @@ const getPayload = async (
|
||||||
const { user } = event.nativeEvent.context;
|
const { user } = event.nativeEvent.context;
|
||||||
if (!user) return { success: false, message: "User not logged in!" };
|
if (!user) return { success: false, message: "User not logged in!" };
|
||||||
|
|
||||||
const tokens = await db.query.discordTokens
|
const tokens = await getAccessToken(user.id);
|
||||||
.findFirst({
|
|
||||||
where: eq(discordTokens.userId, user.id),
|
|
||||||
})
|
|
||||||
.execute();
|
|
||||||
if (!tokens) return { success: false, message: "No discord access token!" };
|
if (!tokens) return { success: false, message: "No discord access token!" };
|
||||||
|
|
||||||
const { GET } = createClient<paths>({
|
const { GET } = createClient<paths>({
|
||||||
baseUrl: "https://discord.com/api/v10",
|
baseUrl: "https://discord.com/api/v10",
|
||||||
});
|
});
|
||||||
const guildsRequest = await GET("/users/@me/guilds", {
|
const { data: guildsData, error } = await GET("/users/@me/guilds", {
|
||||||
headers: { Authorization: `Bearer ${tokens.accessToken}` },
|
headers: { Authorization: `Bearer ${tokens.accessToken}` },
|
||||||
});
|
});
|
||||||
const channelsRequest = await GET("/guilds/{guild_id}/channels", {
|
const { data: channelsData, error: error2 } = await GET(
|
||||||
|
"/guilds/{guild_id}/channels",
|
||||||
|
{
|
||||||
params: {
|
params: {
|
||||||
path: {
|
path: {
|
||||||
guild_id: id,
|
guild_id: id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
headers: { Authorization: `Bot ${import.meta.env.VITE_DISCORD_BOT_TOKEN}` },
|
headers: {
|
||||||
});
|
Authorization: `Bot ${import.meta.env.VITE_DISCORD_BOT_TOKEN}`,
|
||||||
if (guildsRequest.error || channelsRequest.error) {
|
},
|
||||||
console.log(guildsRequest.error, channelsRequest.error, pathname);
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error || error2) {
|
||||||
|
console.log("Discord api error:", { error, error2 });
|
||||||
|
console.log(error, error2, pathname);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
message: "Error on one of the discord api requests!",
|
message: "Error on one of the discord api requests!",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const guild = guildsRequest.data?.find((e) => e.id === id);
|
const guild = guildsData?.find((e) => e.id === id);
|
||||||
|
|
||||||
if (!guild)
|
if (!guild)
|
||||||
return {
|
return {
|
||||||
|
@ -95,7 +96,7 @@ const getPayload = async (
|
||||||
};
|
};
|
||||||
|
|
||||||
const channels: ReturnType<typeof initialValue>["guild"]["channels"] = [];
|
const channels: ReturnType<typeof initialValue>["guild"]["channels"] = [];
|
||||||
channelsRequest.data?.forEach((channel) => {
|
channelsData?.forEach((channel) => {
|
||||||
if (channel.type !== 0) return;
|
if (channel.type !== 0) return;
|
||||||
channels.push({
|
channels.push({
|
||||||
id: channel.id,
|
id: channel.id,
|
||||||
|
|
|
@ -4,14 +4,12 @@ import {
|
||||||
faPlus,
|
faPlus,
|
||||||
} from "@fortawesome/pro-regular-svg-icons";
|
} from "@fortawesome/pro-regular-svg-icons";
|
||||||
import { useLocation, useNavigate } from "@solidjs/router";
|
import { useLocation, useNavigate } from "@solidjs/router";
|
||||||
import { eq } from "drizzle-orm";
|
|
||||||
import createClient from "openapi-fetch";
|
import createClient from "openapi-fetch";
|
||||||
import { For, createResource } from "solid-js";
|
import { For, createResource } from "solid-js";
|
||||||
import { getRequestEvent } from "solid-js/web";
|
import { getRequestEvent } from "solid-js/web";
|
||||||
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon";
|
import { FontAwesomeIcon } from "~/components/FontAwesomeIcon";
|
||||||
import Layout from "~/components/Layout";
|
import Layout from "~/components/Layout";
|
||||||
import db from "~/drizzle";
|
import getAccessToken from "~/lib/accessToken";
|
||||||
import { discordTokens } from "~/drizzle/schema";
|
|
||||||
import { paths } from "~/types/discord";
|
import { paths } from "~/types/discord";
|
||||||
import "../../styles/pages/config.scss";
|
import "../../styles/pages/config.scss";
|
||||||
|
|
||||||
|
@ -43,11 +41,7 @@ const getPayload = async (): Promise<
|
||||||
const { user } = event.nativeEvent.context;
|
const { user } = event.nativeEvent.context;
|
||||||
if (!user) return { success: false, message: "User not logged in!" };
|
if (!user) return { success: false, message: "User not logged in!" };
|
||||||
|
|
||||||
const tokens = await db.query.discordTokens
|
const tokens = await getAccessToken(user.id);
|
||||||
.findFirst({
|
|
||||||
where: eq(discordTokens.userId, user.id),
|
|
||||||
})
|
|
||||||
.execute();
|
|
||||||
if (!tokens) return { success: false, message: "No discord access token!" };
|
if (!tokens) return { success: false, message: "No discord access token!" };
|
||||||
|
|
||||||
const { GET } = createClient<paths>({
|
const { GET } = createClient<paths>({
|
||||||
|
@ -58,7 +52,7 @@ const getPayload = async (): Promise<
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log(error);
|
console.log("Discord api error:", { error });
|
||||||
return { success: false, message: "Error on discord api request!" };
|
return { success: false, message: "Error on discord api request!" };
|
||||||
}
|
}
|
||||||
console.log(pathname, "success");
|
console.log(pathname, "success");
|
||||||
|
|
Loading…
Reference in a new issue