# Build A Fullstack App with Remix, Prisma & MongoDB: Authentication URL: https://www.prisma.io/blog/fullstack-remix-prisma-mongodb-2-ZTmOy58p4re8 Published: 2022-04-26 Authors: Sabin Adams Tags: education Series: Build A Fullstack App with Remix, Prisma & MongoDB Description: Learn how to build and deploy a fullstack application using Remix, Prisma, and MongoDB. In this article, we will be setting up authentication for our Remix application using session-based authentication. Welcome to the second article of this series where you are learning how to build a full-stack application from the ground up using MongoDB, Prisma, and Remix! In this part, you will set up session-based authentication for your Remix application. ## Table Of Contents [#table-of-contents] * [Introduction](#introduction) * [Development environment](#development-environment) * [Set up a login route](#set-up-a-login-route) * [Create a re-usable layout component](#create-a-re-usable-layout-component) * [Create the sign in form](#create-the-sign-in-form) * [Build the form](#build-the-form) * [Create a form field component](#create-a-form-field-component) * [Add a sign up form](#add-a-sign-up-form) * [Store the form action in state](#store-the-form-action-in-state) * [Add toggleable fields](#add-toggleable-fields) * [The authentication flow](#the-authentication-flow) * [Build the register function](#build-the-register-function) * [Create an instance of `PrismaClient`](#create-an-instance-of-prismaclient) * [Update your data model](#update-your-data-model) * [Add a user service](#add-a-user-service) * [Build the login function](#build-the-login-function) * [Add session management](#add-session-management) * [Handle the login and register form submissions](#handle-the-login-and-register-form-submissions) * [Authorize users on private routes](#authorize-users-on-private-routes) * [Add form validation](#add-form-validation) * [Summary & What's next](#summary--whats-next) [Watch the video on YouTube](https://www.youtube.com/watch?v=vR33ZRJekHk) ## Introduction [#introduction] In the [last part](https://www.prisma.io/blog/fullstack-remix-prisma-mongodb-1-7d0bftxbmb6r) of this series you set up your Remix project and got a MongoDB database up and running. You also configured TailwindCSS and Prisma and began to model out a `User` collection in your `schema.prisma` file. In this part you will implement authentication in your application, allowing a user to create an account and sign in via sign in and sign up forms. > **Note**: The starting point for this project is available in the [part-1](https://github.com/sabinadams/kudos-remix-mongodb-prisma/tree/part-1) branch of the GitHub repository. If you'd like to see the final result of this part, head over to the [part-2](https://github.com/sabinadams/kudos-remix-mongodb-prisma/tree/part-2) branch. ### Development environment [#development-environment] In order to follow along with the examples provided, you will be expected to ... * ... have [Node.js](https://nodejs.org) installed. * ... have [Git](https://git-scm.com/downloads) installed. * ... have the [TailwindCSS VSCode Extension](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) installed. *(optional)* * ... have the [Prisma VSCode Extension](https://marketplace.visualstudio.com/items?itemName=Prisma.prisma) installed. *(optional)* > **Note**: The optional extensions add some really nice intellisense and syntax highlighting for Tailwind and Prisma. ## Set up a login route [#set-up-a-login-route] The very first thing you need to do to kick things off is set up a `/login` route where your sign in and sign up forms will live. To create a route within the Remix framework, add a file to the `app/routes` folder. The name of that file will be used as the name of the route. For more info on how routing works in Remix, check out their [docs](https://remix.run/docs/en/v1/tutorials/jokes#routes). Create a new file in `app/routes` named `login.tsx` with the following contents: ```tsx // app/routes/login.tsx export default function Login() { return (

Login Route

) } ``` The default export of a route file is the component Remix renders into the browser. Start the development server using `npm run dev` and navigate to [`http://localhost:3000/login`](http://localhost:3000/login), and you should see the route rendered. ![](https://www.prisma.io/blog/fullstack-remix-prisma-mongodb-2-ZTmOy58p4re8/imgs/initial-login.png) This works, but doesn't look very nice yet... Next you will spruce it up a bit by adding an actual sign in form. ## Create a re-usable layout component [#create-a-re-usable-layout-component] First, create a component you will wrap your routes in to provide some shared formatting and styling. You will use the [composition](https://reactjs.org/docs/composition-vs-inheritance.html) pattern to create this `Layout` component. ### Click to learn more about the composition pattern! ### Composition [#composition] **Composition** is a pattern where you provide a component a set of child elements via its `props`. The `children` prop represents the elements defined between the opening and closing tag of the parent component. For example, consider this usage of a component named `Parent`: ```tsx

The child

``` In this case, the `

` tag is a child of the `Parent` component and will be rendered into the `Parent` component wherever you decide to render the `children` prop value. To see this in action, create a new folder inside the `app` folder named `components`. Inside of that folder create a new file named `layout.tsx`. In that file, export the following [function component](https://reactjs.org/docs/components-and-props.html): ```tsx // app/components/layout.tsx export function Layout({ children }: { children: React.ReactNode }) { return

