ErtisAuth

Getting Started

Install, configure and set up ErtisAuth, and get your first token.

This guide takes you from an empty machine to a running ErtisAuth instance with a membership, an administrator and a first token. It takes about ten minutes.

1. Requirements#

  • .NET 10 SDK (to build from source), or Docker
  • MongoDB 7.0 or later. A standalone server is enough; a replica set is not required.

2. Run ErtisAuth#

From source#

shell
git clone https://github.com/ertugrulozcan/ErtisAuth.git
cd ErtisAuth
export Database__ConnectionString="mongodb://localhost:27017"
dotnet run --project src/ErtisAuth.WebAPI

In the development environment the API listens on http://localhost:9716, and the interactive API reference is at http://localhost:9716/docs.

With Docker Compose#

The repository contains a docker-compose.yml that starts ErtisAuth together with a MongoDB:

shell
git clone https://github.com/ertugrulozcan/ErtisAuth.git
cd ErtisAuth
docker compose up -d --build

The API listens on http://localhost:9716 and the API reference is at http://localhost:9716/docs. The MongoDB data is kept in the mongo-data volume.

With Docker#

Build the image and run it with a MongoDB of your own:

shell
docker build -t ertisauth:latest .
docker run -p 9716:8080 \
	-e Database__ConnectionString="mongodb://<host>:27017" \
	ertisauth:latest

The container listens on port 8080 and runs as the non-root app user of the .NET images. It is based on the Debian image of ASP.NET Core, which includes ICU and the time zone database, so culture and time zone handling work as on a development machine.

ErtisAuth stores its data in the database named by Database:DefaultAuthDatabase (auth by default). See Configuration for all settings.

On startup ErtisAuth creates the indexes it needs. Check that it is up:

shell
curl http://localhost:9716/healthcheck
json
{
	"status": "Unhealthy",
	"message": "ErtisAuth has not been set up yet"
}

Unhealthy with this message is expected at this point: the server is running, but it has no membership yet.

3. Set up the installation#

A fresh installation has no users, so nobody can sign in to create the first ones. The setup endpoint solves this: it creates the first resources in one call, and it is authorized by a token that you put into the database yourself. Having write access to the database proves that you are the operator of the installation.

3.1 Insert a setup token#

Generate a random token of at least 32 characters:

shell
openssl rand -hex 32

Insert it into the setup collection of the ErtisAuth database (with mongosh):

javascript
use auth
db.setup.insertOne({ token: "<setup_token>" })

With Docker Compose, run it in the MongoDB container:

shell
docker compose exec mongo mongosh auth --eval 'db.setup.insertOne({ token: "<setup_token>" })'

3.2 Call the setup endpoint#

shell
curl -X POST http://localhost:9716/setup \
	-H 'X-Setup-Token: <setup_token>' \
	-H 'Content-Type: application/json' \
	-d '{
		"membership": {
			"name": "My Company",
			"slug": "my-company",
			"expires_in": 3600,
			"refresh_token_expires_in": 86400,
			"hash_algorithm": "ARGON2ID",
			"encoding": "UTF-8"
		},
		"user": {
			"username": "admin",
			"firstname": "Ada",
			"lastname": "Lovelace",
			"email_address": "admin@example.com",
			"password": "<a strong password>",
			"user_type": "Employee"
		},
		"application": {
			"name": "Backend",
			"role": "admin"
		}
	}'
FieldRequiredDescription
membership.nameyesDisplay name of the membership.
membership.slugnoURL-friendly name; derived from the name when omitted.
membership.expires_inyesAccess token lifetime in seconds.
membership.refresh_token_expires_inyesRefresh token lifetime in seconds.
membership.hash_algorithmyesPassword hash algorithm. ARGON2ID is recommended (see Memberships).
membership.encodingnoText encoding used for hashing and signing (UTF-8 by default).
membership.secret_keynoThe key tokens are signed with, at least 32 bytes. A random key is generated when omitted.
user.*yesThe administrator user. firstname, username, email_address and password (at least 6 characters) are required.
user.user_typenoName of the user type created for the administrator (User by default).
applicationnoAn application for machine-to-machine access, typically with the admin role.

The setup creates, in this order:

  1. the membership,
  2. the admin role, which has every permission on every ErtisAuth resource,
  3. a user type inheriting from the built-in base-user type,
  4. the administrator user, already active, with the admin role,
  5. the application, if requested.

If any step fails, the resources created before it are removed, so a failed setup can simply be retried.

3.3 Keep the response#

json
{
	"membership": {
		"_id": "66f1c0d2a4b5c6d7e8f90123",
		"name": "My Company",
		"slug": "my-company",
		"expires_in": 3600,
		"refresh_token_expires_in": 86400,
		"secret_key": "…",
		"hash_algorithm": "ARGON2ID",
		"encoding": "UTF-8",
		"…": "…"
	},
	"user": { "_id": "66f1c0d2a4b5c6d7e8f90124", "username": "admin", "role": "admin", "…": "…" },
	"role": { "_id": "66f1c0d2a4b5c6d7e8f90125", "name": "Administrator", "slug": "admin", "permissions": [ "*.memberships.create.*", "…" ] },
	"application": {
		"_id": "66f1c0d2a4b5c6d7e8f90126",
		"name": "Backend",
		"slug": "backend",
		"role": "admin",
		"secret": "<application_secret>"
	}
}

Write down:

  • membership._id: you will send it with every sign-in request.
  • application.secret: it is returned only once. ErtisAuth stores only its hash; if you lose it, rotate it.

When the setup succeeds, the setup collection is dropped and the endpoint is closed for good: further calls answer 409 AlreadySetUp. The health check now answers Healthy.

4. Sign in#

shell
curl -X POST http://localhost:9716/generate-token \
	-H 'X-Ertis-Alias: <membership_id>' \
	-H 'Content-Type: application/json' \
	-d '{ "username": "admin", "password": "<password>" }'
json
{
	"token_type": "Bearer",
	"access_token": "eyJhbGciOiJIUzI1NiIs…",
	"expires_in": 3600,
	"refresh_token": "eyJhbGciOiJIUzI1NiIs…",
	"refresh_token_expires_in": 86400,
	"created_at": "2026-01-01T12:00:00Z"
}

username also accepts the email address.

5. Call the API#

shell
curl http://localhost:9716/me -H 'Authorization: Bearer <access_token>'
shell
curl 'http://localhost:9716/memberships/<membership_id>/users?limit=10&with_count=true' \
	-H 'Authorization: Bearer <access_token>'

The application can call the same endpoints with a Basic token made of its id and secret:

shell
curl 'http://localhost:9716/memberships/<membership_id>/users' \
	-H 'Authorization: Basic <application_id>:<application_secret>'

Next steps#

Found a mistake in the docs? Open an issue