44 type UserinfoResponse ,
55 type TokenSet ,
66 custom ,
7+ generators ,
78} from "openid-client" ;
9+ import type { RequestEvent } from "@sveltejs/kit" ;
810import { addHours , addWeeks , differenceInMinutes , subMinutes } from "date-fns" ;
911import { config } from "$lib/server/config" ;
1012import { sha256 } from "$lib/utils/sha256" ;
@@ -54,7 +56,7 @@ export const OIDConfig = z
5456 } )
5557 . parse ( JSON5 . parse ( config . OPENID_CONFIG || "{}" ) ) ;
5658
57- export const loginEnabled = ! ! OIDConfig . CLIENT_ID && ! ! OIDConfig . CLIENT_SECRET ;
59+ export const loginEnabled = ! ! OIDConfig . CLIENT_ID ;
5860
5961const sameSite = z
6062 . enum ( [ "lax" , "none" , "strict" ] )
@@ -92,7 +94,8 @@ export function refreshSessionCookie(cookies: Cookies, sessionId: string) {
9294
9395export async function findUser (
9496 sessionId : string ,
95- coupledCookieHash ?: string
97+ coupledCookieHash : string | undefined ,
98+ url : URL
9699) : Promise < {
97100 user : User | null ;
98101 invalidateSession : boolean ;
@@ -121,7 +124,8 @@ export async function findUser(
121124 // Attempt to refresh the token
122125 const newTokenSet = await refreshOAuthToken (
123126 { redirectURI : `${ config . PUBLIC_ORIGIN } ${ base } /login/callback` } ,
124- session . oauth . refreshToken
127+ session . oauth . refreshToken ,
128+ url
125129 ) ;
126130
127131 if ( ! newTokenSet || ! newTokenSet . access_token ) {
@@ -236,7 +240,7 @@ export async function generateCsrfToken(
236240
237241let lastIssuer : Issuer < BaseClient > | null = null ;
238242let lastIssuerFetchedAt : Date | null = null ;
239- async function getOIDCClient ( settings : OIDCSettings ) : Promise < BaseClient > {
243+ async function getOIDCClient ( settings : OIDCSettings , url : URL ) : Promise < BaseClient > {
240244 if (
241245 lastIssuer &&
242246 lastIssuerFetchedAt &&
@@ -261,6 +265,14 @@ async function getOIDCClient(settings: OIDCSettings): Promise<BaseClient> {
261265 id_token_signed_response_alg : OIDConfig . ID_TOKEN_SIGNED_RESPONSE_ALG || undefined ,
262266 } ;
263267
268+ if ( OIDConfig . CLIENT_ID === "__CIMD__" ) {
269+ // See https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/
270+ client_config . client_id = new URL (
271+ `${ base } /.well-known/oauth-cimd` ,
272+ config . PUBLIC_ORIGIN || url . origin
273+ ) . toString ( ) ;
274+ }
275+
264276 const alg_supported = issuer . metadata [ "id_token_signing_alg_values_supported" ] ;
265277
266278 if ( Array . isArray ( alg_supported ) ) {
@@ -272,16 +284,29 @@ async function getOIDCClient(settings: OIDCSettings): Promise<BaseClient> {
272284
273285export async function getOIDCAuthorizationUrl (
274286 settings : OIDCSettings ,
275- params : { sessionId : string ; next ?: string }
287+ params : { sessionId : string ; next ?: string ; url : URL ; cookies : Cookies }
276288) : Promise < string > {
277- const client = await getOIDCClient ( settings ) ;
289+ const client = await getOIDCClient ( settings , params . url ) ;
278290 const csrfToken = await generateCsrfToken (
279291 params . sessionId ,
280292 settings . redirectURI ,
281293 sanitizeReturnPath ( params . next )
282294 ) ;
283295
296+ const codeVerifier = generators . codeVerifier ( ) ;
297+ const codeChallenge = generators . codeChallenge ( codeVerifier ) ;
298+
299+ params . cookies . set ( "hfChat-codeVerifier" , codeVerifier , {
300+ path : "/" ,
301+ sameSite,
302+ secure,
303+ httpOnly : true ,
304+ expires : addHours ( new Date ( ) , 1 ) ,
305+ } ) ;
306+
284307 return client . authorizationUrl ( {
308+ code_challenge_method : "S256" ,
309+ code_challenge : codeChallenge ,
285310 scope : OIDConfig . SCOPES ,
286311 state : csrfToken ,
287312 resource : OIDConfig . RESOURCE || undefined ,
@@ -291,10 +316,19 @@ export async function getOIDCAuthorizationUrl(
291316export async function getOIDCUserData (
292317 settings : OIDCSettings ,
293318 code : string ,
294- iss ?: string
319+ codeVerifier : string ,
320+ iss : string | undefined ,
321+ url : URL
295322) : Promise < OIDCUserInfo > {
296- const client = await getOIDCClient ( settings ) ;
297- const token = await client . callback ( settings . redirectURI , { code, iss } ) ;
323+ const client = await getOIDCClient ( settings , url ) ;
324+ const token = await client . callback (
325+ settings . redirectURI ,
326+ {
327+ code,
328+ iss,
329+ } ,
330+ { code_verifier : codeVerifier }
331+ ) ;
298332 const userData = await client . userinfo ( token ) ;
299333
300334 return { token, userData } ;
@@ -305,9 +339,10 @@ export async function getOIDCUserData(
305339 */
306340export async function refreshOAuthToken (
307341 settings : OIDCSettings ,
308- refreshToken : string
342+ refreshToken : string ,
343+ url : URL
309344) : Promise < TokenSet | null > {
310- const client = await getOIDCClient ( settings ) ;
345+ const client = await getOIDCClient ( settings , url ) ;
311346 const tokenSet = await client . refresh ( refreshToken ) ;
312347 return tokenSet ;
313348}
@@ -371,6 +406,7 @@ export async function getCoupledCookieHash(cookie: CookieRecord): Promise<string
371406export async function authenticateRequest (
372407 headers : HeaderRecord ,
373408 cookie : CookieRecord ,
409+ url : URL ,
374410 isApi ?: boolean
375411) : Promise < App . Locals & { secretSessionId : string } > {
376412 // once the entire API has been moved to elysia
@@ -415,7 +451,7 @@ export async function authenticateRequest(
415451 secretSessionId = token ;
416452 sessionId = await sha256 ( token ) ;
417453
418- const result = await findUser ( sessionId , await getCoupledCookieHash ( cookie ) ) ;
454+ const result = await findUser ( sessionId , await getCoupledCookieHash ( cookie ) , url ) ;
419455
420456 if ( result . invalidateSession ) {
421457 secretSessionId = crypto . randomUUID ( ) ;
@@ -502,14 +538,7 @@ export async function authenticateRequest(
502538 return { user : undefined , sessionId, secretSessionId, isAdmin : false } ;
503539}
504540
505- export async function triggerOauthFlow ( {
506- url,
507- locals,
508- } : {
509- request : Request ;
510- url : URL ;
511- locals : App . Locals ;
512- } ) : Promise < Response > {
541+ export async function triggerOauthFlow ( { url, locals, cookies } : RequestEvent ) : Promise < Response > {
513542 // const referer = request.headers.get("referer");
514543 // let redirectURI = `${(referer ? new URL(referer) : url).origin}${base}/login/callback`;
515544 let redirectURI = `${ url . origin } ${ base } /login/callback` ;
@@ -539,7 +568,7 @@ export async function triggerOauthFlow({
539568
540569 const authorizationUrl = await getOIDCAuthorizationUrl (
541570 { redirectURI } ,
542- { sessionId : locals . sessionId , next }
571+ { sessionId : locals . sessionId , next, url , cookies }
543572 ) ;
544573
545574 throw redirect ( 302 , authorizationUrl ) ;
0 commit comments