Skip to main content

How to Integrate Altogic Authentication? Part-3

· 8 min read
Deniz Çolak

Introduction

In the previous tutorial, we implement the crucial part of the Authentication flow. Now, we will continue with the Profile page to extend the power of our application with Change password, Reset password, Change email features.

Creating Profile Page

To handle the profile route let's open the App.js and paste below code block.

/src/App.js
import { BrowserRouter, Routes, Route } from "react-router-dom";import { Home } from "./components/Home";import { Signup } from "./components/Signup";import { Verification } from "./components/Verification";import { Redirect } from "./components/Redirect";import { Login } from "./components/Login";import { PrivateRoute } from "./components/PrivateRoute";import { AuthProvider } from "./contexts/Auth";import { Profile } from "./components/Profile";export default function App() {  return (    <div>      <BrowserRouter>        <AuthProvider>          <Routes>            <Route              path="/"              element={                <PrivateRoute>                  <Home />                </PrivateRoute>              }            />            <Route              path="/profile"              element={                <PrivateRoute>                  <Profile />                </PrivateRoute>              }            />            <Route path="/signup" element={<Signup />} />            <Route path="/auth-redirect" element={<Redirect />} />            <Route path="/verification" element={<Verification />} />            <Route path="/login" element={<Login />} />          </Routes>        </AuthProvider>      </BrowserRouter>    </div>  );}

Here we are adding localhost:3000/profile private route to handle the change password/reset password and change email functionalities.

So, let’s create Profile.js component inside of the components folder to handle these functionalities.

Create react app with npx

Let’s implement all with the below code block then review them step-by-step.

/src/components/Profile.js
import { useRef, useState } from "react";
import { useAuth } from "../contexts/Auth";
import { altogic } from "../helpers/altogic";

export function Profile() {
const oldPassword = useRef();
const newPassword = useRef();
const newEmail = useRef();
const email = useRef();
const currentPassword = useRef();
// Get current user and signOut function from context
const { setUser, setSession, user } = useAuth();
const [changePasswordResponse, setchangePasswordResponse] = useState(null);
const [resetPasswordResponse, setResetPasswordResponse] = useState(null);
const [changeEmailResponse, setChangeEmailResponse] = useState(null);

// create async function to change password
async function handleChangePassword(e) {
e.preventDefault();
// get password input values
const oldPassword = e.target.oldPassword.value;
const newPassword = e.target.newPassword.value;
// call altogic client library `changePassword` function
const changePasswordResponse = await altogic.auth.changePassword(
newPassword,
oldPassword
);
setchangePasswordResponse(changePasswordResponse);
}
// create async function to reset password
async function handleResetPassword(e) {
e.preventDefault();
// get email input value
const email = e.target.email.value;
// call altogic client library `sendResetPwdEmail` function
const resetPasswordResponse = await altogic.auth.sendResetPwdEmail(email);
setResetPasswordResponse(resetPasswordResponse);
}
// create async function to change email address
async function handleChangeEmail(e) {
e.preventDefault();
// get email and password input values
const newEmail = e.target.newEmail.value;
const currentPassword = e.target.currentPassword.value;

// call altogic client library `changeEmail` function
const changeEmailResponse = await altogic.auth.changeEmail(
currentPassword,
newEmail
);
setUser(altogic.auth.getUserFromDB());
setSession(altogic.auth.getSession());
setChangeEmailResponse(changeEmailResponse);
}

return (
<>
<div style={{ margin: "20px 20px" }}>
{/* Displays the user email */}
<p>Welcome, {user?.email} !</p>

<span>
<p>
Your email verification status:{" "}
{user?.emailVerified ? "Verified" : "Not Verified"}
</p>
</span>
<pre>{user && JSON.stringify(user, null, 3)}</pre>
{/* Change Password section */}
<div>
<h1>Change Password</h1>
<form onSubmit={handleChangePassword}>
<label>Old Password:</label>
<input type="password" name="oldPassword" ref={oldPassword} />
<label>New Password:</label>
<input type="password" name="newPassword" ref={newPassword} />

<input type="submit" value="Change Password" />
</form>
<pre>
{changePasswordResponse &&
JSON.stringify(changePasswordResponse, null, 3)}
</pre>
</div>
{/* Change Email section */}
<div>
<h1>Change Email</h1>
<form onSubmit={handleChangeEmail}>
<label>Password:</label>
<input
type="password"
name="currentPassword"
ref={currentPassword}
/>
<label>New Email:</label>
<input type="email" name="newEmail" ref={newEmail} />

<input type="submit" value="Change Email" />
</form>
<pre>
{changeEmailResponse &&
JSON.stringify(changeEmailResponse, null, 3)}
</pre>
</div>
{/* Reset Password section */}
<div>
<h1>Reset Password</h1>
<form onSubmit={handleResetPassword}>
<label>Email:</label>
<input type="email" name="email" ref={email} />

<input type="submit" value="Send Reset Password Email" />
</form>
<pre>
{resetPasswordResponse &&
JSON.stringify(resetPasswordResponse, null, 3)}
</pre>
</div>
</div>
</>
);
}
Create react app with npx

