Jon Rumsey

An online markdown blog and knowledge repository.


Project maintained by nojronatron Hosted on GitHub Pages — Theme by mattgraham

Working with Auth0 With Express

A collection of notes for future review about implementing Auth0 server-side for an Expressjs app.

Table of Contents

Required Reading

Quick Summaries

Auth0 Service and Configuration:

Auth0 Authentication and Token Validation using JWKS:

  1. An application must be configured to find the Auth0 Domain and must have the Auth0 ClientID for the Application configuration in Auth0. An audience and Scope are not required but can be used for fine-grain control during authorization.
  2. The front-end will also need to know (progorammatically or by user input) the URL to the API Server they want authorization to access.
  3. When a user needs to login to the application, a redirect to Auth0's Universal Login provided by the configured in Auth0 Application will display, showing configured authentication types.
  4. When a user authenticates, the Tenant (?) supplies an Auth Token to return to the web app, which can be stored securely.
  5. When a user tries to access a protected API that requires a valid Auth Token, the web app must send the token (hopefully via https) to the custom API server (or if using device-2-device authentication, it is sent to the Auth0 API Management Service with a request to access an audience, but this is beyond the scope of these steps).
  6. The custom API server must know the Auth0 Domain and Auth0 Issuer BaseURL, and the Auth0 JWKS URI. For Device-2-device authentication, an Audience must also be configured (out of scope).
  7. The custom API server processes the client token using jwks-rsa and jsonwebtoken, validating it using the JWKS URI.
  8. This is the point where the token is authenticated or not. If it is authenticated, then Cookies can be used to "keep the user authenticated through several API calls", rather than forcing the API server to digest the Auth Token for every single call. Either way would work though.
  9. Timeouts: There should be timeouts on Application Session logins though, so the custom API server must be configured accordingly. Refresh Tokens eventually expire and Token Lifetimes too, so that scenario should trigger a re-authentication cycle on the client-side (not the custom API server side).
  10. Logouts: Application session logout is just a matter or expiring the Cookies. Auth0 Logout requires the web app to call the logout() method in the Auth0 SDK. Any configured 'logout redirect' will be sent back from Auth0 so the web app redirects to the correct page.

Terminology

Machine-to-Machine (M2M) Application

Applications that need to authenticate without user credentials. Examples:

Instead of a username + password, a ClientID and Client Secret ae used for authentication, and in return are supplied with an Auth Token.

The flow is:

  1. M2M App sends ClientID and ClientSecret to Auth0 Tenant.
  2. Auth0 Tenant validates ClientID and ClientSecret and sends back an Auth Token (or an error).
  3. M2M App sends a request with Auth Token in headers to the API Server endpoint.
  4. API Server endpoint sends a response with data (or access denied).

Java Web Tokens, Signatures, and Keys

JWT: Java Web Token. Check out JWT.io and Auth0 Docs on JWT.

Security Algorithms:

Auth0 SDKs:

In-code:

If a JWT Parser or Validator returns an error message 'jwt malformed' your code must reject the request to remain secure.

JWT Aud and Scope Fields:

Scope Examples:

Auth0 uses scope=openid ... to limit token size.

An API should enumerate Scope to ensure the API Endpoint rejects access when a Token does not include the correct Permission.

JWT Makeup:

Refresh Tookens

Auth0 only allows obtaining a Refresh Token if one of the following flows is used:

There are rate-limits set on Refresh Token requests (200 per Application). Manage this in the Auth0 dashboard.

When testing, follow Auth0's instructions in Token Best Practices to avoid rate limit issues.

React Auth0 Requirements

  1. Pick your technology. Client, Server, CLI, or other. Expressjs is listed among the options. Note: So is Java Spring Framework.
  2. Allowed Callback URLs. Example: 'http://localhost:3000/callback'. Auth0 redirects the user after they have authenticated.
  3. Allowed Logout URLs. Example: 'http://localhost:3000'. Auth0 redirects the user after they logout of Auth0.
  4. Install 'express-openid-connect' module (maintained by Auth0).
  5. Generate a 32-bit random hexadecimal secret using either openssl rand -hex 32 or GRC Password Generator.
  6. Configuration of the OpenID Auth Router.

Routing Considerations

In these cases, use a wrapper around those protected routes that calls Auth0, enabling use of 'isAuthenticated' (and possibly other members).

Testing Considerations

Application Settings

The most critical configuration items to set up:

API Setting:

Optional Settings:

Always verify the Application's Advanced Settings, Endpoints has the correct entiries in them. If any are edited they will be pointing to a non-existing URL (or worse). Most of the time actual entries are already set by Auth0 during Application Setup:

Client ID and Client Secret

Depending on the provider used for OAuth2, these may be named differently.

These are required to enable fully-features OAuth2 integration with your app.

Keys

Custom Developer Keys:

Provider Options:

Your Own Developer Keys:

Auth0 Social Authenticators

This is related to Custom Developer Keys.

In the free tier, Auth0 allows up to 2 "social" authenticators.

Auth0 Paid Tiers allow more.

Auth0 Username Password Authenticator

In the free tier, Auth0 will support capture and use of end-users custom usernames and passwords.

Login and Logout

Three layers of authentication:

Note: An Auth0 Tenant-level 'Login URI' is available that can be used to effect all Applications within that Tenant.

Cookies for Application Login and Logout

Setting and Forgetting Cookies in Express

Request Cookies: There could be multiple cookies within a Request object.

Response Cookie: When setting properties of the Response object, each Cookie is set individually.

Setting a Cookie in Response:

Cookie Options:

Note: res.clearCookie must match all parameters and options in order to clear the identified cookie.

See ExpressJS 4x API for details.

Stores user session cookie data on the client.

Redirect After Logout

