five-minute

Docker Install For the 5-Minute Guide

Docker Install For the 5-Minute Guide

You’ve chosen to install FusionAuth via Docker and Docker Compose.

This is the best option if you have Docker installed locally or you plan to run FusionAuth in a Docker compatible environment such as Kubernetes and want to learn more about how FusionAuth runs in Docker.

If you’ve arrived here directly, start with the 5-Minute Setup Guide Overview.

Below is a brief video showing how to set up FusionAuth in less than 5 minutes.

Requirements

You need to have Node.js installed. This document has been tested with version 14 and 18, but should work with other versions.

Testing the Node.js Installation

node -v

Result Of Testing the Node.js Installation

v18.10.0

You need to have Docker installed. This document has been tested with Docker version 20, but should work with other versions.

Testing the Docker Installation
docker -v
Result Of Testing the Docker Installation
Docker version 20.10.17, build 100c701

Overview

Here are steps to take to set up FusionAuth and configure it to provide login and logout functionality for your application.

  1. Install and Start FusionAuth
  2. Complete the Setup Wizard
  3. Create an Application and configure the OAuth settings
  4. Grant Permissions
  5. Configure the Backend to Complete the Login
  6. Store the user object in the session
  7. Test the Application
  8. Logout
  9. Summing Up

1. Install and Start FusionAuth

You are following the Docker 5 minute guide, so you’ll use docker compose.

To use FusionAuth download the docker-compose.yml and the .env files and then start up the configured containers.

Download the FusionAuth docker files

curl -o docker-compose.yml https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/main/docker/fusionauth/docker-compose.yml
curl -o .env https://raw.githubusercontent.com/FusionAuth/fusionauth-containers/main/docker/fusionauth/.env

Edit these files as needed. The stock .env file will contain the following values, you will want to modify the DATABASE_PASSWORD and ensure the POSTGRES_USER and POSTGRES_PASSWORD values are correct. You may also override any of these values using environment variables.

The .env file

DATABASE_USERNAME=fusionauth
DATABASE_PASSWORD=hkaLBM3RVnyYeYeqE3WI1w2e4Avpy0Wd5O3s3
FUSIONAUTH_APP_MEMORY=512M
FUSIONAUTH_APP_RUNTIME_MODE=development
OPENSEARCH_JAVA_OPTS="-Xms512m -Xmx512m"
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres

Starting FusionAuth

docker compose up -d
docker compose logs -f

Once the installer completes, you will see something similar to this output on the console:

Docker Install Complete

fusionauth-1  | 2022-10-29 01:01:16.527 AM INFO  org.primeframework.mvc.netty.PrimeHTTPServer - Starting FusionAuth HTTP server on port [9011]
fusionauth-1  | 2022-10-29 01:01:16.583 AM INFO  org.primeframework.mvc.netty.PrimeHTTPServer - Starting FusionAuth HTTP loopback server on port [9012]

And that’s it! Docker makes standing up a FusionAuth instance a snap.

Next, start the Setup Wizard by navigating to http://localhost:9011.

2. Complete the Setup Wizard

Once FusionAuth starts up successfully, you will be taken to the Setup Wizard. This is where you will create the administrator account for FusionAuth and accept the license.

Record the email and password you choose for the administrator account. You won’t be able to recover this without setting up an email server, which is beyond the scope of this tutorial.

You can also choose to sign up for the FusionAuth newsletter, which is a great way to stay up-to-date on FusionAuth releases and new features. We will never pitch you, hard-sell or sell your information, so feel free to sign up.

Here’s what the Setup Wizard page looks like.

Setup Wizard

Clicking Submit here will complete the Setup Wizard and log you into the FusionAuth administrative user interface.

3. Create an Application and Configure the OAuth settings

Once you arrive in the FusionAuth admin UI, the first thing you need to do is create an Application.

If you see a First Time Setup wizard in the main content window, you can click “Skip it” as you’ll be creating things outside of this wizard.

An Application is something that a user can log into. This is the application you are building or that you are migrating to use FusionAuth. Click the Applications menu option on the left side of the page.

