fusionauth logo
search-interface-symbol
Downloads
Quickstarts
API Docs
SDKs
search-interface-symbol
talk to an expert
Log In
talk to an expert
Navigate to...
  • Welcome
  • Getting Started
    • Getting Started
    • 5-minute Setup Guide
      • Overview
      • Docker
      • Fast Path
      • Sandbox
    • Setup Wizard & First Login
    • Register a User and Login
    • Self-service Registration
    • Start and Stop FusionAuth
    • Core Concepts
      • Overview
      • Users
      • Roles
      • Groups
      • Registrations
      • Applications
      • Tenants
      • Identity Providers
      • Authentication/Authorization
      • Integration Points
    • Example Apps
      • Overview
      • Dart
      • Go
      • Java
      • JavaScript
      • .NET Core
      • PHP
      • Python
      • Ruby
    • Tutorials
      • Overview
      • Angular
      • C# .NET
      • .NET Core API
      • Express.js
      • Express API
      • Flutter
      • Java Spring
      • Laravel API
      • Python Django
      • Python Flask
      • React
      • Ruby on Rails
      • Ruby on Rails API
      • Vue.js
  • Installation Guide
    • Overview
    • System Requirements
    • Server Layout
    • Cloud
    • Cluster
    • Docker
    • Fast Path
    • Kubernetes
      • Overview
      • Deployment Guide
      • Minikube Setup
      • Amazon EKS Setup
      • Google GKE Setup
      • Microsoft AKS Setup
    • Kickstart™
    • Homebrew
    • Marketplaces
    • Packages
    • Database
    • FusionAuth App
    • FusionAuth Search
    • Common Configuration
  • Migration Guide
    • Overview
    • General
    • Auth0
    • Amazon Cognito
    • Duende IdentityServer
    • Firebase
    • Keycloak
    • Microsoft Azure AD B2C
    • Supabase
    • Tutorial
  • Admin Guide
    • Overview
    • Account Portal
    • CLI
    • Config Management
    • Editions and Features
    • Key Rotation
    • Licensing
    • Monitoring
    • Prometheus Setup
    • Proxy Setup
    • Reference
      • Overview
      • Configuration
      • CORS
      • Data Types
      • Hosted Login Pages Cookies
      • Known Limitations
      • Password Hashes
    • Releases
    • Roadmap
    • Search And FusionAuth
    • Securing
    • Switch Search Engines
    • Technical Support
    • Troubleshooting
    • Upgrading
    • User Support Guide
    • Vulnerabilities
    • WebAuthn
  • Login Methods
    • Identity Providers
      • Overview
      • Apple
      • Epic Games
      • External JWT
        • Overview
        • Example
      • Facebook
      • Google
      • HYPR
      • LinkedIn
      • Nintendo
      • OpenID Connect
        • Overview
        • Amazon Cognito
        • Azure AD
        • Discord
        • Github
        • Okta
      • Sony PlayStation Network
      • Steam
      • Twitch
      • Twitter
      • SAML v2
        • Overview
        • ADFS
        • Azure AD
        • Okta
      • SAML v2 IdP Initiated
        • Overview
        • Okta
      • Xbox
    • OIDC & OAuth 2.0
      • Overview
      • Endpoints
      • Tokens
      • OAuth Modes
      • URL Validation
      • Integrations
        • CockroachDB
        • Salesforce
    • Passwordless
      • Overview
      • Magic Links
      • WebAuthn & Passkeys
    • SAML v2 IdP
      • Overview
      • Aiven
      • Google
      • PagerDuty
      • SendGrid
      • Tableau Cloud
      • Zendesk
  • Developer Guide
    • Overview
    • API Gateways
      • Overview
      • Amazon API Gateway
      • HAProxy
      • Kong Gateway
      • ngrok Cloud Edge
    • Client Libraries & SDKs
      • Overview
      • Angular
      • Dart
      • Go
      • Java
      • JavaScript
      • .NET Core
      • Node
      • OpenAPI
      • PHP
      • Python
      • React
      • Ruby
      • Typescript
      • Vue
    • Events & Webhooks
      • Overview
      • Writing a Webhook
      • Securing Webhooks
      • Events
        • Overview
        • Audit Log Create
        • Event Log Create
        • JWT Public Key Update
        • JWT Refresh
        • JWT Refresh Token Revoke
        • Kickstart Success
        • Group Create
        • Group Create Complete
        • Group Delete
        • Group Delete Complete
        • Group Update
        • Group Update Complete
        • Group Member Add
        • Group Member Add Complete
        • Group Member Remove
        • Group Member Remove Complete
        • Group Member Update
        • Group Member Update Complete
        • User Action
        • User Bulk Create
        • User Create
        • User Create Complete
        • User Deactivate
        • User Delete
        • User Delete Complete
        • User Email Update
        • User Email Verified
        • User IdP Link
        • User IdP Unlink
        • User Login Failed
        • User Login Id Dup. Create
        • User Login Id Dup. Update
        • User Login New Device
        • User Login Success
        • User Login Suspicious
        • User Password Breach
        • User Password Reset Send
        • User Password Reset Start
        • User Password Reset Success
        • User Password Update
        • User Reactivate
        • User Reg. Create
        • User Reg. Create Complete
        • User Reg. Delete
        • User Reg. Delete Complete
        • User Reg. Update
        • User Reg. Update Complete
        • User Reg. Verified
        • User 2FA Method Add
        • User 2FA Method Remove
        • User Update
        • User Update Complete
    • Guides
      • Overview
      • Anonymous Users
      • Application Specific Email Templates
      • Authentication Tokens
      • Exposing A Local Instance
      • JSON Web Tokens
      • Key Master
      • Localization and Internationalization
      • Multi-Factor Authentication
      • Multi-Tenant
      • Passwordless
      • Registration-based Email Verification
      • Searching With Elasticsearch
      • Securing Your APIs
      • Silent Mode
      • Single Sign-on
      • Two Factor (pre 1.26)
      • User Actions
    • Integrations
      • Overview
      • CleanSpeak
      • Kafka
      • Twilio
    • Plugins
      • Overview
      • Writing a Plugin
      • Custom Password Hashing
    • User Control & Gating
      • Overview
      • Gate Unverified Users
      • Gate Unverified Registrations
      • User Account Lockout
  • Customization
    • Email & Templates
      • Overview
      • Configure Email
      • Email Templates
      • Email Variables
      • Message Templates
    • Lambdas
      • Overview
      • Apple Reconcile
      • Client Cred. JWT Populate
      • Epic Games Reconcile
      • External JWT Reconcile
      • Facebook Reconcile
      • Google Reconcile
      • HYPR Reconcile
      • JWT Populate
      • LDAP Connector Reconcile
      • LinkedIn Reconcile
      • Nintendo Reconcile
      • OpenID Connect Reconcile
      • SAML v2 Populate
      • SAML v2 Reconcile
      • SCIM Group Req. Converter
      • SCIM Group Resp. Convtr.
      • SCIM User Req. Converter
      • SCIM User Resp. Converter
      • Self-Service Registration
      • Sony PSN Reconcile
      • Steam Reconcile
      • Twitch Reconcile
      • Twitter Reconcile
      • Xbox Reconcile
    • Messengers
      • Overview
      • Generic Messenger
      • Twilio Messenger
    • Themes
      • Overview
      • Client-side Password Validation
      • Examples
      • Helpers
      • Kickstart Custom Theme
      • Localization
      • Template Variables
      • Tailwind CSS
  • Premium Features
    • Overview
    • Advanced Registration Forms
    • Advanced Threat Detection
    • Application Specific Themes
    • Breached Password Detection
    • Connectors
      • Overview
      • Generic Connector
      • LDAP Connector
      • FusionAuth Connector
    • Entity Management
    • SCIM
      • Overview
      • Azure AD Client
      • Okta Client
      • SCIM-SDK
    • Self Service Account Mgmt
      • Overview
      • Updating User Data & Password
      • Add Two-Factor Authenticator
      • Add Two-Factor Email
      • Add Two-Factor SMS
      • Add WebAuthn Passkey
      • Customizing
      • Bootstrapping Login
      • Troubleshooting
    • WebAuthn
  • APIs
    • Overview
    • Authentication
    • Errors
    • API Explorer
    • Actioning Users
    • API Keys
    • Applications
    • Audit Logs
    • Connectors
      • Overview
      • Generic
      • LDAP
    • Consents
    • Emails
    • Entity Management
      • Overview
      • Entities
      • Entity Types
      • Grants
    • Event Logs
    • Families
    • Forms
    • Form Fields
    • Groups
    • Hosted Backend
    • Identity Providers
      • Overview
      • Links
      • Apple
      • External JWT
      • Epic Games
      • Facebook
      • Google
      • HYPR
      • LinkedIn
      • Nintendo
      • OpenID Connect
      • SAML v2
      • SAML v2 IdP Initiated
      • Sony PlayStation Network
      • Steam
      • Twitch
      • Twitter
      • Xbox
    • Integrations
    • IP Access Control Lists
    • JWT
    • Keys
    • Lambdas
    • Login
    • Message Templates
    • Messengers
      • Overview
      • Generic
      • Twilio
    • Multi-Factor/Two Factor
    • Passwordless
    • Reactor
    • Registrations
    • Reports
    • SCIM
      • Overview
      • SCIM User
      • SCIM Group
      • SCIM EnterpriseUser
      • SCIM Service Provider Config.
    • System
    • Tenants
    • Themes
    • Users
    • User Actions
    • User Action Reasons
    • User Comments
    • WebAuthn
    • Webhooks
  • Release Notes

    HAProxy API Gateway

    Overview

    HAProxy is a popular load balancer that can also be used as an API Gateway.

    You can configure HAProxy to handle authorization to services through JSON Web Tokens (JWTs) issued on behalf of a user authenticated by an identity provider.

    In this document, you’ll learn how to set up HAProxy with FusionAuth as the identity provider to protect an HTTP service using JWTs.

    Prerequisites

    • A FusionAuth instance running on a publicly accessible URL. You can spin up a basic FusionAuth Cloud instance or install it on any server.

    • An HAProxy instance routed to an HTTP service. You can follow this guide to get a simple example running on Docker.

    When creating your HAProxy container, be sure to use haproxytech/haproxy-alpine:latest, not haproxytech/haproxy-alpine:2.4 as the guide instructs. Version 2.4 does not support the full set of JWT features needed in this tutorial.

    If you have trouble getting the haproxy container from the guide to start, make sure your haproxy.cfg file ends with a newline. Even a white space character on the last line will prevent the container from running.

    FusionAuth Configuration

    Navigate to your FusionAuth instance.

    First, you need to make sure that the JWT issuer setting is correct. Navigate to Tenants → Your Tenant and change the Issuer field to the URL of your FusionAuth instance, for example, https://local.fusionauth.io. Record this value, because you will use it later when generating the JWT. It will be referred to as <YOUR_FUSIONAUTH_URL>.

    Tenant issuer

    Next, you need to configure a FusionAuth application to issue tokens to access the HAProxy services.

    Navigate to Applications and create a new application. Fill out the Name field, then click the JWT tab and toggle the Enabled switch. Select Auto generate a new key on save…​ for the Access Token signing key and Id token signing key fields. This will generate an RS256 asymmetric key pair specifically for this application.

    Application JWT settings

    Click the Save button.

    After saving the new application, find it in the list of applications and click the View button next to it (look for the green magnifying glass). Here you will find the application Id. Record this value, as you will need it further on. It will be referred to later as <YOUR_APPLICATION_ID>.

    Application id

    Now, navigate to Settings → Key Master. You will see the access key that you’ve just created in this list. By default, it has the name Access token signing key generated for application <NAME_OF_APPLICATION>. View this key and copy the entire contents of the PEM encoded field under the Public Key section. Create a file called pubkey.pem in the same directory as you placed the haproxy.cfg file while setting up HAProxy with the public key you copied. Remember this filename, as you will supply it to the HAProxy configuration file later.

    Record the public key for later use

    You will use FusionAuth’s API to generate a test JWT for your application. However, in a real-world application, the user or service would get the JWT through an OAuth grant. In the case of a user, the appropriate grant is the Authorization Code grant, and for a service, the Client Credentials grant. To make this example simpler, you’ll use the Login API to get the JWT. To do this, you need an API key with POST access to the /api/login endpoint. Go to Settings → API Keys to add the key. Make sure the appropriate tenant is selected in the Tenant field.

    API key with post access to /api/login

    After saving the API key, you can view the key in the list of API keys. Make a note of the value of the Key field. You will need to click the red padlock icon next to the key to reveal the value.

    Contents of API key

    Finally, make sure there is at least one user registered to your application so that you can test with a JWT issued for that user. You can create a new user by navigating to Users → Add user. Toggle the Send email to set up password switch to disabled and manually enter a password in the Password field.

    Create user with email and password

    After saving this user, click Manage and go to the Registrations tab. Click Add Registration to register the user to your application.

    Register user to HAProxy application

    Configure HAProxy

    After following the HAProxy quickstart guide noted earlier, you can execute the following command in the terminal:

    Test HAProxy setup
    
    curl -X GET -I http://localhost:80

    If you have correctly set up HAProxy, this command will return HTTP/1.1 200 OK, indicating a successful connection to the service.

    Now you can restrict access to the service by requiring a JWT. To do this, add the following lines to the frontend myfrontend section of your haproxy.cfg file. More information on this can be found here.

    Add JWT requirement to HAProxy configuration file
    
      http-request deny content-type 'text/html' string 'Missing Authorization HTTP header' unless { req.hdr(authorization) -m found }
    
      # get header part of the JWT
      http-request set-var(txn.alg) http_auth_bearer,jwt_header_query('$.alg')
      http-request set-var(txn.iss) http_auth_bearer,jwt_payload_query('$.iss')
      http-request set-var(txn.aud) http_auth_bearer,jwt_payload_query('$.aud')
    
      # get payload part of the JWT
      http-request set-var(txn.exp) http_auth_bearer,jwt_payload_query('$.exp','int')
    
      # Validate the JWT
      http-request deny content-type 'text/html' string 'Unsupported JWT signing algorithm'  unless { var(txn.alg) -m str RS256 }
      http-request deny content-type 'text/html' string 'Invalid JWT issuer'  unless { var(txn.iss) -m str <YOUR_FUSIONAUTH_URL> }
      http-request deny content-type 'text/html' string 'Invalid JWT audience'  unless { var(txn.aud) -m str <YOUR_APPLICATION_ID> }
      http-request deny content-type 'text/html' string 'Invalid JWT signature'  unless { http_auth_bearer,jwt_verify(txn.alg,"/etc/haproxy/pubkey.pem") -m int 1 }
    
      http-request set-var(txn.now) date()
      http-request deny content-type 'text/html' string 'JWT has expired' if { var(txn.exp),sub(txn.now) -m int le 0 }

    In the Invalid JWT issuer and Invalid JWT audience lines above, the placeholder values <YOUR_FUSIONAUTH_URL> and <YOUR_APPLICATION_ID> will need to be updated with the appropriate values from the previous section. <YOUR_FUSIONAUTH_URL> is the fully-qualified URL from the Issuer field in your Tenant configuration and <YOUR_APPLICATION_ID> is the UUID that identifies your FusionAuth application.

    Note that your configuration file will look slightly different from the one in HAProxy’s documentation linked above. You only need to import the lines necessary for proper JWT authentication. For example, you do not need to bind to port 443 or set up an SSL certificate, although it is good practice to do so in a production environment. More information on this can be found here.

    Restart HAProxy by running the following:

    Restart the HAProxy load balancer
    
    sudo docker kill -s HUP haproxy

    At this point, the service is inaccessible without a token. To confirm this, you can execute:

    Test that service is inaccessible without JWT
    
    curl -X GET -I http://localhost:80

    This will return HTTP/1.1 403 Forbidden. You can also omit the -I option to see the error message that you supplied above, namely Missing Authorization HTTP header.

    Accessing the Service with a JWT

    You can now generate a test JWT to access your HAProxy service using FusionAuth’s login API. Execute the following command in your terminal:

    Get JWT token from FusionAuth
    
    curl --location --request POST '<YOUR_FUSIONAUTH_URL>/api/login' \
    --header 'Authorization: <API_KEY>' \
    --header 'Content-Type: application/json' \
    --data-raw '  {
        "loginId": "<USER_EMAIL>",
        "password": "<USER_PASSWORD>",
        "applicationId": "<APPLICATION_ID>",
        "noJWT" : false
      }'

    Here, <YOUR_FUSIONAUTH_URL> is the Issuer name, and <API_KEY> is the key you noted when setting it up on the Settings → API Keys page.

    For <APPLICATION_ID>, use the Id of your FusionAuth application, noted when setting up the application.

    The values for <USER_EMAIL> and <USER_PASSWORD> are the Username and Password of the test user that you registered to that application.

    The returned response from FusionAuth should look similar to the following:

    Token response from API call
    
    {
        "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImhDUjA4X3daR2s0OUFlYUFmRDY5ZmJKWmRGTSJ9.eyJhdWQiOiI2M2I3M2Y3Ni03NDAwLTQ4N2QtYjEyMi01NzA1Yjg0OGRhODAiLCJleHAiOjE2NzMzNjYyMDQsImlhdCI6MTY3MzM2MjYwNCwiaXNzIjoiaHR0cHM6Ly9mdXNpb25hdXRoLnJpdHphLmNvIiwic3ViIjoiMzk2MzAwMGYtNjg2ZC00MTY5LWI2MjgtOWM5YzQ1MzRiNzgwIiwianRpIjoiZDk3ZGIyZWYtZjExNS00ZDIxLWFlOTQtMDIyN2RmMGU4YzI5IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6ImJvYkBhd3MuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByZWZlcnJlZF91c2VybmFtZSI6ImJvYmF3cyIsImFwcGxpY2F0aW9uSWQiOiI2M2I3M2Y3Ni03NDAwLTQ4N2QtYjEyMi01NzA1Yjg0OGRhODAiLCJyb2xlcyI6W10sImF1dGhfdGltZSI6MTY3MzM2MjYwNCwidGlkIjoiZjAwNGMxZmUtNDg0Yi05MDJjLWQ3Y2EtYmRiYzQ2NGRhMGI3In0.m7gzXhNLToPNVE1p5Vo2pLgP6WBcPNfS_zZJnJ81mdEgi6-orViz-tU8j0L8wva0-8KlMdy54cq_XjnDnYJ0aX90O4ZE_QVU5NuDDfzXH14wQtKQoIIydsB6ZvQoBt8JNFUHJb9ANLCGnfn6FVQKqPIzye18Gx_7wYSVokw3eLNFyzrq9dwOD5Q8V9gvZmXV2pTokQAtA7qFaadb2dIeFlSEB7wamKiZLXILjeWAeMbbvAAMQZWFh46UJjwr06QTd8PxQmRwDWWznJy1Vs8EAgZA4vkRSWnn3IbiaCtOaL1ANuEex6il7q32ahxj0Ncm9wn0DbDsQE9NB0CCNTSIhA",
        "tokenExpirationInstant": 1673366204805,
        "user": {
            "sampleuserdata" : "..."
        }
    }

    Copy the token value. You can now gain access to your HAProxy service by passing this value as a bearer token.

    Gain access with JWT
    
    curl -I http://localhost:80 \
    -H 'Authorization: Bearer <TOKEN>'

    Here, TOKEN is the value of the JWT that you copied from the /api/login call.

    This command should return HTTP/1.1 200 OK, indicating successful authorization to the HAProxy service.

    Troubleshooting

    • The JWT issued by the FusionAuth Login API has an expiration date. If you wait too long before using it to call the HAProxy service, the token will expire and the call will fail. You can resolve this by rerunning the curl command to obtain another JWT token and use the new token.

    • The issuer URL set in the Issuer field from Tenants → Your Tenant must exactly match the <YOUR_FUSIONAUTH_URL> value in the haproxy.cfg file. If the values do not match exactly, including any white space and slashes, the JWT token will not be accepted by HAProxy.

    Next Steps

    You’ve learned how to enable JWT authorization on an HAProxy service. In a production application, using HAProxy and the JWT plugin is useful to secure the backend services of the application. This way, each service does not need to handle authorization or authentication. Instead, authentication and authorization are handled by FusionAuth and HAProxy.

    You used the FusionAuth API to create a test token on behalf of a user. For a production system, the token will be generated after a user signs in to your application through a frontend. Check out some of the framework integration tutorials to implement that:

    • Java Spring Quickstart

    • Express Quickstart

    • Python Django Quickstart

    • Python Flask Quickstart

    • React Quickstart

    • All Quickstarts

    • Vue.js Quickstart

    Feedback

    How helpful was this page?

    See a problem?

    File an issue in our docs repo

    Have a question or comment to share?

    Visit the FusionAuth community forum.

    © 2023 FusionAuth
    How-to
    Blog
    Expert Advice
    Download
    Release Notes
    Subscribe for developer updates