coyotte508 commited on
Commit
46d21da
·
1 Parent(s): 8ce6ae3

support PKCE

Browse files
src/hooks.server.ts CHANGED
@@ -148,11 +148,7 @@ export const handle: Handle = async ({ event, resolve }) => {
148
  ) {
149
  // To get the same CSRF token after callback
150
  refreshSessionCookie(event.cookies, auth.secretSessionId);
151
- return await triggerOauthFlow({
152
- request: event.request,
153
- url: event.url,
154
- locals: event.locals,
155
- });
156
  }
157
  } else {
158
  // Redirect to OAuth flow unless on the authorized pages (home, shared conversation, login, healthcheck, model thumbnails)
@@ -168,7 +164,7 @@ export const handle: Handle = async ({ event, resolve }) => {
168
  !event.url.pathname.startsWith(`${base}/api`)
169
  ) {
170
  refreshSessionCookie(event.cookies, auth.secretSessionId);
171
- return triggerOauthFlow({ request: event.request, url: event.url, locals: event.locals });
172
  }
173
  }
174
  }
 
148
  ) {
149
  // To get the same CSRF token after callback
150
  refreshSessionCookie(event.cookies, auth.secretSessionId);
151
+ return await triggerOauthFlow(event);
 
 
 
 
152
  }
153
  } else {
154
  // Redirect to OAuth flow unless on the authorized pages (home, shared conversation, login, healthcheck, model thumbnails)
 
164
  !event.url.pathname.startsWith(`${base}/api`)
165
  ) {
166
  refreshSessionCookie(event.cookies, auth.secretSessionId);
167
+ return triggerOauthFlow(event);
168
  }
169
  }
170
  }
src/lib/server/auth.ts CHANGED
@@ -4,7 +4,9 @@ import {
4
  type UserinfoResponse,
5
  type TokenSet,
6
  custom,
 
7
  } from "openid-client";
 
8
  import { addHours, addWeeks, differenceInMinutes, subMinutes } from "date-fns";
9
  import { config } from "$lib/server/config";
10
  import { sha256 } from "$lib/utils/sha256";
@@ -281,7 +283,7 @@ async function getOIDCClient(settings: OIDCSettings, url: URL): Promise<BaseClie
281
 