Dashboard to Applications

This will take you to the listing page for Applications. Next, you’ll click the green plus button (the add button) at the top of the page:

Application Listing

On the Add Application form, provide a name for your Application (this is only used for display purposes).

Click on the OAuth tab.

When you click on the OAuth tab, you’ll see this form. Start with a simple setup that allows existing users to log into your application. There is no need to add any roles or registration configuration.

Application Form

Most of the defaults will work, but provide the following items.

  • An authorized redirect URL . This is the route/controller in your application’s backend that will complete the OAuth grant and obtain a token. For this example, add http://localhost:3000/oauth-redirect. You will see example code below for this route. Note that when you type or paste values into this text field you also must click the popup that appears. Otherwise the value will not be saved.
  • Specify a valid Logout URL . This is where the user will be redirected to after they are logged out of FusionAuth’s OAuth front-end: your application. For this example, set this value to http://localhost:3000/logout. You’ll see how this is used below.
  • Make sure the Authorization Code grant is selected in the Enabled Grants .
  • Set the Require registration field to true.

Once you have configured the application, you’ll click the blue Save button (disk) at the top of the page.

Save Button Application Form

Then, click on the green magnifying glass on the list view next to your application. Scroll down to the OAuth Configuration section and copy the Client Id and Client Secret values off to a text file. You’ll use them later.

Application Details

4. Grant Permissions

Next, grant the user you created in the Setup Wizard permissions to log into your new Application.

This can be done from the User management section of the FusionAuth admin UI. Click the Users link in the navigation. If needed, search for the user.

Manage the user’s details by clicking on the black Manage button.

User Search

Once you are in this user’s account, you’ll click the Add Registration button. This will take us to a page that will allow us to add a registration for this user to the new Application. Registrations associate users with applications. You may have to select an Application if there are multiple options.

Registration Add Form

For this tutorial, you don’t need to provide any additional information for the registration; click the save button at the top.

Now the user has permissions to log into your application.

Adding a registration to a user is not always required to let your users log in to your application.

FusionAuth supports self-service registration. With self-service registration enabled, a registration will be added when a user signs up. Learn more about self-service registration.

You want to make sure your user account has a first name, which should appear under the picture.

If they don’t, use the Edit User button in the upper right hand corner of the user details screen to edit the account and give them one. The first name is how they’ll be greeted in the application.

5. Configure the Backend to Complete the Login

Before you test the login, examine the route/controller in your application’s backend that will complete the OAuth workflow.

After a user logs into FusionAuth’s OAuth front-end successfully, their browser will be redirected to the URL you provided in the configuration step above. This redirect will contain a query parameter called code that contains the one-time use OAuth authorization code from FusionAuth.

Your application code provides the code parameter in a request to FusionAuth. It will exchange the code for an access token, which can be used for a variety of purposes.

In order to complete the process, you’ll use a sample application, which uses the FusionAuth typescript client library, Express and Pug templates. This code is available for download.

To get started, clone the project, and navigate to the project directory.

Get the Example Application Code

git clone https://github.com/FusionAuth/fusionauth-example-5-minute-guide \
&& cd fusionauth-example-5-minute-guide

Here’s a sample .env file, which includes some configuration you’ll need to change.

The Contents Of the .env.sample File

CLIENT_ID=CHANGEME
CLIENT_SECRET=CHANGEME
BASE_URL=http://localhost:9011

Copy .env.sample To .env

cp .env.sample .env

Edit the .env file and update the CLIENT_ID and CLIENT_SECRET values. Set them to the Client Id and the Client Secret you noted when examining the application configuration in a previous step.

If you are not running locally, change the BASE_URL to the correct value.

That’s all the changes you need to make to the .env file.

Next, here’s the package.json file, which includes all the components you’ll need.

The Contents Of the package.json File

