Auth0 Integration in self-hosted authentication APIs

October 15, 2023

Kazi Ehsan Aziz

Your API Server handles registration and authentication

So, you are coming from a traditional backend-handles-authentication scenario, i.e. you have written your own API endpoints that handle user registration and issuance of access tokens. But now you need to integrate Auth0 within your custom APIs. Auth0 is easier to use if you just let your client applications talk to their hosted APIs (in production, preferably behind your own Custom Domain) directly. Auth0 was designed to take care of authentication and authorization for you. But you can still build a traditional authentication backend with your own login and signup APIs, using Auth0's SDK.

First, the best thing to do is to go through Auth0 documentations and familiarize yourself with their terminology (API, Application, various flows, etc.). But it can still be a bit confusing. This article will not explain in details how to setup Auth0. Rather, it is a very concise guide for someone who has already tried to integrate Auth0 within their existing API endpoints, but is still in doubt about their setup. It will be impossible to follow this guide if you have not already tried to setup Auth0 yourself.


An M2M Application

Head over to Auth0's dashboard. Start with creating an API in the existing Tenant.

A Database Connection is also created automatically.

Alongwith the API, a Machine to Machine Application is automatically created which contains our domain, client_id and client_secret. In "Auth0 Dashboard" -> "Applications" -> "Applications" -> "YOUR_M2M_APP" -> "APIs", you will find this M2M Application has been authorised to request access tokens for the API that we just created. But this application has not yet been authorized to request access tokens for the Auth0 Management API.

Under these settings, you can set up the Auth0 SDK in the following manner (shown in Python with FastAPI). This will allow you to sign up users.

  • auth0.py
  • auth.py
from auth0.authentication import Database, GetToken

from src.core.config import settings


def auth0_authapi_database():
    database = Database(settings.AUTH0_DOMAIN, settings.AUTH0_CLIENT_ID)
    return database


def auth0_authapi_token():
    token = GetToken(
        settings.AUTH0_DOMAIN,
        settings.AUTH0_CLIENT_ID,
        client_secret=settings.AUTH0_CLIENT_SECRET,
    )
    return token
from fastapi import APIRouter, Depends
from fastapi.responses import Response
from auth0.authentication import Database, GetToken

from src.core.config import settings
from src.deps.auth0 import auth0_authapi_database, auth0_authapi_token


router = APIRouter()

@router.post("/v1/auth/signup", tags=["auth"])
async def signup(
    auth0api_database: Database = Depends(auth0_authapi_database),
) -> Response:
    response = auth0api_database.signup(
        email="ehsan_1@yopmail.com",
        password="Abc!_1234",
        connection="Username-Password-Authentication",
    )
    return response

Resource Owner Password Flow

But in order to use sign-in with username and password, the M2M Application needs to be given the password Grant Type in "Auth0 Dashboard" -> "Applications" -> "Applications" -> "YOUR_M2M_APP" -> "Settings" -> "Advanced Settings" -> "Grant Types". This password Grant Type is what comprises a Resource Owner Password Flow. Now you can sign-in.

auth.py
# ...

@router.post("/v1/auth/signin", tags=["auth"])
async def signin(
    auth0api_token: GetToken = Depends(auth0_authapi_token),
) -> Response:
    response = auth0api_token.login(
        username="ehsan_1@yopmail.com",
        password="Abc!_1234",
        realm="Username-Password-Authentication",
        audience=settings.AUTH0_API_AUDIENCE,
    )
    return response

# ...

Client Credentials Flow

By default, an M2M only has the Client Credentials grant type. In the Client Credentials Flow, you just provide the Application client id and secret to obtain access tokens from Auth0. As such, the access token has no user information, it is an access token just for the Application itself authorised to use some API declared in Auth0. Hence, the term Machine to Machine.


Authorization Code Flow

Now, it will be ok to go forward with the M2M Application created for us and use its client_id to initialise the SDK. But if you have plans in the future for your API Server to be consumed by a frontend client where Authorization Code Flow might be adopted (client provides backend with an OAuth code, which the backend then exchanges with Auth0 for an access token), then you also need the Authorization Code Grant Type.


A Regular Web App

But instead of adding yet another Grant Type Authorization Code to our M2M application (which is only meant to have the Client Credentials grant type), we can create a new Application of type Regular Web App, which by default will have the Authorization Code and Client Credentials grant types amongst others. We need to add the password grant type manually here as well.

So technically, any one of "Resource Owner Password" or "Client Credentials" or "Authorization Code" flows can be used with both M2M and Regular Web App type Applications.


Granting Auth0 Management API permission to M2M Application

You will also need the Auth0 Management API for user CRUD operations. In "Auth0 Dashboard" -> "Applications" -> "APIs" -> "Auth0 Management API" -> "Machine To Machine Applications", first authorize your M2M application to request access tokens for the Management API. Then expand the M2M Application to also add the required permissions (scopes).