diff --git a/backend/src/controllers/v2/usersController.ts b/backend/src/controllers/v2/usersController.ts index b5849f2e7..0c78d7667 100644 --- a/backend/src/controllers/v2/usersController.ts +++ b/backend/src/controllers/v2/usersController.ts @@ -81,25 +81,62 @@ export const updateMyMfaEnabled = async (req: Request, res: Response) => { } /** - * Update the current user's name [firstName, lastName]. + * Update name of the current user to [firstName, lastName]. * @param req * @param res * @returns */ export const updateName = async (req: Request, res: Response) => { - const { firstName, lastName }: { firstName: string; lastName: string; } = req.body; - req.user.firstName = firstName; - req.user.lastName = lastName || ""; + const { + firstName, + lastName + }: { + firstName: string; + lastName: string; + } = req.body; - await req.user.save(); - - const user = req.user; + const user = await User.findByIdAndUpdate( + req.user._id.toString(), + { + firstName, + lastName: lastName ?? "" + }, + { + new: true + } + ); return res.status(200).send({ user, }); } +/** + * Update auth provider of the current user to [authProvider] + * @param req + * @param res + * @returns + */ +export const updateAuthProvider = async (req: Request, res: Response) => { + const { + authProvider + } = req.body; + + const user = await User.findByIdAndUpdate( + req.user._id.toString(), + { + authProvider + }, + { + new: true + } + ); + + return res.status(200).send({ + user + }); +} + /** * Return organizations that the current user is part of. * @param req diff --git a/backend/src/controllers/v3/authController.ts b/backend/src/controllers/v3/authController.ts index 7a70f171c..7cb3cfd97 100644 --- a/backend/src/controllers/v3/authController.ts +++ b/backend/src/controllers/v3/authController.ts @@ -56,7 +56,7 @@ export const login1 = async (req: Request, res: Response) => { if (!user) throw new Error("Failed to find user"); - if (user.authProvider) { + if (user.authProvider && user.authProvider !== AuthProvider.EMAIL) { await validateProviderAuthToken({ email, user, @@ -117,7 +117,7 @@ export const login2 = async (req: Request, res: Response) => { if (!user) throw new Error("Failed to find user"); - if (user.authProvider) { + if (user.authProvider && user.authProvider !== AuthProvider.EMAIL) { await validateProviderAuthToken({ email, user, diff --git a/backend/src/models/user.ts b/backend/src/models/user.ts index 68f2965c6..6559b1b7e 100644 --- a/backend/src/models/user.ts +++ b/backend/src/models/user.ts @@ -1,6 +1,7 @@ import { Document, Schema, Types, model } from "mongoose"; export enum AuthProvider { + EMAIL = "email", GOOGLE = "google", OKTA_SAML = "okta-saml" } diff --git a/backend/src/routes/v2/users.ts b/backend/src/routes/v2/users.ts index 7815513ec..ca27d5b57 100644 --- a/backend/src/routes/v2/users.ts +++ b/backend/src/routes/v2/users.ts @@ -10,6 +10,9 @@ import { AUTH_MODE_API_KEY, AUTH_MODE_JWT, } from "../../variables"; +import { + AuthProvider +} from "../../models"; router.get( "/me", @@ -34,11 +37,25 @@ router.patch( requireAuth({ acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY], }), - body("firstName").exists(), + body("firstName").exists().isString(), + body("lastName").isString(), validateRequest, usersController.updateName ); +router.patch( + "/me/auth-provider", + requireAuth({ + acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY], + }), + body("authProvider").exists().isString().isIn([ + AuthProvider.EMAIL, + AuthProvider.GOOGLE + ]), + validateRequest, + usersController.updateAuthProvider +); + router.get( "/me/organizations", requireAuth({ diff --git a/frontend/public/locales/en/translations.json b/frontend/public/locales/en/translations.json index e4c671ca4..a70a7dd8b 100644 --- a/frontend/public/locales/en/translations.json +++ b/frontend/public/locales/en/translations.json @@ -227,7 +227,7 @@ }, "password": { "password": "Password", - "change": "Change password", + "change": "Change Password", "current": "Current password", "current-wrong": "The current password may be wrong", "new": "New password", diff --git a/frontend/src/hooks/api/users/index.tsx b/frontend/src/hooks/api/users/index.tsx index e1e530443..0c00810e5 100644 --- a/frontend/src/hooks/api/users/index.tsx +++ b/frontend/src/hooks/api/users/index.tsx @@ -13,4 +13,6 @@ export { useLogoutUser, useRegisterUserAction, useRevokeMySessions, - useUpdateOrgUserRole} from "./queries"; + useUpdateOrgUserRole, + useUpdateUserAuthProvider +} from "./queries"; diff --git a/frontend/src/hooks/api/users/queries.tsx b/frontend/src/hooks/api/users/queries.tsx index fe9efd73f..19504217e 100644 --- a/frontend/src/hooks/api/users/queries.tsx +++ b/frontend/src/hooks/api/users/queries.tsx @@ -58,6 +58,27 @@ export const useRenameUser = () => { }); }; +export const useUpdateUserAuthProvider = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ + authProvider + }: { + authProvider: string; + }) => { + const { data: { user } } = await apiRequest.patch("/api/v2/users/me/auth-provider", { + authProvider + }); + + return user; + }, + onSuccess: () => { + queryClient.invalidateQueries(userKeys.getUser); + } + }); +}; + export const useGetUserAction = (action: string) => useQuery({ queryKey: userKeys.userAction, diff --git a/frontend/src/hooks/api/users/types.ts b/frontend/src/hooks/api/users/types.ts index 31a33c756..0c312b2b6 100644 --- a/frontend/src/hooks/api/users/types.ts +++ b/frontend/src/hooks/api/users/types.ts @@ -1,11 +1,18 @@ import { UserWsKeyPair } from "../keys/types"; +export enum AuthProvider { + EMAIL = "email", + GOOGLE = "google", + OKTA_SAML = "okta-saml" +} + export type User = { createdAt: Date; updatedAt: Date; email?: string; firstName?: string; lastName?: string; + authProvider?: AuthProvider; encryptionVersion?: number; protectedKey?: string; protectedKeyIV?: string; diff --git a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx index 86a6c4b5e..abc6adf56 100644 --- a/frontend/src/views/Login/components/InitialStep/InitialStep.tsx +++ b/frontend/src/views/Login/components/InitialStep/InitialStep.tsx @@ -2,8 +2,8 @@ import { FormEvent, useState } from "react"; import { useTranslation } from "react-i18next"; import Link from "next/link"; import { useRouter } from "next/router"; -// import { faGoogle } from "@fortawesome/free-brands-svg-icons"; -// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faGoogle } from "@fortawesome/free-brands-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import axios from "axios" import Error from "@app/components/basic/Error"; @@ -159,7 +159,7 @@ export const InitialStep = ({ or
- {/*
+
-
*/} +
+ + ); +} \ No newline at end of file diff --git a/frontend/src/views/Settings/PersonalSettingsPage/AuthMethodSection/index.tsx b/frontend/src/views/Settings/PersonalSettingsPage/AuthMethodSection/index.tsx new file mode 100644 index 000000000..12540cbb4 --- /dev/null +++ b/frontend/src/views/Settings/PersonalSettingsPage/AuthMethodSection/index.tsx @@ -0,0 +1 @@ +export { AuthMethodSection } from "./AuthMethodSection"; \ No newline at end of file diff --git a/frontend/src/views/Settings/PersonalSettingsPage/PersonalAuthTab/PersonalAuthTab.tsx b/frontend/src/views/Settings/PersonalSettingsPage/PersonalAuthTab/PersonalAuthTab.tsx new file mode 100644 index 000000000..73f04113e --- /dev/null +++ b/frontend/src/views/Settings/PersonalSettingsPage/PersonalAuthTab/PersonalAuthTab.tsx @@ -0,0 +1,13 @@ +import { AuthMethodSection } from "../AuthMethodSection"; +import { ChangePasswordSection } from "../ChangePasswordSection"; +import { MFASection } from "../SecuritySection"; + +export const PersonalAuthTab = () => { + return ( +
+ + + +
+ ); +} \ No newline at end of file diff --git a/frontend/src/views/Settings/PersonalSettingsPage/PersonalAuthTab/index.tsx b/frontend/src/views/Settings/PersonalSettingsPage/PersonalAuthTab/index.tsx new file mode 100644 index 000000000..4ab3d0b6e --- /dev/null +++ b/frontend/src/views/Settings/PersonalSettingsPage/PersonalAuthTab/index.tsx @@ -0,0 +1 @@ +export { PersonalAuthTab } from "./PersonalAuthTab"; \ No newline at end of file diff --git a/frontend/src/views/Settings/PersonalSettingsPage/PersonalSecurityTab/PersonalSecurityTab.tsx b/frontend/src/views/Settings/PersonalSettingsPage/PersonalGeneralTab/PersonalGeneralTab.tsx similarity index 64% rename from frontend/src/views/Settings/PersonalSettingsPage/PersonalSecurityTab/PersonalSecurityTab.tsx rename to frontend/src/views/Settings/PersonalSettingsPage/PersonalGeneralTab/PersonalGeneralTab.tsx index abfec566a..531776751 100644 --- a/frontend/src/views/Settings/PersonalSettingsPage/PersonalSecurityTab/PersonalSecurityTab.tsx +++ b/frontend/src/views/Settings/PersonalSettingsPage/PersonalGeneralTab/PersonalGeneralTab.tsx @@ -1,18 +1,14 @@ import { ChangeLanguageSection } from "../ChangeLanguageSection"; -import { ChangePasswordSection } from "../ChangePasswordSection"; import { EmergencyKitSection } from "../EmergencyKitSection"; -import { SecuritySection } from "../SecuritySection"; import { SessionsSection } from "../SessionsSection"; import { UserNameSection } from "../UserNameSection"; -export const PersonalSecurityTab = () => { +export const PersonalGeneralTab = () => { return (
- -
); diff --git a/frontend/src/views/Settings/PersonalSettingsPage/PersonalGeneralTab/index.tsx b/frontend/src/views/Settings/PersonalSettingsPage/PersonalGeneralTab/index.tsx new file mode 100644 index 000000000..1a83f1b88 --- /dev/null +++ b/frontend/src/views/Settings/PersonalSettingsPage/PersonalGeneralTab/index.tsx @@ -0,0 +1 @@ +export { PersonalGeneralTab } from "./PersonalGeneralTab"; \ No newline at end of file diff --git a/frontend/src/views/Settings/PersonalSettingsPage/PersonalSecurityTab/index.tsx b/frontend/src/views/Settings/PersonalSettingsPage/PersonalSecurityTab/index.tsx deleted file mode 100644 index e9d5025cb..000000000 --- a/frontend/src/views/Settings/PersonalSettingsPage/PersonalSecurityTab/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export { PersonalSecurityTab } from "./PersonalSecurityTab"; \ No newline at end of file diff --git a/frontend/src/views/Settings/PersonalSettingsPage/PersonalTabGroup/PersonalTabGroup.tsx b/frontend/src/views/Settings/PersonalSettingsPage/PersonalTabGroup/PersonalTabGroup.tsx index 6f3edef97..f2e367b36 100644 --- a/frontend/src/views/Settings/PersonalSettingsPage/PersonalTabGroup/PersonalTabGroup.tsx +++ b/frontend/src/views/Settings/PersonalSettingsPage/PersonalTabGroup/PersonalTabGroup.tsx @@ -2,10 +2,12 @@ import { Fragment } from "react" import { Tab } from "@headlessui/react" import { PersonalAPIKeyTab } from "../PersonalAPIKeyTab"; -import { PersonalSecurityTab } from "../PersonalSecurityTab"; +import { PersonalAuthTab } from "../PersonalAuthTab"; +import { PersonalGeneralTab } from "../PersonalGeneralTab"; const tabs = [ - { name: "General", key: "tab-account-security" }, + { name: "General", key: "tab-account-general" }, + { name: "Authentication", key: "tab-account-auth" }, { name: "API Keys", key: "tab-account-api-keys" } ]; @@ -28,7 +30,10 @@ export const PersonalTabGroup = () => { - + + + + diff --git a/frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/SecuritySection.tsx b/frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/MFASection.tsx similarity index 98% rename from frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/SecuritySection.tsx rename to frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/MFASection.tsx index 4f23cbf08..8f9b36db7 100644 --- a/frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/SecuritySection.tsx +++ b/frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/MFASection.tsx @@ -9,7 +9,7 @@ import { useGetUser } from "../../../../hooks/api"; import { User } from "../../../../hooks/api/types"; import updateMyMfaEnabled from "../../../../pages/api/user/updateMyMfaEnabled"; -export const SecuritySection = () => { +export const MFASection = () => { const [isMfaEnabled, setIsMfaEnabled] = useState(false); const { data: user } = useGetUser(); const { createNotification } = useNotificationContext(); diff --git a/frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/index.tsx b/frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/index.tsx index b360c0b4b..981ac6d5c 100644 --- a/frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/index.tsx +++ b/frontend/src/views/Settings/PersonalSettingsPage/SecuritySection/index.tsx @@ -1 +1 @@ -export { SecuritySection } from "./SecuritySection"; \ No newline at end of file +export { MFASection } from "./MFASection"; \ No newline at end of file diff --git a/frontend/src/views/Settings/PersonalSettingsPage/UserNameSection/UserNameSection.tsx b/frontend/src/views/Settings/PersonalSettingsPage/UserNameSection/UserNameSection.tsx index bf03877a8..722112bf9 100644 --- a/frontend/src/views/Settings/PersonalSettingsPage/UserNameSection/UserNameSection.tsx +++ b/frontend/src/views/Settings/PersonalSettingsPage/UserNameSection/UserNameSection.tsx @@ -55,7 +55,7 @@ export const UserNameSection = (): JSX.Element => { className="p-4 bg-mineshaft-900 mb-6 rounded-lg border border-mineshaft-600" >

- User name + Name