{
  "name": "fusionauth-example-5-minute-guide",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "@fusionauth/typescript-client": "^1.45.0",
    "cookie-parser": "~1.4.6",
    "debug": "~4.3.4",
    "dotenv": "^16.0.3",
    "express": "^4.18.2",
    "express-session": "1.17.3",
    "http-errors": "~2.0.0",
    "morgan": "~1.10.0",
    "pkce-challenge": "^3.1.0",
    "pug": "^3.0.3"
  }
}

Install required packages using npm.

Install Required Packages

npm install

This application is almost ready to run, but let’s look at some code first. Here’s the heart of the application, the main Express router.

The Node.js Complete Express Router

const express = require('express');
const router = express.Router();
const {FusionAuthClient} = require('@fusionauth/typescript-client');

// tag::clientIdSecret[]
// set in the environment or directly
const clientId = process.env.CLIENT_ID; // or set directly
const clientSecret = process.env.CLIENT_SECRET; // or set directly
// end::clientIdSecret[]

// tag::baseURL[]
const fusionAuthURL = process.env.BASE_URL;
// end::baseURL[]

const client = new FusionAuthClient('noapikeyneeded', fusionAuthURL);
const pkceChallenge = require('pkce-challenge');

// tag::logoutRoute[]
/* logout page. */
router.get('/logout', function (req, res, next) {
  req.session.destroy();
  res.redirect(302, '/');
});
// end::logoutRoute[]

/* GET home page. */
router.get('/', function (req, res, next) {
  const stateValue = Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
  req.session.stateValue = stateValue;

  //generate the pkce challenge/verifier dict
  const pkce_pair = pkceChallenge.default();
  // Store the PKCE verifier in session
  req.session.verifier = pkce_pair['code_verifier'];
  const challenge = pkce_pair['code_challenge'];
  res.render('index', {user: req.session.user, title: 'FusionAuth Example', clientId: clientId, challenge: challenge, stateValue: stateValue, fusionAuthURL: fusionAuthURL});
});

// tag::fullOAuthCodeExchange[]
/* OAuth return from FusionAuth */
router.get('/oauth-redirect', function (req, res, next) {
  const stateFromServer = req.query.state;
  if (stateFromServer !== req.session.stateValue) {
    console.log("State doesn't match. uh-oh.");
    console.log("Saw: " + stateFromServer + ", but expected: " + req.session.stateValue);
    res.redirect(302, '/');
    return;
  }

// tag::exchangeOAuthCode[]
  // This code stores the user in a server-side session
 client.exchangeOAuthCodeForAccessTokenUsingPKCE(req.query.code,
                                                 clientId,
                                                 clientSecret,
                                                 'http://localhost:3000/oauth-redirect',
                                                 req.session.verifier)
// end::exchangeOAuthCode[]
      .then((response) => {
        console.log(response.response.access_token);
        return client.retrieveUserUsingJWT(response.response.access_token);
      })
      .then((response) => {
// tag::setUserInSession[]
        req.session.user = response.response.user;
        return response;
      })
// end::setUserInSession[]
      .then((response) => {
        res.redirect(302, '/');
      }).catch((err) => {console.log("in error"); console.error(JSON.stringify(err));});

});
// end::fullOAuthCodeExchange[]

  // This code can be set in the last promise above to send the access and refresh tokens
  // back to the browser as secure, HTTP-only cookies, an alternative to storing user info in the session
  //     .then((response) => {
  //       res.cookie('access_token', response.response.access_token, {httpOnly: true});
  //       res.cookie('refresh_token', response.response.refresh_token, {httpOnly: true});
  //       res.redirect(302, '/');
  //     }).catch((err) => {console.log("in error"); console.error(JSON.stringify(err));});

module.exports = router;

There are three main routes:

  • /logout, which logs the user out by destroying the local session
  • /, which is the home page
  • /oauth-redirect which handles the results of the login at FusionAuth and completes the grant, as mentioned above. After the token is retrieved, the user is stored in the session. This will be discussed further below.

There are also files under the views directory, which are the views shown to the end user.

6. Store the User Object In The Session

