Skip to content

Instantly share code, notes, and snippets.

@JacobWeinbren
Last active May 14, 2024 08:35
Show Gist options
  • Save JacobWeinbren/c2763d3b4ca5cd5fe749be887afb7845 to your computer and use it in GitHub Desktop.
Save JacobWeinbren/c2763d3b4ca5cd5fe749be887afb7845 to your computer and use it in GitHub Desktop.
Auth System in Astro using Kinde - Supports Cloudflare Serverless Edge Hosting
PUBLIC_KINDE_DOMAIN=***
PUBLIC_KINDE_CLIENT_ID=***
PUBLIC_KINDE_CLIENT_SECRET=***
PUBLIC_KINDE_REDIRECT_URI_DEV=***
PUBLIC_KINDE_LOGOUT_REDIRECT_URI_DEV=***
PUBLIC_KINDE_REDIRECT_URI_PROD=***
PUBLIC_KINDE_LOGOUT_REDIRECT_URI_PROD=***
export function generateRandomState(length: number = 16): string {
const characters =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
return Array.from(
{ length },
() => characters[Math.floor(Math.random() * characters.length)],
).join("");
}
export async function fetchJwks() {
const response = await fetch(
`${import.meta.env.PUBLIC_KINDE_DOMAIN}/.well-known/jwks.json`,
);
if (!response.ok) {
throw new Error(`Failed to fetch JWKS: ${response.statusText}`);
}
return await response.json();
}
export function safeBase64Decode(base64String: string) {
base64String = base64String.replace(/-/g, "+").replace(/_/g, "/");
while (base64String.length % 4) {
base64String += "=";
}
return atob(base64String);
}
async function getSigningKey(kid: string) {
const jwks = await fetchJwks();
const signingKey = jwks.keys.find((key: any) => key.kid === kid);
if (!signingKey) {
throw new Error(`Unable to find a signing key that matches '${kid}'`);
}
return signingKey;
}
export const isLoggedIn = async (
accessToken: string | null,
): Promise<boolean> => {
if (!accessToken) return false;
const tokenParts = accessToken.split(".");
if (tokenParts.length !== 3) {
return false;
}
try {
const header = JSON.parse(safeBase64Decode(tokenParts[0]));
const signingKey = await getSigningKey(header.kid);
const publicKey = await crypto.subtle.importKey(
"jwk",
signingKey,
{ name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" },
false,
["verify"],
);
const isValid = await crypto.subtle.verify(
"RSASSA-PKCS1-v1_5",
publicKey,
new Uint8Array(
Array.from(safeBase64Decode(tokenParts[2]), (c) =>
c.charCodeAt(0),
),
),
new TextEncoder().encode(tokenParts[0] + "." + tokenParts[1]),
);
return isValid;
} catch (error) {
console.error(error);
return false;
}
};
---
import { generateRandomState, isLoggedIn } from "../utils/auth";
const accessTokenCookie = Astro.cookies.get("access_token");
const accessToken = accessTokenCookie ? accessTokenCookie.value : null;
const loggedIn = await isLoggedIn(accessToken);
if (loggedIn) {
return Astro.redirect("/");
}
const state = generateRandomState();
const redirectUri = import.meta.env.PROD
? import.meta.env.PUBLIC_KINDE_REDIRECT_URI_PROD
: import.meta.env.PUBLIC_KINDE_REDIRECT_URI_DEV;
const kindeLoginUrl = `${import.meta.env.PUBLIC_KINDE_DOMAIN}/oauth2/auth?response_type=code&client_id=${import.meta.env.PUBLIC_KINDE_CLIENT_ID}&redirect_uri=${redirectUri}&scope=openid%20profile%20email&state=${state}`;
return Astro.redirect(kindeLoginUrl);
---
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment