Skip to main content

Examples cookbook: Authentication flow with JWT

☑️ Prerequisites

This page is part of the back end customization examples cookbook. Please ensure you've read its introduction.

💭 Context:

Out of the box, the front-end website of FoodAdvisor does not provide any log in functionality. Logging in is done by accessing Strapi's admin panel at localhost:1337/admin.

Let's add a basic login page to the front-end, Next.js-powered website included in the /client folder of FoodAdvisor. The login page will be accessible at localhost:3000/auth/login and contain a typical email/password login form. This will allow programmatically authenticating API requests sent to Strapi.

Example login page
A possible example of a login form on the front-end website of FoodAdvisor

🎯 Goal:

Create a front-end component to:

  1. to display a login form,
  2. send a request to the /auth/local route of the Strapi back-end server to authenticate,
  3. get a JSON Web Token (JWT),
  4. and store the JWT into the localStorage property of your browser for later retrieval and authentication of our requests.
🤓 Related concept

Additional information about JWT authentication can be found in the Users & Permissions plugin documentation.

🧑‍💻 Code example:

☑️ Prerequisites

The code example in this section uses the formik package. Install it using yarn add formik or npm install formik and restart the dev server.

To achieve this, in the /client folder of the FoodAdvisor project, you could create a pages/auth/login.js file that contains the following example code. Highlighted lines show the request sent to the /auth/local route provided by Strapi's Users & Permissions plugin:

/client/pages/auth/login.js

import React from 'react';
import { useFormik } from 'formik';
import { Button, Input } from '@nextui-org/react';
import Layout from '@/components/layout';
import { getStrapiURL } from '@/utils';

const Login = () => {
const { handleSubmit, handleChange } = useFormik({
initialValues: {
identifier: '',
password: '',
},
onSubmit: async (values) => {
/**
* API URLs in Strapi are by default prefixed with /api,
* but because the API prefix can be configured
* with the rest.prefix property in the config/api.js file,
* we use the getStrapiURL() method to build the proper full auth URL.
**/
const res = await fetch(getStrapiURL('/auth/local'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(values),
});
/**
* Gets the JWT from the server response
*/
const { jwt } = await res.json();
/**
* Stores the JWT in the localStorage of the browser.
* A better implementation would be to do this with an authentication context provider
* or something more sophisticated, but it's not the purpose of this tutorial.
*/
localStorage.setItem('token', jwt);
},
});
/**
* The following code renders a basic login form
* accessible from the localhost:3000/auth/login page.
*/
return (
<Layout>
<div className="h-full w-full flex justify-center items-center my-24">
<form onSubmit={handleSubmit} className="flex flex-col gap-y-6 w-4/12 ">
<h1 className="font-bold text-3xl mb-6">Login</h1>
<Input
onChange={handleChange}
type="email"
name="identifier"
label="Email"
placeholder="Enter your email"
/>
<Input
type="password"
name="password"
label="Password"
placeholder="Enter your password"
onChange={handleChange}
/>
<Button type="submit" className="bg-primary rounded-md text-muted">
Login
</Button>
</form>
</div>
</Layout>
);
};

export default Login;

🤓 What's next?

Learn more about how custom services and controllers can help you tweak a Strapi-based application.