Integrating Credentials Provider with NextAuthJS and MongoDB with Typescript - Part 2
Oliver Kem
Posted on April 27, 2023
Now, we work on the client side, in the client side, we will be handling how we can protect routes and how we can interact with the logged in user through an active session. With that, let's start.
From part 1 Part 1 we were done with returning the logged in user information (id, name and email) We will now use this in the frontend
Let's handle with the sign up functionality. From our frontend, sign up page let's start
import React, { SyntheticEvent, useReducer } from 'react';
import { signIn } from 'next-auth/react';
function FormComponent() {
const handleFormSubmit = async (e: SyntheticEvent)=>{
e.preventDefault();
const { password, ...others } = state;
if (password !==others['confirm-password']) {
dispatch({name:'response',value:{code:401,message:"Passwords don't match!!"}})
}
type sentResponse = Omit<FormComponents,'confirm-password'|'response'| 'passwordType'>
const sendingResp: sentResponse = {
name: state.name,
email: state.email,
password: state.password,
}
const res = await fetch('/api/auth/sign-up',{
method: 'POST',
headers:{
'Content-Type':'application/json'
},
body: JSON.stringify({...sendingResp})
})
const data = await res.json();
console.log(data.message,res.status);
if (res.status===(200| 201)) {
//sign in the user
const status = await signIn('credentials', {
redirect: false,
email: state.email,
password: state.password
})
console.log(status);
}
dispatch({name:'response',value:{code:res.status,message:data.message}})
}
type FormComponents={
name: string
email: string
password: string
response: {
code: number,
message: string
}
}
const formVals: FormComponents={
name: '',
email: '',
password: '',
'confirm-password': '',
response: {
code: 0,
message: ''
},
}
function signInReducer(prev: FormComponents,next: {name :string,value: string | {code:number,message:string} | boolean}) {
return {
...prev,
[next.name]: next.value
}
}
const [state,dispatch] = useReducer(signInReducergit,formVals)
function handleOnChange(e: React.FormEvent<HTMLInputElement> | React.FormEvent<HTMLSelectElement>){
if (e.target instanceof HTMLSelectElement) {
dispatch({name: e.target.name,value: e.target.value})
return;
}
const {name,value} = e.target as HTMLTextAreaElement
dispatch({name: name ,value: value})
}
return (
<>
<div className={styles.form_container}>
<form className={styles.form} onSubmit={handleFormSubmit}>
<p style={{fontWeight: 'bold', fontSize: '20px'}}>Create an Account Today!!</p>
{
state.response.message?(
<p className={state.response.code===201? styles.success_message:styles.failure_message}>{state.response.message}</p>
):''
}
<div className={styles.form_element}>
<label>Name</label>
<input name='name' value={state.name} required type="text" onChange={handleOnChange} />
</div>
<div className={styles.form_element}>
<label>Email</label>
<input name='email' value={state.email} required type="email" onChange={handleOnChange}/>
</div>
<div className={styles.form_element}>
<label >Password</label>
<input name='password' value={state.password} required type="password" onChange={handleOnChange} />
</div>
<div className={styles.form_element}>
<label>Confirm Password</label>
<input name='confirm-password' value={state['confirm-password']} required type="password" onChange={handleOnChange} />
</div>
<input required type="submit" value="SIGN UP" />
</form>
</div>
</>
);
}
export default FormComponent;
We have just handled the sign up component. We are using useReducer
in react where we store user state (user information) from the state, after that, we send a post request to the backend router /api/auth/sign-up
page, the file will now be executed.
After a positive response from the server i.e the response is of type 200 status code, we now proceed to sign in the user. We have just imported the signIn
function from the next-auth
library.
When we sign in, next-auth will now be responsible with storing the user session, this is amazing because we don't have to use a state management tool to store the user information.
next-auth
handles that for us THIS IS AMAZING
Now let's proceed with the login functionality.
async function handleSignIn(e: SyntheticEvent){
e.preventDefault();
const status = await signIn('credentials',{
redirect: false,
email: state.email,
password: state.password
})
console.log(status)
}
<form onSubmit={handleSignIn}>
<p >Already have an Account? </p>
<div >
<label>Email</label>
<input name='email' value={state.email} required type="email" onChange={handleOnChange}/>
</div>
<div className={styles.form_element}>
<label>Password</label>
<input name='password' value={state.password} required type={state.passwordType?'text':'password'} onChange={handleOnChange} />
<p onClick={()=>dispatch({name:'passwordType', value: !state.passwordType})} style={{cursor:'pointer',display:'inline-block'}}>show</p>
</div>
<input onClick={handleSignIn} type="submit" value="SIGN IN" />
<input style={{color: 'white', background:'blue'}} required type="submit" value="SIGN IN WITH GOOGLE" />
</form>
This session of code is self explanatory, upon sign in, the [...nextauth.ts]
file will be executed, this will now be responsible for signing in our user. Amazing
Now, let's handle how we can protect some route. In our Navigation, we would wish to protect a link to a route i.e user dashboard
import Link from "next/link";
import React from "react";
import Image from "next/image";
import { useSession } from "next-auth/react";
import { signOut } from "next-auth/react";
function NavComponent() {
const {data: session} = useSession();
return(
<header>
<h1>My App</h1>
<nav >
<ul >
<li>
<Link href='/'>Home</Link>
</li>
<li>
<Link href='/partners'>Partners</Link>
</li>
{
session && (
<li>
<Link href='/my-dashboard'>My Dashboard</Link>
</li>
)
}
{
session && (
<li>
<Link href='/doctor-dashboard'>My Dashboard</Link>
</li>
)
}
<li>
<Link href='/contact'>Contact Us</Link>
</li>
</ul>
<div >
{
session && (
<div >
<Image src='/images/cart.webp' alt="image" />
<div>
<h4>{session.user?.name}</h4>
<p>{session.user?.id}</p>
</div>
</div>
)
}
{
!session && (
<button >
<Link href='/auth'>Login/Register</Link>
</button>
)
}
{
session && (
<button onClick={()=>signOut()} >
Logout
</button>
)
}
{
session && (
<button>Book Appointment</button>
)
}
</div>
</nav>
</header>
)
}
export default NavComponent;
With this navigation component, we have protected the user Dashboard. We also want to display a user image and name when there's an active session. next-auth
also provides a sign out library function where we can log out our user easily.
Now for our final part, let's protect a user page. It's always good to protect a page containing user information page
import React from 'react';
import Link from 'next/link';
import { getSession } from 'next-auth/react';
function DoctorDashboard() {
const [toogle, setToogle] = React.useState(false);
const handleClick = () => {
setToogle(!toogle);
}
return (
<>
<div style={{margin: '20px'}}>
<h1> User information dashboard</h1>
</div>
</>
);
}
export async function getServerSideProps(context: GetServerSidePropsContext){
const session= await getSession(context);
if (!session) {
return{
redirect:{
destination:'/auth',
permanent: false
}
}
}
//Do your logic e.g fetching a user information from the // backend to retrieve information
return{
props:{
//data
}
}
}
export default DoctorDashboard;
From this section of code, when a user navigates to this route and he/she isn't logged in (no active session), they are being redirected back to the
/auth
page, amazing. This is just amazing.
And with this we are done with our two sections we are done.
For more information, find everything from the official documentation. Next Auth Docs
Posted on April 27, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
April 27, 2023