Skip to content

Authentication

Adam Bachman edited this page Nov 12, 2025 · 1 revision

The initial sandbox / beta-testing launch of NPD uses Django's default authentication flow in "basic" configuration for API and data-browsing app access.

This documentation covers developer-oriented management of authentication in the local contexts, not the actual NPD APIs in any deployed context.

Requiring authentication

By default, the locally built application does not require authentication.

Add this environment variable to your local .env:

NPD_REQUIRE_AUTHENTICATION=True

Or a compose.override.yml file in the root of the project:

services:
  django-web:
    environment:
      NPD_REQUIRE_AUTHENTICATION: True

Creating the first superuser

From the backend/ directory, use make createsuperuser or python manage.py createsuperuser to create a new superuser.

The process can be automated by setting the following environment variables first:

  • DJANGO_SUPERUSER_EMAIL
  • DJANGO_SUPERUSER_USERNAME
  • DJANGO_SUPERUSER_PASSWORD
# via dialogs
make createsuperuser

# automated
make createsuperuser \
  DJANGO_SUPERUSER_EMAIL=admin@cms.hhs.gov \
  DJANGO_SUPERUSER_USERNAME=npdadmin \
  DJANGO_SUPERUSER_PASSWORD="password123+"

# with docker compose
docker compose run --rm \
    -e DJANGO_SUPERUSER_EMAIL="npd@cms.hhs.gov" \
	-e DJANGO_SUPERUSER_USERNAME="npd@cms.hhs.gov" \
	-e DJANGO_SUPERUSER_PASSWORD="password123+" \
	--rm django-web python manage.py createsuperuser --no-input

# directly from host in `backend/`
DJANGO_SUPERUSER_EMAIL="npd@cms.hhs.gov" \
  DJANGO_SUPERUSER_USERNAME="npdadmin" \
  DJANGO_SUPERUSER_PASSWORD="password123+" \
  python manage.py createsuperuser --no-input

Bootstrapping a superuser with flyway

There is already a migration in the flyway/ sub-directory which will create a superuser if a PBKDF2 hashed password is provided.

You can generate a valid PBDKF2 hash with the infrastructure/modules/fhir-api/generate_hash.py script:

echo '{"password_input":"password123+"}' | python infrastructure/modules/fhir-api/generate_hash.py
# {"hashed_password": "pbkdf2_sha256$1000000$23d3b8a387f187b8466b9c11cef344b8$PcIzqWBVMzZDUqZCrTsduIAWsvMa4O/acSupTPCo9hI="}

After the next time you reset your development database, you can pass the hashed_password value to the flyway migrate command locally with docker compose to get a new user with the username npd+deploy@cms.hhs.gov and password password123+ in your local environment.

That process looks like running a docker compose command with the FLYWAY_PLACEHOLDERS_superuserDefaultPassword env var set.

docker compose run --rm \
	-e FLYWAY_PLACEHOLDERS_superuserDefaultPassword='pbkdf2_sha256$1000000$23d3b8a387f187b8466b9c11cef344b8$PcIzqWBVMzZDUqZCrTsduIAWsvMa4O/acSupTPCo9hI=' \
	db-migrations migrate

Tip

The PBKDF2 formatted password includes $ symbols, and so MUST be single quoted in order to execute successfully from a shell command. If you're setting the env var value in a docker compose file, make sure to use double dollar signs to indicate a literal dollar sign in the output.

On subsequent runs of the migrate command, the default value of FLYWAY_PLACEHOLDERS_superuserDefaultPassword in backend/docker-compose.yml is set to "" (empty string) and so the repeatable migration will run, but the password value of the superuser account will not change since an invalid value was given.

Visit the admin site

http://localhost:8000/admin/login/

Login using the USERNAME and PASSWORD you just created.

Visit any other part of the application

All requests should redirect to http://localhost:8000/accounts/login/ if you are not logged in.

Creating more users user

Use the Django admin site to create a user: http://localhost:8000/admin/auth/user/

Or open a Django shell and creating a user with Django models:

$ docker compose run --rm -it django-web python manage.py shell

Python 3.13.9 (main, Oct 21 2025, 02:15:13) [GCC 14.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> user = User.objects.create(username="testuser")
>>> user.set_password("123+password")
>>> exit()

It's helpful to keep a record of the users that you create locally. I keep mine in a file named scratch/users.yml so that I can use it later on in scripts and it will be ignored by git.

admin:
	EMAIL: npd.admin@cms.hhs.gov
	USERNAME: npd.admin@cms.hhs.gov
	PASSWORD: "cheque46,baric"
user:
	EMAIL: npd.user@cms.hhs.gov
	USERNAME: npd.user@cms.hhs.gov
	PASSWORD: "ternal26+aimara9"

Authenticate with /fhir/ API

Use HTTP Basic Auth to authenticate with the API. Here's a script that'll spit out a curl command with the appropriate header:

#!/usr/bin/env python

# save as "encode-basic" and chmod +x
# usage: encode-basic USERNAME PASSWORD

import base64
import sys

username = sys.argv[1]
password = sys.argv[2]
payload_bytes = f"{username}:{password}".encode("utf-8")
payload = base64.b64encode(payload_bytes).decode('utf-8')

print(f"encoding: {payload_bytes}")
print(f'curl -H "Authorization: Basic {payload}" ')

Calling that script for me looks like:

$ encode-basic npd@cms.hhs.gov "password123+"
encoding: b'npd@cms.hhs.gov:password123+'
curl -H "Authorization: Basic bnBkQGNtcy5oaHMuZ292OnBhc3N3b3JkMTIzKw=="

I can now use that header with curl or an API client that accepts a base64 encoded Authorization: Basic value.

Clone this wiki locally