Skip to content

Commit

Permalink
Quickstart javascript remix (FusionAuth#2515)
Browse files Browse the repository at this point in the history
Remix javascript quickstart

---------

Co-authored-by: RichardJECooke <[email protected]>
Co-authored-by: Tatenda <[email protected]>
Co-authored-by: worktheclock <[email protected]>
Co-authored-by: Vinícius Campitelli <[email protected]>
  • Loading branch information
5 people authored Oct 21, 2023
1 parent bc094ca commit 4e3a2e3
Show file tree
Hide file tree
Showing 4 changed files with 317 additions and 13 deletions.
315 changes: 315 additions & 0 deletions astro/src/content/quickstarts/quickstart-javascript-remix-web.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,315 @@
---
title: Remix
description: Quickstart integration of Javascript Remix web application with FusionAuth
navcategory: getting-started
prerequisites: Node.js version 18
section: web
technology: Remix
language: Javascript
icon: /img/icons/javascript.svg
faIcon: fa-node-js
color: green
cta: EmailListCTA
---

import Aside from '/src/components/Aside.astro';
import LoginAfter from '../../diagrams/quickstarts/login-after.astro';
import LoginBefore from '../../diagrams/quickstarts/login-before.astro';
import RemoteCode from '/src/components/RemoteCode.astro';

In this quickstart, you will build an application with Remix and integrate it with FusionAuth. The application is for [ChangeBank](https://www.youtube.com/watch?v=pkH-kD73QUM), a global leader in converting dollars into coins. It will have areas reserved for logged in users and public-facing sections.

Find the Docker Compose file and source code for the complete application at https://github.com/FusionAuth/fusionauth-quickstart-javascript-remix-web.

## Prerequisites

For this Quickstart, you'll need:

* [Node 18](https://nodejs.org/en/download) or later.
* [Docker](https://www.docker.com), which is the quickest way to start FusionAuth. (There are [other ways](/docs/v1/tech/installation-guide/)).

## General Architecture

While this sample application doesn't have login functionality without FusionAuth, a more typical integration will replace an existing login system with FusionAuth.

In that case, the system might look like this before FusionAuth is introduced.

<LoginBefore alt={"Request flow during login before FusionAuth"}/>

The login flow will look like this after FusionAuth is introduced.

<LoginAfter alt={"Request flow during login after FusionAuth"}/>

In general, you would introduce FusionAuth to normalize and consolidate user data, making it consistent and up-to-date and offloading your login security and functionality to FusionAuth.

## Getting Started

Start with getting FusionAuth up and running and creating a new Remix application.

### Clone The Code

First, grab the code from the repository and change into that folder.

```
git clone https://github.com/FusionAuth/fusionauth-quickstart-javascript-remix-web.git
cd fusionauth-quickstart-javascript-remix-web
```

All shell commands in this guide can be entered in a terminal in this folder. On Windows, you need to replace forward slashes with backslashes in paths.

All the files you'll create in this guide already exist in the `complete-application` subfolder, if you prefer to copy them.

### Run FusionAuth Via Docker

You'll find a Docker Compose file `docker-compose.yml` and an environment variables configuration file `.env` in the root folder of the repository.

Assuming you have Docker installed on your machine, you can start FusionAuth on your machine with the following.

```
docker compose up -d
```

This will start three containers, one each for FusionAuth, Postgres, and Elasticsearch.

Here you are using a bootstrapping feature of FusionAuth, called [Kickstart](/docs/v1/tech/installation-guide/kickstart). When FusionAuth starts for the first time, it will look at the `kickstart/kickstart.json` file and configure FusionAuth to your specified state.

<Aside type="note">
If you ever want to reset the FusionAuth system, delete the volumes created by `docker compose` by executing `docker compose down -v`, then rerun `docker compose up -d`.
</Aside>

FusionAuth will be configured with these settings:

* Your client Id is `e9fdb985-9173-4e01-9d73-ac2d60d1dc8e`.
* Your client secret is `super-secret-secret-that-should-be-regenerated-for-production`.
* Your example username is `[email protected]` and the password is `password`.
* Your admin username is `[email protected]` and the password is `password`.
* The base URL of FusionAuth is `http://localhost:9011/`.

You can log in to the [FusionAuth admin UI](http://localhost:9011/admin) and look around if you want to, but with Docker and Kickstart, everything will already be configured correctly.

<Aside type="caution">
The `.env` and `kickstart.json` files contain passwords. In a real application, always add these files to your `.gitignore` file and never commit secrets to version control.
</Aside>

### Create A Basic Remix Application

Next, you'll set up a basic Remix app. While this guide builds a new Remix project, you can use the same method to integrate your existing project with FusionAuth.

Create the default Remix starter project.

```shell
npx create-remix@latest mysite
```

If `create-remix` isn't on your machine, npx will prompt you to install it. Confirm the installation with a `y` input.

Choose the following options:
- Just the basics
- Remix App Server
- TypeScript
- Yes (to running `npm install`)

Add additional npm packages the site will use using the commands below.

```shell
cd mysite
npm install dotenv remix-auth remix-auth-oauth2 jwt-decode
```

The two Remix authentication packages allow you to use FusionAuth without needing to know how OAuth works. The JWT package will provide your app with the user details returned from FusionAuth. The `dotenv` package allows the app to read configuration details from a `.env` environment file.

Now create the `.env` file in your `mysite` folder and add the following to it (note that this is a different environment file to the one in the root folder used by Docker for FusionAuth).

<RemoteCode url="https://raw.githubusercontent.com/FusionAuth/fusionauth-quickstart-javascript-remix-web/main/complete-application/.env"
lang="shell" />

### Preliminary Setup

Before you configure authentication, you need to change the default files created by Remix.

Replace the contents of the file `mysite/app/root.tsx` with the following code.

<RemoteCode url="https://raw.githubusercontent.com/FusionAuth/fusionauth-quickstart-javascript-remix-web/main/complete-application/app/root.tsx"
lang="typescript" />

This is what these changes do:
- Add some styling with `import css from "~/css/changebank.css";` at the top.
- Specify the `<head>` content for the page (title and CSS link) in the two functions at the bottom.

At the top of the file `mysite/app/entry.server.tsx`, add the following line.

```tsx
import 'dotenv/config';
```

This allows any other file run on the server to use settings from `.env`.

## Authentication

Authentication in Remix requires two parts:

- An Authenticator service to handle communication with FusionAuth.
- Routes (login, logout, callback) that talk to the Authenticator.

### Services

The `app` folder in the `mysite` project holds all your code. The `routes` folder is a reserved word in Remix, and contains your application's pages. Let's make a `services` folder to keep your authentication code separate.

```shell
mkdir app/services
```

In this `services` folder, add a file called `session.server.ts` and copy in the following code (this guide follows the example from the [Remix Auth README](https://github.com/sergiodxa/remix-auth)).

<RemoteCode url="https://raw.githubusercontent.com/FusionAuth/fusionauth-quickstart-javascript-remix-web/main/complete-application/app/services/session.server.ts"
lang="typescript" />

This code provides a way for Remix to store cookies that maintain the user's session. (If you'd prefer to use file-based sessions instead of cookie-based, you can read about it in an earlier [blog post about using Remix](/blog/remix-demo).)

Next, create an authentication file in the `services` folder, `auth.server.ts`, that uses the session provider you just created.

<RemoteCode url="https://raw.githubusercontent.com/FusionAuth/fusionauth-quickstart-javascript-remix-web/main/complete-application/app/services/auth.server.ts"
lang="typescript" />

In the line `new Authenticator<User>`, you can specify which user details you want to keep in the session. This example uses a single string containing the user's email, but you could define an object with more details or just a user Id.

Note that the `User` must have a value. If the Authenticator returns nothing to store in the session, then a user will not stay logged in.

In `authOptions`, you specify the FusionAuth URLs stored in the `.env` file, which match those created by Kickstart when you ran the Docker command to start FusionAuth.

Finally, `authStrategy` returns the user's email, decoded from the JWT returned by FusionAuth.

### Routes

First replace the homepage of your app in `mysite/app/routes/_index.tsx` with the following code.

<RemoteCode url="https://raw.githubusercontent.com/FusionAuth/fusionauth-quickstart-javascript-remix-web/main/complete-application/app/routes/_index.tsx"
lang="typescript" />

Remix extends React with a few more custom elements that automatically hide the difference between client and server from the programmer. For example, the line `<Link to="/login" className="button-lg">Login</Link>` allows Remix to load the linked page in the background without a separate page refresh.

If you have not worked with React before, there are a few differences from normal HTML to note:

- The `class` attribute is replaced by `className`, as `class` is a reserved word in JavaScript.
- To add JavaScript values into your HTML, you wrap the value in `{ }`.
- The `style` values are not strings but object literals in braces, wrapped in more braces.

The `_index.tsx` file imports the Authenticator service you created in the previous section, checks if the user has a session, and redirects the user to their profile if they do. This `isAuthenticated` function does not call FusionAuth but checks the user's cookie instead.

<Aside type="caution">
Note that this code runs in a Remix `loader` function on the server, not the client. Remember that Remix provides two functions that run only on the server: `loader` for GET requests and `action` for POST, PATCH, and UPDATE requests. These functions will always run before any HTML is returned to the client. By contrast, the `default` function (that returns HTML) is exported from a route and runs only in the browser (unless the user browses directly to the route without already being on the site). Since Remix mixes server-side and client-side code in the same file, it is essential not to put any authentication handling or secrets in the client-side code.
</Aside>

The link you created looks for a login route, so let's code it. Create the file `mysite/app/routes/login.tsx` and add the following code to it.

<RemoteCode url="https://raw.githubusercontent.com/FusionAuth/fusionauth-quickstart-javascript-remix-web/main/complete-application/app/routes/login.tsx"
lang="typescript" />

This route is purely server-side, as it exports no default function. Instead, the GET loader function redirects the user to FusionAuth. (The user will automatically be logged in without FusionAuth if they already have a session cookie in their browser).

Now create the file `mysite/app/routes/logout.tsx` and add the following code to it.

<RemoteCode url="https://raw.githubusercontent.com/FusionAuth/fusionauth-quickstart-javascript-remix-web/main/complete-application/app/routes/logout.tsx"
lang="typescript" />

When a user browses to this route, they will be logged out by the Authenticator clearing their session, and redirected to the home page.

The final route to create is `mysite/app/routes/auth.callback.tsx`.

<RemoteCode url="https://raw.githubusercontent.com/FusionAuth/fusionauth-quickstart-javascript-remix-web/main/complete-application/app/routes/auth.callback.tsx"
lang="typescript" />

This is where FusionAuth is configured to direct the user after authentication. It may look like this code is doing yet another authentication with `authenticator.authenticate`, but the Authenticator will already have created a session for the user, so no call to FusionAuth will be made here. It is just a way to direct the user to the appropriate page after login.

<Aside type="tip">
If you are wondering why `auth.callback.tsx` has two dots, it's because Remix routing treats the first dot as a slash in the URL, `/auth/callback`.
</Aside>

## Customization

Now that authentication is done, the last task is to create two example pages that a user can access only when logged in.

Copy over some CSS and an image from the example app.

```shell
mkdir app/css
cp ../complete-application/app/css/changebank.css ./app/css/changebank.css
cp ../complete-application/public/money.jpg ./public/money.jpg
```

Add the account page `mysite/app/routes/account.tsx` with the following code.

<RemoteCode url="https://raw.githubusercontent.com/FusionAuth/fusionauth-quickstart-javascript-remix-web/main/complete-application/app/routes/account.tsx"
lang="typescript" />

The `loader` function at the top of the page prohibits the user from viewing this page if they are not authenticated. Any page you create that needs the user to be authenticated requires this function.

The line `const email: string = useLoaderData<typeof loader>();` provides whatever data the loader function returns for use in the HTML. It's used in the line `<p className="header-email">{email}</p>`.

The final page you need is `mysite/app/routes/change.tsx`, with the following code.

<RemoteCode url="https://raw.githubusercontent.com/FusionAuth/fusionauth-quickstart-javascript-remix-web/main/complete-application/app/routes/change.tsx"
lang="typescript" />

This page has identical Remix and authentication functionality to the Account page and some pure React code that updates the DOM whenever the state changes.

## Run The Application

Start the Remix server from the root `mysite` directory.

```shell
npm run dev
```

You can now browse to [the app](http://localhost:3000).

Log in using `[email protected]` and `password`. The change page will allow you to enter a number. Log out and verify that you can't browse to the [account page](http://localhost:3000/account).

## Next Steps

This quickstart is a great way to get a proof of concept up and running quickly, but to run your application in production, there are some things you're going to want to do.

### FusionAuth Customization

FusionAuth gives you the ability to customize just about everything to do with the user's experience and the integration of your application. This includes:
* [Hosted pages](/docs/v1/tech/themes/) such as login, registration, email verification, and many more.
* [Email templates](/docs/v1/tech/email-templates/email-templates).
* [User data and custom claims in access token JWTs](/articles/tokens/jwt-components-explained).

### Security

* You may want to customize the [token expiration times and policies](/docs/v1/tech/oauth/#configure-application-oauth-settings) in FusionAuth.
* Choose [password rules](/docs/v1/tech/core-concepts/tenants#password) and a [hashing algorithm](/docs/v1/tech/reference/password-hashes) that meet your security needs.

### Tenant And Application Management

* Model your application topology using [Applications](/docs/v1/tech/core-concepts/applications), [Roles](/docs/v1/tech/core-concepts/roles), [Groups](/docs/v1/tech/core-concepts/groups), [Entities](/docs/v1/tech/core-concepts/groups), and more.
* Set up [MFA](/docs/v1/tech/guides/multi-factor-authentication), [Social login](/docs/v1/tech/identity-providers/), or [SAML](/docs/v1/tech/identity-providers/samlv2/) integrations.
* Integrate with external systems using [Webhooks](/docs/v1/tech/events-webhooks/), [SCIM](/docs/v1/tech/core-concepts/scim), and [Lambdas](/docs/v1/tech/lambdas/).

### Remix Authentication

- [Remix Docs](https://remix.run/docs)
- [Passport.js authentication concepts](https://www.passportjs.org/concepts/authentication/downloads/html/)
- [Remix Auth](https://github.com/sergiodxa/remix-auth)
- [Remix-Auth-Oauth2 Docs](https://github.com/sergiodxa/remix-auth-oauth2)


## Troubleshooting

* I get "This site can’t be reached localhost refused to connect" when I click the login button.

Ensure FusionAuth is running in the Docker container. You should be able to log in as the admin user `[email protected]` with a password of `password` at [http://localhost:9011/admin](http://localhost:9011/admin).

* It still doesn't work.

You can always pull down a complete running application and compare what's different.

```
git clone https://github.com/FusionAuth/fusionauth-quickstart-javascript-remix-web.git
cd fusionauth-quickstart-javascript-remix-web
docker compose up -d
cd complete-application
npm install
npm run dev
```
4 changes: 2 additions & 2 deletions astro/src/heroes/DocLanding.astro
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const appTypes = [
},
{
"title": "Single-page App",
"description": "Javascript web app that runs in the browser",
"footer": "e.g. Angular, React, Remix",
"description": "JavaScript web app that runs in the browser",
"footer": "e.g. Angular, React, Vue",
"icon": "/img/icons/dl-single-page-app.svg",
"href": "/docs/quickstarts/#spa"
},
Expand Down
7 changes: 0 additions & 7 deletions astro/src/pages/docs/quickstarts/quickstart-sections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,6 @@ const qsSections: QuickStartSection[] = [
anchorTag: 'spa',
desc: 'JavaScript app that runs in the browser',
articles: [
{
href: '/blog/2022/07/15/remix-demo',
title: 'Remix',
icon: '/img/icons/javascript.svg',
faIcon: 'fa-j',
navColor: 'yellow'
},
],
},
{
Expand Down
4 changes: 0 additions & 4 deletions site/_data/exampleapps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -231,10 +231,6 @@
name: Webhook Lambda Function
description: Stores FusionAuth webhook events to S3 using the CDK to manage infra
language: java
- url: https://github.com/FusionAuth/fusionauth-example-remix
name: Remix auth
description: Example using FusionAuth to provide auth for a Remix application
language: javascript
- url: https://github.com/FusionAuth/fusionauth-example-gaming-device-grant
name: Gaming and device grant
description: Example using the Device Authorization grant to provide authentication to a game.
Expand Down

0 comments on commit 4e3a2e3

Please sign in to comment.