Here we are implementing handleChangePassword, handleChangeEmail, and handleResetPassword functions to send request to Altogic backend.

  • To use the altogic.auth.changePassword function, the Altogic client library requires oldPassword and newPassword parameters, so to collect this information, we are implementing the useRef hook again to manage these values from the form element.

    Once the user enters the values and clicks the Submit button, Altogic changes the user password.

  • To use the altogic.auth.changeEmail function, the Altogic client library requires currentPassword and newEmail parameters, so to collect this information we are implementing the useRef hook to collect these values from the form element.

    Once the user enters the values and clicks the Submit button, Altogic will send a confirmation email to the user to click. Once the user clicks the link in the email, it navigates users to the Redirect URL with the action=change-email and access_token query string parameter.

http://localhost:3001/auth-redirect?access_token=0e55c6fa...4e8cb11b35&action=change-email
  • To use the altogic.auth.resetPassword function, the Altogic client library requires only email parameter, so to collect that information, we are implementing the useRef hook to collect email from the form element.

    Once the user enters the email and clicks the Submit button, Altogic will send a reset password link to the user to click.

    Create react app with npx

    When the user clicks the link in the email, it navigates users to the Redirect URL with the action=reset-pwd and access_token query string parameters. At that time, we need to use this access_token with another Altogic Client library method, altogic.auth.ResetPwdWithToken, to reset password.

http://localhost:3001/auth-redirect?access_token=0e55c6fa...4e8cb11b35&action=reset-pwd

Also, we need to store access_token in the previously created Authentication context to share state across the pages.

Updating Authentication Context

Copy the following code to the Auth.js file inside the context/ folder to store and share the access_token state.

/src/contexts/Auth.js
import React, { useContext, useState, useEffect } from "react";
import { altogic } from "../helpers/altogic";

const AuthContext = React.createContext();
export function useAuth() {
return useContext(AuthContext);
}
export function AuthProvider({ children }) {
const [user, setUser] = useState();
const [accessToken, setAccessToken] = useState();
const [session, setSession] = useState();
const [loading, setLoading] = useState(true);

useEffect(() => {
// Check active sessions and sets the user and session
const session = altogic.auth.getSession();
const user = altogic.auth.getUser();
setUser(user ?? null);
setSession(session ?? null);
setLoading(false);
}, []);

const value = {
user,
session,
setUser,
setSession,
loading,
setLoading,
accessToken,
setAccessToken,
};

return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
}

Creating Reset Password page

Let's create ResetPassword.js inside the components/ folder and open ResetPassword.js and copy the below code block to call the resetPwdWithToken method with access_token.

/src/components/ResetPassword.js
import { useRef, useState } from "react";
import { useAuth } from "../contexts/Auth";
import { altogic } from "../helpers/altogic";
import { useNavigate } from "react-router-dom";

