Skip to content

Commit 3f577a4

Browse files
committed
Add CIMD support
1 parent f08b520 commit 3f577a4

File tree

6 files changed

+59
-14
lines changed

6 files changed

+59
-14
lines changed

.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ PUBLIC_COMMIT_SHA=
163163
ALLOW_INSECURE_COOKIES=false # LEGACY! Use COOKIE_SECURE and COOKIE_SAMESITE instead
164164
PARQUET_EXPORT_SECRET=#DEPRECATED, use ADMIN_API_SECRET instead
165165
RATE_LIMIT= # /!\ DEPRECATED definition of messages per minute. Use USAGE_LIMITS.messagesPerMinute instead
166-
OPENID_CLIENT_ID=
166+
OPENID_CLIENT_ID="" # You can set to "__CIMD__" for automatic oauth app creation when deployed
167167
OPENID_CLIENT_SECRET=
168168
OPENID_SCOPES="openid profile" # Add "email" for some providers like Google that do not provide preferred_username
169169
OPENID_NAME_CLAIM="name" # Change to "username" for some providers that do not provide name

src/hooks.server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ export const handle: Handle = async ({ event, resolve }) => {
133133

134134
const auth = await authenticateRequest(
135135
{ type: "svelte", value: event.request.headers },
136-
{ type: "svelte", value: event.cookies }
136+
{ type: "svelte", value: event.cookies },
137+
event.url
137138
);
138139

139140
event.locals.sessionId = auth.sessionId;

src/lib/server/api/authPlugin.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
import Elysia from "elysia";
22
import { authenticateRequest } from "../auth";
3+
import { config } from "../config";
34

45
export const authPlugin = new Elysia({ name: "auth" }).derive(
56
{ as: "scoped" },
67
async ({
78
headers,
89
cookie,
10+
request,
911
}): Promise<{
1012
locals: App.Locals;
1113
}> => {
14+
request.url;
1215
const auth = await authenticateRequest(
1316
{ type: "elysia", value: headers },
1417
{ type: "elysia", value: cookie },
18+
new URL(request.url, config.PUBLIC_ORIGIN),
1519
true
1620
);
1721
return {

src/lib/server/auth.ts

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ export function refreshSessionCookie(cookies: Cookies, sessionId: string) {
9292

9393
export async function findUser(
9494
sessionId: string,
95-
coupledCookieHash?: string
95+
coupledCookieHash: string | undefined,
96+
url: URL
9697
): Promise<{
9798
user: User | null;
9899
invalidateSession: boolean;
@@ -121,7 +122,8 @@ export async function findUser(
121122
// Attempt to refresh the token
122123
const newTokenSet = await refreshOAuthToken(
123124
{ redirectURI: `${config.PUBLIC_ORIGIN}${base}/login/callback` },
124-
session.oauth.refreshToken
125+
session.oauth.refreshToken,
126+
url
125127
);
126128

127129
if (!newTokenSet || !newTokenSet.access_token) {
@@ -236,7 +238,7 @@ export async function generateCsrfToken(
236238

237239
let lastIssuer: Issuer<BaseClient> | null = null;
238240
let lastIssuerFetchedAt: Date | null = null;
239-
async function getOIDCClient(settings: OIDCSettings): Promise<BaseClient> {
241+
async function getOIDCClient(settings: OIDCSettings, url: URL): Promise<BaseClient> {
240242
if (
241243
lastIssuer &&
242244
lastIssuerFetchedAt &&
@@ -261,6 +263,13 @@ async function getOIDCClient(settings: OIDCSettings): Promise<BaseClient> {
261263
id_token_signed_response_alg: OIDConfig.ID_TOKEN_SIGNED_RESPONSE_ALG || undefined,
262264
};
263265

266+
if (OIDConfig.CLIENT_ID === "__CIMD__") {
267+
OIDConfig.CLIENT_ID = new URL(
268+
"/.well-known/oauth-cimd",
269+
config.PUBLIC_ORIGIN || url.origin
270+
).toString();
271+
}
272+
264273
const alg_supported = issuer.metadata["id_token_signing_alg_values_supported"];
265274

266275
if (Array.isArray(alg_supported)) {
@@ -272,9 +281,9 @@ async function getOIDCClient(settings: OIDCSettings): Promise<BaseClient> {
272281

273282
export async function getOIDCAuthorizationUrl(
274283
settings: OIDCSettings,
275-
params: { sessionId: string; next?: string }
284+
params: { sessionId: string; next?: string; url: URL }
276285
): Promise<string> {
277-
const client = await getOIDCClient(settings);
286+
const client = await getOIDCClient(settings, params.url);
278287
const csrfToken = await generateCsrfToken(
279288
params.sessionId,
280289
settings.redirectURI,
@@ -291,9 +300,10 @@ export async function getOIDCAuthorizationUrl(
291300
export async function getOIDCUserData(
292301
settings: OIDCSettings,
293302
code: string,
294-
iss?: string
303+
iss: string | undefined,
304+
url: URL
295305
): Promise<OIDCUserInfo> {
296-
const client = await getOIDCClient(settings);
306+
const client = await getOIDCClient(settings, url);
297307
const token = await client.callback(settings.redirectURI, { code, iss });
298308
const userData = await client.userinfo(token);
299309

@@ -305,9 +315,10 @@ export async function getOIDCUserData(
305315
*/
306316
export async function refreshOAuthToken(
307317
settings: OIDCSettings,
308-
refreshToken: string
318+
refreshToken: string,
319+
url: URL
309320
): Promise<TokenSet | null> {
310-
const client = await getOIDCClient(settings);
321+
const client = await getOIDCClient(settings, url);
311322
const tokenSet = await client.refresh(refreshToken);
312323
return tokenSet;
313324
}
@@ -371,6 +382,7 @@ export async function getCoupledCookieHash(cookie: CookieRecord): Promise<string
371382
export async function authenticateRequest(
372383
headers: HeaderRecord,
373384
cookie: CookieRecord,
385+
url: URL,
374386
isApi?: boolean
375387
): Promise<App.Locals & { secretSessionId: string }> {
376388
// once the entire API has been moved to elysia
@@ -415,7 +427,7 @@ export async function authenticateRequest(
415427
secretSessionId = token;
416428
sessionId = await sha256(token);
417429

418-
const result = await findUser(sessionId, await getCoupledCookieHash(cookie));
430+
const result = await findUser(sessionId, await getCoupledCookieHash(cookie), url);
419431

420432
if (result.invalidateSession) {
421433
secretSessionId = crypto.randomUUID();
@@ -539,7 +551,7 @@ export async function triggerOauthFlow({
539551

540552
const authorizationUrl = await getOIDCAuthorizationUrl(
541553
{ redirectURI },
542-
{ sessionId: locals.sessionId, next }
554+
{ sessionId: locals.sessionId, next, url }
543555
);
544556

545557
throw redirect(302, authorizationUrl);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { OIDConfig } from "$lib/server/auth";
2+
import { config } from "$lib/server/config";
3+
4+
export const GET = ({ url }) => {
5+
if (!OIDConfig.CLIENT_ID) {
6+
return new Response("Client ID not found", { status: 404 });
7+
}
8+
if (OIDConfig.CLIENT_ID !== "__CIMD__") {
9+
return new Response("Client ID is manually set to something other than '__CIMD__'", {
10+
status: 404,
11+
});
12+
}
13+
return new Response(
14+
JSON.stringify({
15+
client_id: new URL("/.well-known/oauth-cimd", config.PUBLIC_ORIGIN || url.origin).toString(),
16+
client_name: config.PUBLIC_APP_NAME,
17+
redirect_uris: [new URL("/login/callback", config.PUBLIC_ORIGIN || url.origin).toString()],
18+
token_endpoint_auth_method: "none",
19+
scopes: OIDConfig.SCOPES,
20+
}),
21+
{
22+
headers: {
23+
"Content-Type": "application/json",
24+
},
25+
}
26+
);
27+
};

src/routes/login/callback/+server.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ export async function GET({ url, locals, cookies, request, getClientAddress }) {
5555
const { userData, token } = await getOIDCUserData(
5656
{ redirectURI: validatedToken.redirectUrl },
5757
code,
58-
iss
58+
iss,
59+
url
5960
);
6061

6162
// Filter by allowed user emails or domains

0 commit comments

Comments
 (0)