282
  export async function getOIDCAuthorizationUrl(
283
  settings: OIDCSettings,
284
- params: { sessionId: string; next?: string; url: URL }
285
  ): Promise<string> {
286
  const client = await getOIDCClient(settings, params.url);
287
  const csrfToken = await generateCsrfToken(
@@ -290,7 +292,20 @@ export async function getOIDCAuthorizationUrl(
290
  sanitizeReturnPath(params.next)
291
  );
292
 
 
 
 
 
 
 
 
 
 
 
 
293
  return client.authorizationUrl({
 
 
294
  scope: OIDConfig.SCOPES,
295
  state: csrfToken,
296
  resource: OIDConfig.RESOURCE || undefined,
@@ -300,11 +315,16 @@ export async function getOIDCAuthorizationUrl(
300
  export async function getOIDCUserData(
301
  settings: OIDCSettings,
302
  code: string,
 
303
  iss: string | undefined,
304
  url: URL
305
  ): Promise<OIDCUserInfo> {
306
  const client = await getOIDCClient(settings, url);
307
- const token = await client.callback(settings.redirectURI, { code, iss });
 
 
 
 
308
  const userData = await client.userinfo(token);
309
 
310
  return { token, userData };
@@ -514,14 +534,7 @@ export async function authenticateRequest(
514
  return { user: undefined, sessionId, secretSessionId, isAdmin: false };
515
  }
516
 
517
- export async function triggerOauthFlow({
518
- url,
519
- locals,
520
- }: {
521
- request: Request;
522
- url: URL;
523
- locals: App.Locals;
524
- }): Promise<Response> {
525
  // const referer = request.headers.get("referer");
526
  // let redirectURI = `${(referer ? new URL(referer) : url).origin}${base}/login/callback`;
527
  let redirectURI = `${url.origin}${base}/login/callback`;
@@ -551,7 +564,7 @@ export async function triggerOauthFlow({
551
 
552
  const authorizationUrl = await getOIDCAuthorizationUrl(
553
  { redirectURI },
554
- { sessionId: locals.sessionId, next, url }
555
  );
556
 
557
  throw redirect(302, authorizationUrl);
 
4
  type UserinfoResponse,
5
  type TokenSet,
6
  custom,
7
+ generators,
8
  } from "openid-client";
9
+ import type { RequestEvent } from "@sveltejs/kit";
10
  import { addHours, addWeeks, differenceInMinutes, subMinutes } from "date-fns";
11
  import { config } from "$lib/server/config";
12
  import { sha256 } from "$lib/utils/sha256";
 
283
 
284
  export async function getOIDCAuthorizationUrl(
285
  settings: OIDCSettings,
286
+ params: { sessionId: string; next?: string; url: URL; cookies: Cookies }
287
  ): Promise<string> {
288
  const client = await getOIDCClient(settings, params.url);
289
  const csrfToken = await generateCsrfToken(
 
292
  sanitizeReturnPath(params.next)
293
  );
294
 
295
+ const codeVerifier = generators.codeVerifier();
296
+ const codeChallenge = generators.codeChallenge(codeVerifier);
297
+
298
+ params.cookies.set("hfChat-codeVerifier", codeVerifier, {
299
+ path: "/",
300
+ sameSite,
301
+ secure,
302
+ httpOnly: true,
303
+ expires: addHours(new Date(), 1),
304
+ });
305
+
306
  return client.authorizationUrl({
307
+ code_challenge_method: "S256",
308
+ code_challenge: codeChallenge,
309
  scope: OIDConfig.SCOPES,
310
  state: csrfToken,
311
  resource: OIDConfig.RESOURCE || undefined,
 
315
  export async function getOIDCUserData(
316
  settings: OIDCSettings,
317
  code: string,
318
+ codeVerifier: string,
319
  iss: string | undefined,
320
  url: URL
321
  ): Promise<OIDCUserInfo> {
322
  const client = await getOIDCClient(settings, url);
323
+ const token = await client.callback(settings.redirectURI, {
324
+ code,
325
+ iss,
326
+ checks: { code_verifier: codeVerifier },
327
+ });
328
  const userData = await client.userinfo(token);
329
 
330
  return { token, userData };
 
534
  return { user: undefined, sessionId, secretSessionId, isAdmin: false };
535
  }
536
 
537
+ export async function triggerOauthFlow({ url, locals, cookies }: RequestEvent): Promise<Response> {
 
 
 
 
 
 
 
538
  // const referer = request.headers.get("referer");
539
  // let redirectURI = `${(referer ? new URL(referer) : url).origin}${base}/login/callback`;
540
  let redirectURI = `${url.origin}${base}/login/callback`;
 
564
 
565
  const authorizationUrl = await getOIDCAuthorizationUrl(
566
  { redirectURI },
567
+ { sessionId: locals.sessionId, next, url, cookies }
568
  );
569
 
570
  throw redirect(302, authorizationUrl);
src/routes/login/+server.ts CHANGED
@@ -1,5 +1,5 @@
1
  import { triggerOauthFlow } from "$lib/server/auth";
2
 
3
- export async function GET({ request, url, locals }) {
4
- return await triggerOauthFlow({ request, url, locals });
5
  }
 
1
  import { triggerOauthFlow } from "$lib/server/auth";
2
 
3
+ export async function GET(event) {
4
+ return await triggerOauthFlow(event);
5
  }
src/routes/login/callback/+server.ts CHANGED
@@ -52,9 +52,15 @@ export async function GET({ url, locals, cookies, request, getClientAddress }) {
52
  throw error(403, "Invalid or expired CSRF token");
53
  }
54
 
 
 
 
 
 
55
  const { userData, token } = await getOIDCUserData(
56
  { redirectURI: validatedToken.redirectUrl },
57
  code,
 
58
  iss,
59
  url
60
  );
 
52
  throw error(403, "Invalid or expired CSRF token");
53
  }
54
 
55
+ const codeVerifier = cookies.get("hfChat-codeVerifier");
56
+ if (!codeVerifier) {
57
+ throw error(403, "Code verifier cookie not found");
58
+ }
59
+
60
  const { userData, token } = await getOIDCUserData(
61
  { redirectURI: validatedToken.redirectUrl },
62
  code,
63
+ codeVerifier,
64
  iss,
65
  url
66
  );