Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance Dynamic User Attribute Handling in OIDC Integration #885

Merged
merged 10 commits into from Mar 22, 2024
4 changes: 3 additions & 1 deletion .env
Expand Up @@ -29,13 +29,15 @@ OPENID_CONFIG=`{
"PROVIDER_URL": "",
"CLIENT_ID": "",
"CLIENT_SECRET": "",
"SCOPES": ""
"SCOPES": "",
"NAME_CLAIM": ""
}`

# /!\ legacy openid settings, prefer the config above
OPENID_CLIENT_ID=
OPENID_CLIENT_SECRET=
OPENID_SCOPES="openid profile" # Add "email" for some providers like Google that do not provide preferred_username
OPENID_NAME_CLAIM="name" # Change to "username" for some providers that do not provide name
OPENID_PROVIDER_URL=https://huggingface.co # for Google, use https://accounts.google.com
OPENID_TOLERANCE=
OPENID_RESOURCE=
Expand Down
7 changes: 6 additions & 1 deletion src/lib/server/auth.ts
Expand Up @@ -6,6 +6,7 @@ import {
OPENID_CLIENT_SECRET,
OPENID_PROVIDER_URL,
OPENID_SCOPES,
OPENID_NAME_CLAIM,
OPENID_TOLERANCE,
OPENID_RESOURCE,
OPENID_CONFIG,
Expand All @@ -32,12 +33,16 @@ const stringWithDefault = (value: string) =>
.default(value)
.transform((el) => (el ? el : value));

const OIDConfig = z
export const OIDConfig = z
.object({
CLIENT_ID: stringWithDefault(OPENID_CLIENT_ID),
CLIENT_SECRET: stringWithDefault(OPENID_CLIENT_SECRET),
PROVIDER_URL: stringWithDefault(OPENID_PROVIDER_URL),
SCOPES: stringWithDefault(OPENID_SCOPES),
NAME_CLAIM: stringWithDefault(OPENID_NAME_CLAIM).refine(
(el) => !["preferred_username", "email", "picture", "sub"].includes(el),
{ message: "nameClaim cannot be one of the restricted keys." }
),
TOLERANCE: stringWithDefault(OPENID_TOLERANCE),
RESOURCE: stringWithDefault(OPENID_RESOURCE),
})
Expand Down
17 changes: 16 additions & 1 deletion src/routes/login/callback/updateUser.ts
Expand Up @@ -8,6 +8,7 @@ import { error, type Cookies } from "@sveltejs/kit";
import crypto from "crypto";
import { sha256 } from "$lib/utils/sha256";
import { addWeeks } from "date-fns";
import { OIDConfig } from "$lib/server/auth";

export async function updateUser(params: {
userData: UserinfoResponse;
Expand Down Expand Up @@ -38,10 +39,24 @@ export async function updateUser(params: {
sub: z.string(),
email: z.string().email().optional(),
})
.setKey(OIDConfig.NAME_CLAIM, z.string())
.refine((data) => data.preferred_username || data.email, {
message: "Either preferred_username or email must be provided by the provider.",
})
.parse(userData);
.transform((data) => ({
...data,
name: data[OIDConfig.NAME_CLAIM],
}))
.parse(userData) as {
preferred_username?: string;
email?: string;
picture?: string;
sub: string;
name: string;
} & Record<string, string>;

// Dynamically access user data based on NAME_CLAIM from environment
// This approach allows us to adapt to different OIDC providers flexibly.

// check if user already exists
const existingUser = await collections.users.findOne({ hfUserId });
Expand Down