-
-
Save JacobWeinbren/c2763d3b4ca5cd5fe749be887afb7845 to your computer and use it in GitHub Desktop.
Auth System in Astro using Kinde - Supports Cloudflare Serverless Edge Hosting
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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=*** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
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