aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/auth.server.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/auth.server.ts')
-rw-r--r--src/lib/auth.server.ts73
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;
+};