There is a route that corresponds to the URL you configured in the FusionAuth admin user interface above (https://localhost:3000/oauth-redirect). This is critical functionality, so let’s take a closer look at this.

Here’s the complete code for exchanging the token, retrieving the user data, storing it in the session, and redirecting the user.

The Complete OAuth Code Exchange Function

/* OAuth return from FusionAuth */
router.get('/oauth-redirect', function (req, res, next) {
  const stateFromServer = req.query.state;
  if (stateFromServer !== req.session.stateValue) {
    console.log("State doesn't match. uh-oh.");
    console.log("Saw: " + stateFromServer + ", but expected: " + req.session.stateValue);
    res.redirect(302, '/');
    return;
  }

// tag::exchangeOAuthCode[]
  // This code stores the user in a server-side session
 client.exchangeOAuthCodeForAccessTokenUsingPKCE(req.query.code,
                                                 clientId,
                                                 clientSecret,
                                                 'http://localhost:3000/oauth-redirect',
                                                 req.session.verifier)
// end::exchangeOAuthCode[]
      .then((response) => {
        console.log(response.response.access_token);
        return client.retrieveUserUsingJWT(response.response.access_token);
      })
      .then((response) => {
// tag::setUserInSession[]
        req.session.user = response.response.user;
        return response;
      })
// end::setUserInSession[]
      .then((response) => {
        res.redirect(302, '/');
      }).catch((err) => {console.log("in error"); console.error(JSON.stringify(err));});

});

This route retrieves the code parameter from the request and exchanges it for an access token. This access token can be used for a variety of purposes, but in this tutorial is used to retrieve user data from FusionAuth.

Once you have the User object, it is stored in a server side session so that it can be used in other parts of the application. This includes a view where it is used to greet the user.

There are other options for storing this user object, but for this tutorial, this will work fine. If you want to learn more, such as other patterns for storing the token, please check out these articles about login workflows.

The final thing this code does is redirect the authorized user back to the homepage. There, they’ll be greeted by name, as you’ll see below.

7. Test the Application

Now that everything is written, let’s test the workflow. Fire up the Node.js application using this command.

Start the Node.js Server

npm start

Open http://localhost:3000/ in an incognito browser.

You need to use an incognito browser because otherwise FusionAuth won’t prompt you to login. You have already logged into FusionAuth when you configured the application above, and will be recognized.

FusionAuth supports single sign-on, and will automatically redirect you to the application if you are already logged in with the FusionAuth server.

The Node.js Application Home Screen

Click the Login link, this will take us to FusionAuth’s login page.

The Login Screen

From here, you can log in with the account you used to access the admin user interface above. If you log in with a valid account that is registered for the application, you arrive at the home page, but are greeted by name.

Successful Login

In a real application, there’d be a lot more than a “Hello” message, but this is a building block. You’ve successfully authenticated and stored user data in the express session. From here, you can add other pages, hide or display information based on attributes of the user, and more.

If, on the other hand, you log in with an account that exists in FusionAuth but is not registered for the application, you’ll end up at an error page.

Login Without Registration

This error message and overall page look and feel can be modified via a custom FusionAuth theme.

8. Logout

Of course, for every login, there should be a corresponding logout. Log out by clicking on the “Logout” link. This will log you out of both FusionAuth and the application.

Here’s the pug code which builds a link to log the user out.

The Logout Link

    a(href=fusionAuthURL+'/oauth2/logout/?client_id='+clientId) Logout

When you visit this link, you’re sent to FusionAuth, where the FusionAuth session is destroyed, and then to the application’s logout URL. This URL was configured when you set up the Application in FusionAuth, and has the value of http://localhost:3000/logout.

That route destroys the local session, logging you out from the Node.js application, and then sending you back to the home page.

Destroying the Local Session

/* logout page. */
router.get('/logout', function (req, res, next) {
  req.session.destroy();
  res.redirect(302, '/');
});

Now you are logged out.

9. Summing Up

You’ve now taken an application and layered on authentication with FusionAuth. A user who is registered for the application can log in, view private data, and log out.

Congrats!

Here are typical next steps.

Of course, there are plenty of other docs, covering a variety of topics. Here are some popular ones.