export function ResetPassword() {
// Get current user and signOut function from context
const { user, accessToken } = useAuth();
const [errors, setError] = useState(null);
const navigate = useNavigate();
const passwordRef = useRef();

async function handleChangePassword() {
let response = await altogic.auth.resetPwdWithToken(
accessToken,
passwordRef.current.value
);
console.log(response);

// Signout and redirect the user to Login page
const { errors } = await altogic.auth.signOutAll();
if (errors) return setError(errors);
}

return (
<>
<div style={{ margin: "20px 20px" }}>
{/* Displays the user ID */}
<p>Welcome, {user?.email} !</p>

<h2> Reset Password</h2>
{/* Input to collect new password */}
<div>
<label>New Password:</label>
<input type="password" name="password" ref={passwordRef} />

<input
type="button"
value="Reset password"
onClick={handleChangePassword}
/>
</div>
<pre>{user && JSON.stringify(user, null, 3)}</pre>
<pre>{errors && JSON.stringify(errors, null, 3)}</pre>
</div>
</>
);
}
Create react app with npx

Let's open App.js to add reset-password route, so copy the following code block.

/src/App.js
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { Home } from "./components/Home";
import { Signup } from "./components/Signup";
import { Verification } from "./components/Verification";
import { Redirect } from "./components/Redirect";
import { Login } from "./components/Login";
import { PrivateRoute } from "./components/PrivateRoute";
import { AuthProvider } from "./contexts/Auth";
import { Profile } from "./components/Profile";
import { ResetPassword } from "./components/ResetPassword";

export default function App() {
return (
<div>
<BrowserRouter>
<AuthProvider>
<Routes>
<Route
path="/"
element={
<PrivateRoute>
<Home />
</PrivateRoute>
}
/>
<Route
path="/profile"
element={
<PrivateRoute>
<Profile />
</PrivateRoute>
}
/>
<Route
path="/reset-password"
element={
<PrivateRoute>
<ResetPassword />
</PrivateRoute>
}
/>
<Route path="/signup" element={<Signup />} />
<Route path="/auth-redirect" element={<Redirect />} />
<Route path="/verification" element={<Verification />} />
<Route path="/login" element={<Login />} />
</Routes>
</AuthProvider>
</BrowserRouter>
</div>
);
}

Updating Redirect Page

We need to use query string parameters such as reset-pwd and change-email to handle the actions on Redirect page.

So, Let's open the Redirect.js and copy the following code.

/src/components/Redirect.js
import React, { useEffect, useState } from "react";
import { altogic } from "../helpers/altogic";
import { useNavigate, useSearchParams } from "react-router-dom";
import { useAuth } from "../contexts/Auth";

export function Redirect() {
const navigate = useNavigate();
const { session, setSession, setUser, setAccessToken } = useAuth();
const [errors, setError] = useState(null);

const [searchParams] = useSearchParams();
let queryParam = searchParams.get("action");

useEffect(() => {
async function fetchData() {
if (queryParam === "change-email") {
alert("Email changed successfully!");
let { user } = await altogic.auth.getUserFromDB();
setUser(user);
navigate("/");
} else if (queryParam === "reset-pwd") {
let accessToken = searchParams.get("access_token");
setAccessToken(accessToken ?? null);
navigate("/reset-password");
} else {
let { session, user, errors } = await altogic.auth.getAuthGrant();
setSession(session);
setUser(user);
// If error occurs, set error state
if (errors) return setError(errors);
navigate("/");
}
}

fetchData();

// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return !session ? (
<div style={{ margin: "20px 20px", display: "flex", alignItems: "center" }}>
You are redirecting...
</div>
) : (
<div style={{ margin: "20px 20px" }}>
Please wait ...
<pre>{errors && JSON.stringify(errors, null, 3)}</pre>
</div>
);
}

Conclucion

We have completed our starter authentication application. We define different routes and functionalities to cover all the needs of the authentication flow. We have implement

  • Login
  • Signup
  • Home
  • Profile
  • Verification
  • Redirect
  • Reset Password

pages for authentication starter app and implement Signout, Change Password, Change Email, Reset Password functionalities.