Redirection post-logout can be done by registering the 'redirect url' in the Application Settings in Auth0.

Note: There is a Tenant-level 'redirect url' ("Allowed Logout URLs") which applies to all Applications within that Tenant that don't have 'redirect url' configured.

Username Password Authentication

Auth0 provides a username-password-authentication database for free. It is possible to redirect Auth0 to your own DB.

User Management in Auth0

Creating a Role (RoleID) enables establishment of Permissions to Users that will access an Auth0 API.

Code Fellows suggested using:

Enabling API Authentication the Auth0 Way

  1. Create an Auth0 API.
  2. Decide whether to set any Permissions.
  3. Follow the steps at Auth0 Dashboard, Quickstart tab, for your API.
  4. Once configured, go to the Test tab and follow the steps to request an Authorization Token, and then GET authenticated.
  5. Configure your Front-end with the API Server settings and an Audience setting (the URL to your back-end server).
  6. Follow the Auth0 documentation to programmatically check for authorization on the Front end.
  7. Follow the Auth0 documentation for your back-end to ensure it is registered as an M2M App with Auth0, and that it has the Auth0 API settings for validating the user Authorization Token.
  8. Be sure that the front-end acquires the correct Auth Token from Auth0 API before trying to get authorization from your custom backend server.

Note: You MUST include the following '/' in the 'issuer' entry. Not doing so will not authorize the jwt token and an error 'jwt issuer invalid' will be returned.

Enabling Authentication to your Custom Backend the CodeFellows Way

This method relies on using the Asymmetric Keys available in the Single Page Application advanced settings, JSON Web Tokens, and auth0-react.

Express Back End:

  1. Install cors, dotenv, jsonwebtoken, and jwks-rsa. Redirects will not work without CORS installed.
  2. Implement an Authorization module that uses jsonwebtoken and jwks-rsa. Implement a client by configuring jwksClient with the JSON Web Key Set URI of the front-end server. Implement getKey function that calls client.getSigningKey() and has a callback (supplying the signingKey) for jwt.verify() to use. Implement verifyUser() function that extracts a valid token from req.headers.authorization and supplies that token to jwt.verify() alogn with getKey, and empty object {}, and errorFirstOrUserCallbackFunction. Catch any errors. Export verifyUser from the module.
  3. Require verifyUser (the custom module from previous step) and insert it (as a middleware) to each route that requires authorization to access.

React Front End:

  1. Install @auth0/auth0-react and perhaps axios (else use fetch).
  2. Import {Auth0Provider} from auth0-react and wrap all child Components within the render(). Remember to configure Auth0Provider with Auth_Domain, Auth_ClientID, and Auth_Redirect.
  3. Optional: Create Login and Logout buttons (see info below).
  4. Child components that need authorization: Import { withAuth0 }. With this module comes auth0.isAuthenticated for free. Use isAuthenticated to show/hide components. The Module that uses 'withAuth0' must export like: export default withAuth0(child); where 'child' is the name of the component e.g. 'App'.
  5. Child Components that require use of useAuth0 functions or properties must import it. Functions include user, isAuthenticated, isLoading...

Get a Token and Configure Axios to Call With Authorization

Within the front-end app, create a component that checks for authentication and if true, configures Axios with a valid Authorization Header with an Auth0 token.

  1. Import withAuth0 and axios (and perhaps React).
  2. Check for authentication with auth0.isAuthenticated.
  3. If authenticated, get claims with auth0.getIdTokenClaims().
  4. Define variable jwt = res.__raw two underscores.
  5. Create an Axios configuration object with method, baseURL (server base address, optionally acquired from env vars), url (target path), headers: { 'Authorization': 'Bearer ${jwt}'}. Note: Last part is a back-ticked template literal!
  6. Execute the Axios call a-la const results = await axios(config); and the authorization header will be included. If path requires token validation (authorization) that will happen automatically on the back end and the appropriate HTTP Status Code (and optional message) should be returned/expected.

Note: Much of this code will need to within asynchronous method(s), and could be executed within componentDidMount() or a useEffect().

Login and Logout Buttons

Login:

  1. Import useAuth0.
  2. Return a function that takes 0 parameters.
  3. Within the function, deconstruct loginWithRedirect from useAuth0().
  4. Function returns a button with an onClick method that calls Auth0 loginWithRedirect().

Logout:

  1. Import userAuth0;
  2. Return a function that takes 0 parameters.
  3. Within the function, deconstruct logout from useAuth0().
  4. Function returns a button with an onClick handler that calls window.location.origin (this helps manage redirects from Auth0).

User Details Available Upon Authorization

useAuth() provides a user object that has properties that can be used for a profile page:

import { userAuth0 } from '@auth0/auth0-react';

const UserProfile = () = > {
  const { user, isLoading } = useAuth0();

  if (isLoading) {
    return <div>Loading . . .</div>;
  }

  return (
    isAuthenticated && (
      <div>
        <img src={user.picture} alt={user.name} />
        <h2>{user.name}</h2>
        <h4>{user.email}<h4>
      </div>
    )
  );
};

export default UserProfile;

Auth0 Tenants

Separate configuration boundaries within an Auth0 account.

Support dev, test, and production phases. Often company-dev and company-qa and another company-prod.

Separate user communities.

Sandboxes: Use to test different deployment scripts or implementation without impacting an existing deployment/production tenant.

If deleted, a Tenant Name can never by used again.

Enter a name, logo, and support email so customers can confirm they are in the right place, and get support if necessary.

Vanity Domain URL:

Enable MFA for your Admins.

Tenants support multiple Admins (recommended).

SSO Cookie Timeout (login session lifetime) is per-Tenant. Default is 7 days.

Sandboxing: Ensure tenants are associated with your account so that the sandboxes are within your account scope.

References

Return to Conted Index

Return to Root Readme