Authen
Authen is a web service used to enhance an existing authentication system. It is a Go executable that exposes an HTTP interface and supports 3 storage backends: SQLite, PostgreSQL and CockroachDB.
It is open source, easy to install, reliable, secure and performant. When the PostgreSQL or CockroachDB storage options are used, multiple instances of Authen can be run to achieve high available and horizontal scaling.
Running
Authen is meant to be both easy to run and easy to compile.
You can grab the latest pre-compiled binary, create a simple config.json and run it.
To compile the project, you'll need a recent version of Go (1.18+). Download the source and run:
wget https://github.com/goblgobl/authen/archive/refs/heads/master.tar.gz
tar -xzvf master.tar.gz
cd authen-master
make build
To generate an executable named authen
.
Authentication
Authen is designed to be called exclusively from a controlled system (i.e. a backend system). It should not be exposed publicly. As such, it does not authenticate requests.
Authen does support multi-tenancy though. See the multi-tenancy section.
Configuration
By default, the configuration will be loaded from the config.json
file. This can be changed by specifying the -config PATH
argument on startup.
- basic
- advance
{
"storage": {
"type": "sqlite",
"path": "/opt/authen/data.sqlite"
},
"totp": {
"issuer": "yourcompany.com"
}
}
{
"instance_id": 0,
"migrations": true,
"db_clean_frequency": 120,
"project_update_frequency": 120,
"log": {
"level": "info",
"requests": true,
"pool_size": 100,
"format": "kv",
"kv": {
"max_size": 4096
}
},
"http": {
"listen": "127.0.0.1:5200"
},
"storage": {
"type": "postgres",
"url": "postgres://localhost:5432/gobl_authen"
},
"totp": {
"max": 0,
"setup_ttl": 300,
"secret_length": 16,
"issuer": "yourcompany.com"
},
"ticket": {
"max": 0,
"max_payload_length": 0
},
"login_log": {
"max": 0,
"max_payload_length": 0
}
}
Common Configuration Settings
log.level
The log level to use. Can one one of INFO
, WARN
, ERROR
, FATAL
or NONE
. Defaults to INFO
.
log.requests
HTTP requests are logged regardless of the configured log.level
. Set this value to false
if you do not want HTTP requests to be logged. Defaults to true
, which will log HTTP requests.
http.listen
The address:port
that the HTTP should listen on. Please note that Authen is not designed to be exposed publicly. The specified address should not be publicly reachable. Defaults to 127.0.0.1:5200
.
totp.issuer
The issuer to include in the generated otpauth URL (which is part of the data encoded in the QR code). This helps prevent collisions when the user has multiple TOTP for different services. This will generally be the name of your company or website.
storage.type
Must be either sqlite
, postgres
or cockroach
.
This setting is required and there is no default.
storage.sqlite.path
When storage.type
is set to sqlite
, this must be set to the location of the SQLite database. If the file does not exist, it will be created.
storage.postgres.url
When storage.type
is set to postgres
, this must be the postgres connection string.
storage.cockroach.url
When storage.type
is set to cockroach
, this must be the cockroach connection string. Note that cockroach DB uses the same connection string format as PostgreSQL, including the postgres://
protocol. Typically, only the port is different.
Advanced Configuration Settings
instance_id
This value should only be set when multiple instances of Authen are deployed. It defaults to 0
, which is fine when a single instance is used.
Every request is assigned a RequestId
, which is included in any logs generated with respect to the request, as well as placed in the RequestId
header of the response. While the RequestId
isn't guaranteed to be unique, in deployments using multiple instances, giving each instance a unique id (from 0-255) will greatly reduce duplicates.
multi_tenancy
See the multi-tenancy section for more details. In short, when true
, each tenant (called a project) is loaded from the database based on the Project
request header. Some of the configuration settings listed here are ignored in favor of project-specific configuration (stored along the project in the database).
project_update_frequency
Frequency, in seconds, to scan for changed project configuration. This value should only be set when multi-tenancy is enabled. It defaults to 0, which disables the scan, which is correct in single-tenancy as there is no project data.
A reasonable value for this, when using multi-tenancy, would be 60. This would mean that changes made to existing projects would take up to 60 seconds before being registered.
db_clean_frequency
Frequency, in seconds, to "clean" the database. Exactly what happens when the database is "cleaned" is meant to be an implementation detail. The behavior can change in the future. It currently means:
- Deleting expired TOTPs
- Deleting expired tickets
- Deleting tickets with no more uses
The default value is 120
seconds. This value should not be set to 0
, which disables the cleaner, except for advance cases. When multiple instances are used (HA), it is reasonable to disable the cleaner on all but one instance. Alternatively, in most cases, letting the cleaner run on each instance is also fine (the cleaner start with a random delay, so each instance will likely run at different times).
migrations
By default, Authen will automatically run data migrations on startup (these are baked into the binary). The only valid reason to set this to false
is when multiple Authen servers are deployed. See the migrations section for more details.
log.pool_size
Authen pre-allocates a pool of loggers which helps reduce the amount of memory that is created and which must be garbage collected during runtime. The amount of pre-allocated memory depends on the pool_size and the configured maximum log size.
For best performance, at the cost of memory, this should be set to the maximum number of concurrent requests the system will handle. It defaults to 100
.
The log pool will not grow or shrink and is non-blocking. If more loggers are requested than the pool can handle, loggers will be dynamically allocated, but will not be added back to the pool.
log.format
The format of the generated log messages. Currently, the only supported value, and the default, is kv
for a key=value type log output.
log.kv.max_size
The maximum size of an individual log message. Any additional data will be discarded. Defaults to 4096
. With a default pool_size of 100
, the total memory pre-allocated for logging is 100 * 4096
bytes (0.4096 megabytes).
totp.max
The maximum number of TOTP entries to store. The default, 0
allows for an unlimited number. For self-hosted setups where you have total control over the backend which calls Authen, there's generally no reason to set this.
In multi-tenancy mode, this value is ignored and the project-specific configuration is used.
totp.setup_ttl
A user's TOTP is configured, or changed, in two phases. First the user is presented with a QR code and then their TOTP is validated. Having the user validate the code ensures that we don't enable a TOTP secret that the user cannot verify. This setting defines the time, in second, that the user has for entering their verification from the time the QR code is generated. Defaults to 300
(5 minutes).
In multi-tenancy mode, this value is ignored and the project-specific configuration is used.
totp.secret_length
The length of the TOTP secret to generate. Defaults to 16
.
In multi-tenancy mode, this value is ignored and the project-specific configuration is used.
ticket.max
The maximum number of tickets that can exist at once. The default, 0
allows for an unlimited number. For self-hosted setups where you have total control over the backend which calls Authen, there's generally no reason to set this.
In multi-tenancy mode, this value is ignored and the project-specific configuration is used.
ticket.max_payload_length
The maximum length, in bytes, of each ticket payload. The default, 0
allows for an unlimited payload length. For self-hosted setups where you have total control over the backend which calls Authen, there's generally no reason to set this.
In multi-tenancy mode, this value is ignored and the project-specific configuration is used.
login_log.max
The maximum number of login_logs allowed to be created. The default, 0
allows for an unlimited number. For self-hosted setups where you have total control over the backend which calls Authen, there's generally no reason to set this.
In multi-tenancy mode, this value is ignored and the project-specific configuration is used.
login_log.max_payload_length
The maximum length, in bytes, of each login log payload. The default, 0
allows for an unlimited payload length. For self-hosted setups where you have total control over the backend which calls Authen, there's generally no reason to set this.
In multi-tenancy mode, this value is ignored and the project-specific configuration is used.
Errors and Codes
Authen tries to provide developer-friendly error and validation messages. Every error response has an integer code
field which identifies the error. Every error response also has a string error
field which is a description of the error, in English. While basic and aimed at guiding developers, the error
field will never contain sensitive data and can be shown to end-users (although, again, it's rather basic, might be a little technical, and is always in English).
For example, a request to an invalid route would return a response with a 404 status code, as well as body with a code
and error
field:
$ curl http://127.0.0.1:5200/invalid
{
"code": 102001,
"error": "not found"
}
Error Codes
code | desc | |
---|---|---|
2001 | A generic internal server error. This is the least specific and thus least useful error. The response will have an uuid | |
2002 | A response could not be serialized to JSON. Like the 2001 error, please see the | |
2003 | The request payload was not valid JSON. | |
2004 | The request contained invalid data. See the validation section for details on validation errors. | |
102001 | An http 404 that secifically relates to the URL path being unknown (as opposed to, say, an endpoint returning a 404 because some ID wasn't valid). | |
102002 |
| |
102003 | The id specified by the | |
102005 | The project has reached the maximum configured TOTP entries. | |
102006 | The TOTP entry could not be found. This means the | |
102007 | The | |
102008 | The | |
102009 | The project has reached the maximum configured tickets. | |
102010 | The | |
102011 | The | |
102012 | The project has reached the maximum configured login logs. | |
102013 | The |
Validation Errors
A validation error is a normal error with a code of 2004
. Validation errors always contain an invalid
field which contains an error of errors. Each item within this array contains its own code
and error
field and, optionally, a field
and data
field.
$ curl -X POST "http://127.0.0.1:5200/v1/totp" \
-H "Content-Type: application/json" \
-d '{"key": "goku", "account": 9001}'
{
"code": 2004,
"error": "invalid data",
"invalid": [
{
"code": 1003,
"field": "key",
"error": "must be between 64 and 64 characters",
"data": {"min": 64, "max": 64}
},
{
"code": 101001,
"field": "key",
"error": "key must be a 32-byte hex encoded value"
},
{
"code": 1001,
"field": "user_id",
"error": "required"
},
{
"code": 1002,
"field": "account"
"error": "must be a string",
}
]}
The error
field contains a user-friendly error message in English. The presence and contents of the optional data
field depends on the code
. We can see that code
1003
, which represents a string length validation error, contains a data field with the min
and max
length.
Multi-Tenancy
Authen supports multi-tenancy largely by way of project_id
uuid columns in the database. When multi-tenancy is enabled, the project is identified via the Project
header.
To enable multi-tenancy, the multi_tenancy
configuration parameter must be set to true.
Even with multi-tenancy enabled, Authen does not do authentication. The Project
header parameter becomes required, and an invalid or unknown id will result in an error, but it is up to the caller to make sure that the correct id is passed.
Project Management
There is currently no API to manage projects. However, projects can be added, removed or deleted by modifying the authen_projects
table of the configured database.
While the exact structure of the table will depend on the configured storage type, it is a simple schema. For PostgreSQL, the table is defined as:
create table authen_projects (
id uuid not null primary key,
totp_max int not null,
totp_issuer text not null,
totp_setup_ttl int not null,
totp_secret_length int not null,
created timestamptz not null default now(),
updated timestamptz not null default now()
);
We can see from the above schema that, where it makes sense, multi-tenancy uses per-project configuration values. Specifically, when multi-tenancy is enabled, the global totp.*
configuration is ignored (you'll get a warning if the totp
section is defined when multi_tenancy == true
).
Project Update
Internally, Authen has a non-expiring id => Project cache. While new projects will be detected, changes to existing projects will not. Setting the project_update_frequency
value will cause Authen to periodically look for updated projects based on the updated
column of the authen_projects.
table. A value of 60 (seconds) is reasonable.
We hope to provide better way to manage projects in the future.
Data Migrations
When you run a new version of Authen, it will automatically apply any necessary data/schema migrations to the configured store.
When multiple Authen services are deployed, this behavior might not be desirable. There are two options. The first is to set the migrations
configuration option to false for all but one server. Ideally, the one server that will run migrations is launched first.
The other option is to run the authen
binary with the special -migrate
flag. This will load the same config.json
(or whatever -config PATH
you specify), run any necessary migrations, and exit.