Andrew commited on
Commit
64950db
·
1 Parent(s): f941835

refactor(auth): adapt OIDC user update for multi-provider logic

Browse files
src/routes/login/callback/updateUser.ts CHANGED
@@ -1,16 +1,11 @@
1
- import { getCoupledCookieHash, refreshSessionCookie } from "$lib/server/auth";
2
- import { collections } from "$lib/server/database";
3
- import { ObjectId } from "mongodb";
4
- import { DEFAULT_SETTINGS } from "$lib/types/Settings";
5
  import { z } from "zod";
6
  import type { UserinfoResponse } from "openid-client";
7
- import { error, type Cookies } from "@sveltejs/kit";
8
- import crypto from "crypto";
9
- import { sha256 } from "$lib/utils/sha256";
10
- import { addWeeks } from "date-fns";
11
  import { OIDConfig } from "$lib/server/auth";
12
  import { config } from "$lib/server/config";
13
- import { logger } from "$lib/server/logger";
 
 
 
14
 
15
  export async function updateUser(params: {
16
  userData: UserinfoResponse;
@@ -18,8 +13,10 @@ export async function updateUser(params: {
18
  cookies: Cookies;
19
  userAgent?: string;
20
  ip?: string;
 
 
21
  }) {
22
- const { userData, locals, cookies, userAgent, ip } = params;
23
 
24
  // Microsoft Entra v1 tokens do not provide preferred_username, instead the username is provided in the upn
25
  // claim. See https://learn.microsoft.com/en-us/entra/identity-platform/access-token-claims-reference
@@ -76,130 +73,32 @@ export async function updateUser(params: {
76
  }>;
77
  } & Record<string, string>;
78
 
79
- // Dynamically access user data based on NAME_CLAIM from environment
80
- // This approach allows us to adapt to different OIDC providers flexibly.
81
-
82
- logger.info(
83
- {
84
- login_username: username,
85
- login_name: name,
86
- login_email: email,
87
- login_orgs: orgs?.map((el) => el.sub),
88
- },
89
- "user login"
90
- );
91
- // if using huggingface as auth provider, check orgs for earl access and amin rights
92
  const isAdmin =
93
- (config.HF_ORG_ADMIN && orgs?.some((org) => org.sub === config.HF_ORG_ADMIN)) || false;
 
 
94
  const isEarlyAccess =
95
- (config.HF_ORG_EARLY_ACCESS && orgs?.some((org) => org.sub === config.HF_ORG_EARLY_ACCESS)) ||
96
- false;
97
-
98
- logger.debug(
99
- {
100
- isAdmin,
101
- isEarlyAccess,
102
- hfUserId,
103
- },
104
- `Updating user ${hfUserId}`
105
- );
106
-
107
- // check if user already exists
108
- const existingUser = await collections.users.findOne({ hfUserId });
109
- let userId = existingUser?._id;
110
-
111
- // update session cookie on login
112
- const previousSessionId = locals.sessionId;
113
- const secretSessionId = crypto.randomUUID();
114
- const sessionId = await sha256(secretSessionId);
115
-
116
- if (await collections.sessions.findOne({ sessionId })) {
117
- error(500, "Session ID collision");
118
- }
119
-
120
- locals.sessionId = sessionId;
121
-
122
- // Get cookie hash if coupling is enabled
123
- const coupledCookieHash = await getCoupledCookieHash({ type: "svelte", value: cookies });
124
-
125
- if (existingUser) {
126
- // update existing user if any
127
- await collections.users.updateOne(
128
- { _id: existingUser._id },
129
- { $set: { username, name, avatarUrl, isAdmin, isEarlyAccess } }
130
- );
131
-
132
- // remove previous session if it exists and add new one
133
- await collections.sessions.deleteOne({ sessionId: previousSessionId });
134
- await collections.sessions.insertOne({
135
- _id: new ObjectId(),
136
- sessionId: locals.sessionId,
137
- userId: existingUser._id,
138
- createdAt: new Date(),
139
- updatedAt: new Date(),
140
- userAgent,
141
- ip,
142
- expiresAt: addWeeks(new Date(), 2),
143
- ...(coupledCookieHash ? { coupledCookieHash } : {}),
144
- });
145
- } else {
146
- // user doesn't exist yet, create a new one
147
- const { insertedId } = await collections.users.insertOne({
148
- _id: new ObjectId(),
149
- createdAt: new Date(),
150
- updatedAt: new Date(),
151
  username,
152
  name,
153
  email,
154
  avatarUrl,
155
- hfUserId,
156
  isAdmin,
157
  isEarlyAccess,
158
- });
159
-
160
- userId = insertedId;
161
-
162
- await collections.sessions.insertOne({
163
- _id: new ObjectId(),
164
- sessionId: locals.sessionId,
165
- userId,
166
- createdAt: new Date(),
167
- updatedAt: new Date(),
168
- userAgent,
169
- ip,
170
- expiresAt: addWeeks(new Date(), 2),
171
- ...(coupledCookieHash ? { coupledCookieHash } : {}),
172
- });
173
-
174
- // move pre-existing settings to new user
175
- const { matchedCount } = await collections.settings.updateOne(
176
- { sessionId: previousSessionId },
177
- {
178
- $set: { userId, updatedAt: new Date() },
179
- $unset: { sessionId: "" },
180
- }
181
- );
182
-
183
- if (!matchedCount) {
184
- // if no settings found for user, create default settings
185
- await collections.settings.insertOne({
186
- userId,
187
- updatedAt: new Date(),
188
- createdAt: new Date(),
189
- ...DEFAULT_SETTINGS,
190
- });
191
- }
192
- }
193
-
194
- // refresh session cookie
195
- refreshSessionCookie(cookies, secretSessionId);
196
-
197
- // migrate pre-existing conversations
198
- await collections.conversations.updateMany(
199
- { sessionId: previousSessionId },
200
- {
201
- $set: { userId },
202
- $unset: { sessionId: "" },
203
- }
204
- );
205
  }
 
 
 
 
 
1
  import { z } from "zod";
2
  import type { UserinfoResponse } from "openid-client";
 
 
 
 
3
  import { OIDConfig } from "$lib/server/auth";
4
  import { config } from "$lib/server/config";
5
+ import { updateUserSession } from "./userSession";
6
+ import type { Cookies } from "@sveltejs/kit";
7
+
8
+ import type { AuthProvider } from "$lib/types/User";
9
 
10
  export async function updateUser(params: {
11
  userData: UserinfoResponse;
 
13
  cookies: Cookies;
14
  userAgent?: string;
15
  ip?: string;
16
+ authProvider: AuthProvider;
17
+ accessToken?: string;
18
  }) {
19
+ const { userData, locals, cookies, userAgent, ip, authProvider, accessToken } = params;
20
 
21
  // Microsoft Entra v1 tokens do not provide preferred_username, instead the username is provided in the upn
22
  // claim. See https://learn.microsoft.com/en-us/entra/identity-platform/access-token-claims-reference
 
73
  }>;
74
  } & Record<string, string>;
75
 
76
+ const isHuggingFace = authProvider === "huggingface";
77
+ // if using huggingface as auth provider, check orgs for early access and admin rights
 
 
 
 
 
 
 
 
 
 
 
78
  const isAdmin =
79
+ isHuggingFace && config.HF_ORG_ADMIN
80
+ ? orgs?.some((org) => org.sub === config.HF_ORG_ADMIN) || false
81
+ : false;
82
  const isEarlyAccess =
83
+ isHuggingFace && config.HF_ORG_EARLY_ACCESS
84
+ ? orgs?.some((org) => org.sub === config.HF_ORG_EARLY_ACCESS) || false
85
+ : false;
86
+
87
+ return await updateUserSession({
88
+ userData: {
89
+ authProvider,
90
+ authId: hfUserId,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  username,
92
  name,
93
  email,
94
  avatarUrl,
 
95
  isAdmin,
96
  isEarlyAccess,
97
+ },
98
+ locals,
99
+ cookies,
100
+ userAgent,
101
+ ip,
102
+ hfAccessToken: isHuggingFace ? accessToken : undefined,
103
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  }