diff options
feat: oidc attempt 1
Diffstat (limited to 'src/lib/auth.server.ts')
| -rw-r--r-- | src/lib/auth.server.ts | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/src/lib/auth.server.ts b/src/lib/auth.server.ts new file mode 100644 index 0000000..77e0dd7 --- /dev/null +++ b/src/lib/auth.server.ts @@ -0,0 +1,73 @@ +import { env as env_priv } from '$env/dynamic/private'; +import { env } from '$env/dynamic/public'; +import * as client from 'openid-client'; +import oncePromise from './oncePromise'; + +const server = new URL(env.PUBLIC_AUTH_KEYCLOAK_ISSUER); +const clientId = env_priv.PRIVATE_AUTH_KEYCLOAK_ID; +const clientSecret = env_priv.PRIVATE_AUTH_KEYCLOAK_SECRET; +const redirectPath = '/login/callback'; + +// Only trigger discovery on first client.discovery (resetting the function after a failed discovery) +export const getConfig = oncePromise(() => + client.discovery(server, clientId, clientSecret) +); +const codeVerifier = client.randomPKCECodeVerifier(); + +export const getAuthorizeUrl = async ( + currentUrl: URL | string, + scope: string[] +) => { + if (!scope.includes('openid')) scope.unshift('openid'); + // do same for `email` maybe? + + const config = await getConfig(); + const redirectUri = new URL(redirectPath, currentUrl); + const codeChallenge = await client.calculatePKCECodeChallenge(codeVerifier); + const codeChallengeMethod = 'S256'; + let nonce: string | undefined = undefined; + + // redirect user to as.authorization_endpoint + let parameters: Record<string, string> = { + redirect_uri: redirectUri.href, + scope: scope.join(' '), + code_challenge: codeChallenge, + code_challenge_method: codeChallengeMethod, + }; + + /** + * We cannot be sure the AS supports PKCE so we're going to use nonce too. Use + * of PKCE is backwards compatible even if the AS doesn't support it which is + * why we're using it regardless. + */ + if (!config.serverMetadata().supportsPKCE()) { + nonce = client.randomNonce(); + parameters.nonce = nonce; + } + + const redirectTo = client.buildAuthorizationUrl(config, parameters); + + return { + /** Defined if PKCE isnt supported */ + nonce, + /** Redirect Target URL */ + redirectTo, + /** Where we get the user back on */ + returnURI: redirectUri, + }; +}; +/** Throws on failure */ +export const authorizeNewSession = async ( + currentUrl: URL, + nonce: string | undefined +) => { + const config = await getConfig(); + + let tokens = await client.authorizationCodeGrant(config, currentUrl, { + pkceCodeVerifier: codeVerifier, + expectedNonce: nonce, + idTokenExpected: true, + }); + + return tokens; +}; |