From bc4c3cd1857480ce482f7fb0fa6457320300923d Mon Sep 17 00:00:00 2001 From: Richard Emijere <67247138+The-CodeINN@users.noreply.github.com> Date: Sat, 21 Dec 2024 13:00:47 +0100 Subject: [PATCH 1/2] Auth feature - fourth iteration Fixes #30 Implement the OAuth flow state object storage in cookies and the signout/disconnect feature. * Store the OAuth flow state object in cookies in `src/lib/actions/do-auth.js` and compare it with the `state` param in the `doAuth` function. * Retrieve the stored state from cookies in `src/pages/api/github/oauth/callback.js`, compare it with the `state` param, and delete the stored state from cookies after comparison. * Update the signout/disconnect link in `src/components/islands/profile.jsx` to point to the new signout route. * Update the signout logic in `src/pages/logout.astro` to use the new signout function from `doAuth`. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/jargonsdev/jargons.dev/issues/30?shareId=XXXX-XXXX-XXXX-XXXX). --- src/components/islands/profile.jsx | 4 ++-- src/lib/actions/do-auth.js | 17 +++++++++++++++-- src/pages/api/github/oauth/callback.js | 14 +++++++++++++- src/pages/logout.astro | 14 ++++++++++---- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/components/islands/profile.jsx b/src/components/islands/profile.jsx index 88c6431a..380d968b 100644 --- a/src/components/islands/profile.jsx +++ b/src/components/islands/profile.jsx @@ -67,7 +67,7 @@ export default function Profile({ isAuthed, userData, authUrl }) { {/* Logout/Disconnect */}
); -} \ No newline at end of file +} diff --git a/src/lib/actions/do-auth.js b/src/lib/actions/do-auth.js index 7928b1da..39485009 100644 --- a/src/lib/actions/do-auth.js +++ b/src/lib/actions/do-auth.js @@ -22,7 +22,6 @@ export default async function doAuth(astroGlobal) { /** * Generate OAuth Url to start authorization flow - * @todo improvement: store `state` in cookie for later retrieval/comparison with auth `state` in `github/oauth/callback` * @param {{ path: string }} state */ function getAuthUrl(state) { @@ -39,10 +38,16 @@ export default async function doAuth(astroGlobal) { if (otherStates.length > 0) parsedState += `|${otherStates}`; } - const { url } = app.oauth.getWebFlowAuthorizationUrl({ + const { url, state: generatedState } = app.oauth.getWebFlowAuthorizationUrl({ state: parsedState, }); + cookies.set("oauthState", generatedState, { + expires: resolveCookieExpiryDate(600), + path: "/", + encode: (value) => encrypt(value), + }); + return url; } @@ -60,6 +65,14 @@ export default async function doAuth(astroGlobal) { } } + const storedState = cookies.get("oauthState", { + decode: (value) => decrypt(value), + }); + + if (storedState !== searchParams.get("state")) { + throw new Error("Invalid OAuth state"); + } + const userOctokit = app.getUserOctokit({ token: accessToken.value }); const { data } = await userOctokit.request("GET /user"); diff --git a/src/pages/api/github/oauth/callback.js b/src/pages/api/github/oauth/callback.js index 38d00a68..05a830d7 100644 --- a/src/pages/api/github/oauth/callback.js +++ b/src/pages/api/github/oauth/callback.js @@ -1,8 +1,10 @@ +import { decrypt } from "../../../../lib/utils/crypto.js"; + /** * GitHub OAuth Callback route handler * @param {import("astro").APIContext} context */ -export async function GET({ url: { searchParams }, redirect }) { +export async function GET({ url: { searchParams }, redirect, cookies }) { const code = searchParams.get("code"); const state = searchParams.get("state"); @@ -10,6 +12,16 @@ export async function GET({ url: { searchParams }, redirect }) { return new Response(null, { status: 400 }); } + const storedState = cookies.get("oauthState", { + decode: (value) => decrypt(value), + }); + + if (storedState !== state) { + return new Response("Invalid OAuth state", { status: 400 }); + } + + cookies.delete("oauthState"); + const path = state.includes("path") && state.split("|")[0].split(":")[1]; if (path) return redirect(`${path}?code=${code}`); diff --git a/src/pages/logout.astro b/src/pages/logout.astro index dddaeabf..7e3ef205 100644 --- a/src/pages/logout.astro +++ b/src/pages/logout.astro @@ -1,8 +1,14 @@ --- -import doLogout from "../lib/actions/do-logout.js"; +import doAuth from "../lib/actions/do-auth.js"; const { url: { searchParams }, redirect } = Astro; -const { isLoggedOut } = await doLogout(Astro); -if (isLoggedOut) return redirect(searchParams.get("return_to") || "/"); ---- \ No newline at end of file +const { isAuthed, getAuthUrl } = await doAuth(Astro); + +if (isAuthed) { + const { isLoggedOut } = await doAuth(Astro, { action: "logout" }); + if (isLoggedOut) return redirect(searchParams.get("return_to") || "/"); +} else { + return redirect(getAuthUrl({ path: "/logout" })); +} +--- From f816ad70a27eef338dd6ede6f761025de8c838f7 Mon Sep 17 00:00:00 2001 From: Richard Emijere <67247138+The-CodeINN@users.noreply.github.com> Date: Mon, 23 Dec 2024 07:52:51 +0100 Subject: [PATCH 2/2] Update profile.jsx --- src/components/islands/profile.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/islands/profile.jsx b/src/components/islands/profile.jsx index 380d968b..5cdb246e 100644 --- a/src/components/islands/profile.jsx +++ b/src/components/islands/profile.jsx @@ -67,7 +67,7 @@ export default function Profile({ isAuthed, userData, authUrl }) { {/* Logout/Disconnect */}