From 8c18d7c1061a6d9528980bd5630d9a5e24e3c6e6 Mon Sep 17 00:00:00 2001 From: Nikita Pekin Date: Mon, 20 Mar 2023 07:57:27 -0400 Subject: [PATCH] Cognito auth 5/7 - set username (#5866) 5th PR for IDE/Cloud authorization with cognito. This PR introduces user username templates + flows + backend wrappers for setting username. Forgot Password flows are to be added in next PRs to keep the changes reviewable. --- .../authentication/components/setUsername.tsx | 71 +++++++++++++++++++ .../src/authentication/providers/auth.tsx | 39 +++++++++- .../src/authentication/src/components/app.tsx | 7 ++ .../authentication/src/dashboard/service.tsx | 15 ++++ 4 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 app/ide-desktop/lib/dashboard/src/authentication/src/authentication/components/setUsername.tsx diff --git a/app/ide-desktop/lib/dashboard/src/authentication/src/authentication/components/setUsername.tsx b/app/ide-desktop/lib/dashboard/src/authentication/src/authentication/components/setUsername.tsx new file mode 100644 index 0000000000..c64b1b0ff3 --- /dev/null +++ b/app/ide-desktop/lib/dashboard/src/authentication/src/authentication/components/setUsername.tsx @@ -0,0 +1,71 @@ +/** @file Container responsible for rendering and interactions in setting username flow, after + * registration. */ + +import * as auth from "../providers/auth"; +import * as hooks from "../../hooks"; +import * as icons from "../../components/svg"; +import * as utils from "../../utils"; + +// =================== +// === SetUsername === +// =================== + +function SetUsername() { + const { setUsername } = auth.useAuth(); + const { accessToken, email } = auth.usePartialUserSession(); + + const [username, bindUsername] = hooks.useInput(""); + + return ( +
+
+
+ Set your username +
+
+
+ setUsername(accessToken, username, email) + )} + > +
+
+
+ +
+ + +
+
+
+ +
+
+
+
+
+ ); +} + +export default SetUsername; diff --git a/app/ide-desktop/lib/dashboard/src/authentication/src/authentication/providers/auth.tsx b/app/ide-desktop/lib/dashboard/src/authentication/src/authentication/providers/auth.tsx index e46cf43377..f7e3d7aaeb 100644 --- a/app/ide-desktop/lib/dashboard/src/authentication/src/authentication/providers/auth.tsx +++ b/app/ide-desktop/lib/dashboard/src/authentication/src/authentication/providers/auth.tsx @@ -21,6 +21,7 @@ import * as sessionProvider from "./session"; const MESSAGES = { signUpSuccess: "We have sent you an email with further instructions!", confirmSignUpSuccess: "Your account has been confirmed! Please log in.", + setUsernameSuccess: "Your username has been set!", signInWithPasswordSuccess: "Successfully logged in!", pleaseWait: "Please wait...", } as const; @@ -76,6 +77,11 @@ export interface PartialUserSession { interface AuthContextType { signUp: (email: string, password: string) => Promise; confirmSignUp: (email: string, code: string) => Promise; + setUsername: ( + accessToken: string, + username: string, + email: string + ) => Promise; signInWithGoogle: () => Promise; signInWithGitHub: () => Promise; signInWithPassword: (email: string, password: string) => Promise; @@ -232,9 +238,30 @@ export function AuthProvider(props: AuthProviderProps) { } }); + const setUsername = async ( + accessToken: string, + username: string, + email: string + ) => { + const body: backendService.SetUsernameRequestBody = { + userName: username, + userEmail: email, + }; + + /** TODO [NP]: https://github.com/enso-org/cloud-v2/issues/343 + * The API client is reinitialised on every request. That is an inefficient way of usage. + * Fix it by using React context and implementing it as a singleton. */ + const backend = backendService.createBackend(accessToken, logger); + + await backend.setUsername(body); + navigate(app.DASHBOARD_PATH); + toast.success(MESSAGES.setUsernameSuccess); + }; + const value = { signUp: withLoadingToast(signUp), confirmSignUp: withLoadingToast(confirmSignUp), + setUsername, signInWithGoogle: cognito.signInWithGoogle.bind(cognito), signInWithGitHub: cognito.signInWithGitHub.bind(cognito), signInWithPassword: withLoadingToast(signInWithPassword), @@ -298,13 +325,23 @@ export function ProtectedLayout() { export function GuestLayout() { const { session } = useAuth(); - if (session?.variant === "full") { + if (session?.variant === "partial") { + return ; + } else if (session?.variant === "full") { return ; } else { return ; } } +// ============================= +// === usePartialUserSession === +// ============================= + +export function usePartialUserSession() { + return router.useOutletContext(); +} + // ========================== // === useFullUserSession === // ========================== diff --git a/app/ide-desktop/lib/dashboard/src/authentication/src/components/app.tsx b/app/ide-desktop/lib/dashboard/src/authentication/src/components/app.tsx index 924e0a5804..55daafebee 100644 --- a/app/ide-desktop/lib/dashboard/src/authentication/src/components/app.tsx +++ b/app/ide-desktop/lib/dashboard/src/authentication/src/components/app.tsx @@ -47,6 +47,7 @@ import ConfirmRegistration from "../authentication/components/confirmRegistratio import Dashboard from "../dashboard/components/dashboard"; import Login from "../authentication/components/login"; import Registration from "../authentication/components/registration"; +import SetUsername from "../authentication/components/setUsername"; // ================= // === Constants === @@ -60,6 +61,8 @@ export const LOGIN_PATH = "/login"; export const REGISTRATION_PATH = "/registration"; /** Path to the confirm registration page. */ export const CONFIRM_REGISTRATION_PATH = "/confirmation"; +/** Path to the set username page. */ +export const SET_USERNAME_PATH = "/set-username"; // =========== // === App === @@ -144,6 +147,10 @@ function AppRouter(props: AppProps) { {/* Protected pages are visible to authenticated users. */} }> } /> + } + /> {/* Other pages are visible to unauthenticated and authenticated users. */} { + return this.post(SET_USER_NAME_PATH, body).then((response) => + response.json() + ); + } + /** Returns organization info for the current user, from the Cloud backend API. * * @returns `null` if status code 401 or 404 was received. */