{children}
} ``` This component uses Tailwind classes to specify you want anything wrapped in the component to take up the full width and height of the screen, use the mono font, and show a moderately dark blue as the background. Notice the `children` prop is rendered inside the `
`. To see how this will get rendered when put to use, check out the snippets below: ```tsx

Child Element

``` ```tsx

Child Element

``` ## Create the sign in form [#create-the-sign-in-form] Now you can import that component into the `app/routes/login.tsx` file and wrap your `

` tag inside of the new `Layout` component instead of the `
` where it currently lives: ```tsx // app/routes/login.tsx import { Layout } from '~/components/layout' export default function Login() { return (

Login Route

) } ``` ### Build the form [#build-the-form] Next add a sign in form that takes in `email` and `password` inputs and displays a submit button. Add a nice welcome message at the top to greet users when they enter your site and center the entire form on the screen using [Tailwind's flex classes](https://tailwindcss.com/docs/flex). ```tsx // app/routes/login.tsx import { Layout } from '~/components/layout' export default function Login() { return (

Welcome to Kudos!

Log In To Give Some Praise!

) } ``` ![](https://www.prisma.io/blog/fullstack-remix-prisma-mongodb-2-ZTmOy58p4re8/imgs/login-form.png) At this point, you don't need to worry about where the `
`'s action is pointing, just that it has a `method` value of `"post"`. Later on you will check out some cool Remix magic that sets up the action for us! ### Create a form field component [#create-a-form-field-component] The input fields and their labels will be re-written quite a bit throughout this application as you add more forms, so break those out into a [controlled component](https://reactjs.org/docs/forms.html#controlled-components) called `FormField` to avoid code duplication. Create a new file in `app/components` named `form-field.tsx` where you will build the `FormField` component. Then add the following code to get it started: ```tsx // app/components/form-field.tsx interface FormFieldProps { htmlFor: string label: string type?: string value: any onChange?: (...args: any) => any } export function FormField({ htmlFor, label, type = 'text', value, onChange = () => {} }: FormFieldProps) { return ( <> ) } ``` This will define and export the exact same label and input combination you had before in the sign in form, except this component will have configurable options: * `htmlFor`: The value to use for the `id` and `name` attributes of the input field, and the `htmlFor` attribute of the label. * `label`: The text to show in the label. * `value`: The current *controlled* value of the input field. * `type`: *optional* Allows you to set the input field's `type` attribute, but defaults to a value of `'text'`. * `onChange`: *optional* Allows you to provide a function to run when the input field's value is changed. Defaults to an empty function call. You may now replace the existing labels and inputs with this component: ```tsx // app/routes/login.tsx import { useState } from 'react' import { Layout } from '~/components/layout' import { FormField } from '~/components/form-field' export default function Login() { const [formData, setFormData] = useState({ email: '', password: '', }) // Updates the form data when an input changes const handleInputChange = (event: React.ChangeEvent, field: string) => { setFormData(form => ({ ...form, [field]: event.target.value })) } return (

Welcome to Kudos!

Log In To Give Some Praise!

handleInputChange(e, 'email')} /> handleInputChange(e, 'password')} />
) } ``` This will import the new `FormField` component whose state will be managed by the parent *(the sign in form in this case)*. Any updates to the values will be tracked using the `handleInputChange` function. You will come back to the `FormField` component a little bit later to add error message handling, but this will do what it needs to for now! ## Add a sign up form [#add-a-sign-up-form] You will also need a way for the user to sign up for an account, which means you'll need another form. This form will take in four values: * `email` * `password` * `firstName` * `lastName` To avoid having to create a new `/signup` route that looks *almost* identical to the `/login` route, repurpose the sign in form so that it can be toggled between two different actions: login and register. ### Store the form action in state [#store-the-form-action-in-state] First you'll need some way for the user to be able to switch between forms and for your code to be able to differentiate between the forms. At the top of the `Login` component, create another variable in state to hold your `action`. ```tsx // app/routes/login.tsx export default function Login() { const [action, setAction] = useState('login') // ... } ``` > **Note**: The default state will be the login screen. Next you will need some way to switch change which state you want to view. Above the "Welcome to Kudos" message, add the following button: ```tsx // app/routes/login.tsx return (
{/* ... */}
) ``` This button's text will change based on the `action` state. The `onClick` method will toggle the state back and forth between `'login`' and `'register`' values. There are a few places with static text on this page that you will want to adjust depending on which form you are viewing. Specifically the "Log In To Give Some Praise!" sub-header and the "Sign In" button within the form itself. Change the form's sub-header to show a different message on each form: ```tsx diff // app/routes/login.tsx -

Log In To Give Some Praise!

+

+ {action === 'login' ? 'Log In To Give Some Praise!' : 'Sign Up To Get Started!'} +

``` Get rid of the *Sign In* button altogether and replace it with the following ` ``` This new button has a `name` and a `value` attribute. The value is set to whatever the state's `action` is. When your form is submitted, this value will be passed as along with the form data as `_action`. > **Note**: This trick only works on a `