Skip to content

Commit

Permalink
Update profile image on login (#3491)
Browse files Browse the repository at this point in the history
Fixes #2196 (not only for discord, but all providers)

Instead of doing the big mess of having to refresh images in the
background, we update the images when the user signs in, from the
profile we get from the provider.

This has the side effect that if you have 2 different images on 2
different login providers, your image will always change depending on
the last provider you logged in with, but I think this is ok.

Also, consolidate all of the updates that are applied to the user to one
database call.
  • Loading branch information
AbdBarho committed Jun 18, 2023
1 parent 9f5b241 commit 8ea3f14
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 100 deletions.
2 changes: 1 addition & 1 deletion website/src/components/Header/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export function UserMenu() {
<Menu>
<MenuButton border="solid" borderRadius="full" borderWidth="thin" borderColor={borderColor}>
<Box display="flex" alignItems="center" gap="3" p="1">
<Avatar size="sm" src={session.user.image!} />
<Avatar size="sm" src={session.user.image} />
<Text data-cy="username" className="hidden lg:flex ltr:pr-2 rtl:pl-2" style={{ overflow: "hidden" }}>
{session.user.name || "New User"}
</Text>
Expand Down
51 changes: 0 additions & 51 deletions website/src/lib/discord_avatar_refresh.ts

This file was deleted.

75 changes: 27 additions & 48 deletions website/src/pages/api/auth/[...nextauth].ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import DiscordProvider from "next-auth/providers/discord";
import EmailProvider from "next-auth/providers/email";
import GoogleProvider from "next-auth/providers/google";
import { checkCaptcha } from "src/lib/captcha";
import { discordAvatarRefresh } from "src/lib/discord_avatar_refresh";
import { createApiClientFromUser } from "src/lib/oasst_client_factory";
import prisma from "src/lib/prismadb";
import { convertToBackendUserCore } from "src/lib/users";
Expand Down Expand Up @@ -90,15 +89,15 @@ if (boolean(process.env.DEBUG_LOGIN) || process.env.NODE_ENV === "development")
// Create a map of provider types to a set of admin user identifiers based on
// the environment variables. We assume the list is separated by ',' and each
// entry is separated by ':'.
const adminUserMap = process.env.ADMIN_USERS.split(",").reduce((result, entry) => {
const adminUserMap: Map<string, Set<string>> = process.env.ADMIN_USERS.split(",").reduce((result, entry) => {
const [authType, id] = entry.split(":");
const s = result.get(authType) || new Set();
s.add(id);
result.set(authType, s);
return result;
}, new Map());

const moderatorUserMap = process.env.MODERATOR_USERS.split(",").reduce((result, entry) => {
const moderatorUserMap: Map<string, Set<string>> = process.env.MODERATOR_USERS.split(",").reduce((result, entry) => {
const [authType, id] = entry.split(":");
const s = result.get(authType) || new Set();
s.add(id);
Expand Down Expand Up @@ -127,60 +126,44 @@ const authOptions: AuthOptions = {
session.user.role = token.role;
session.user.isNew = token.isNew;
session.user.name = token.name;
session.user.image = token.picture;
session.user.tosAcceptanceDate = token.tosAcceptanceDate;
session.inference = { isAuthenticated: !!token.inferenceTokens };
return session;
},
},
events: {
/**
* Update the user's role after they have successfully signed in
* Update the user after they have successfully signed in
*/
async signIn({ user, account, isNewUser }) {
async signIn({ user, account, isNewUser, profile }) {
// any values that might be updated in the user profile
const toBeUpdated: Partial<{ name: string; role: string; image: string }> = {};

if (isNewUser && account.provider === "email" && !user.name) {
// only generate a username if the user is new and they signed up with email and they don't have a name
// although the name already assigned in the jwt callback, this is to ensure nothing breaks, and we should never reach here.
await prisma.user.update({
data: {
name: generateUsername(),
},
where: {
id: user.id,
},
});
toBeUpdated.name = generateUsername();
}

// Get the admin list for the user's auth type.
const adminForAccountType = adminUserMap.get(account.provider);
const moderatorForAccountType = moderatorUserMap.get(account.provider);

// Return early if there's no admin list.
if (!adminForAccountType && !moderatorForAccountType) {
return;
// update image
if (profile && profile.image) {
toBeUpdated.image = profile.image;
}

// update roles
// TODO(#236): Reduce the number of times we update the role field.

// Update the database if the user is an admin.
if (adminForAccountType.has(account.providerAccountId)) {
await prisma.user.update({
data: {
role: "admin",
},
where: {
id: user.id,
},
});
if (moderatorUserMap.get(account.provider)?.has(account.providerAccountId)) {
toBeUpdated.role = "moderator";
}
if (adminUserMap.get(account.provider)?.has(account.providerAccountId)) {
toBeUpdated.role = "admin";
}

if (moderatorForAccountType.has(account.providerAccountId)) {
if (Object.keys(toBeUpdated).length > 0) {
await prisma.user.update({
data: {
role: "moderator",
},
where: {
id: user.id,
},
where: { id: user.id },
data: toBeUpdated,
});
}
},
Expand All @@ -199,7 +182,7 @@ export default function auth(req: NextApiRequest, res: NextApiResponse) {
async jwt({ token }) {
const frontendUser = await prisma.user.findUnique({
where: { id: token.sub },
select: { name: true, role: true, isNew: true, accounts: true, id: true },
select: { name: true, role: true, isNew: true, accounts: true, image: true, id: true },
});

if (!frontendUser) {
Expand All @@ -221,23 +204,19 @@ export default function auth(req: NextApiRequest, res: NextApiResponse) {
});
}

const backendUser = convertToBackendUserCore(frontendUser);
if (backendUser.auth_method === "discord") {
const discordAccount = frontendUser.accounts.find((a) => a.provider === "discord");
discordAvatarRefresh.updateImageIfNecessary(discordAccount);
}

token.name = frontendUser.name;
token.role = frontendUser.role;
token.isNew = frontendUser.isNew;
token.picture = frontendUser.image;

// these are immutable once assigned
if (!token.tosAcceptanceDate || !token.backendUserId) {
const backendUser = convertToBackendUserCore(frontendUser);
const oasstApiClient = createApiClientFromUser(backendUser);

const frontendUser = await oasstApiClient.upsert_frontend_user(backendUser);
token.backendUserId = frontendUser.user_id;
token.tosAcceptanceDate = frontendUser.tos_acceptance_date;
const user = await oasstApiClient.upsert_frontend_user(backendUser);
token.backendUserId = user.user_id;
token.tosAcceptanceDate = user.tos_acceptance_date;
}
return token;
},
Expand Down

0 comments on commit 8ea3f14

Please sign in to comment.