Authura is a very lightweight but comprehensive authentication service that supports JWT, email verification, and user management, designed to seamlessly integrate with Hasura while still giving you the flexibility to make it work for your data source through a backend system.
Built with Go, Authura empowers developers by providing a flexible authentication solution that maintains control over their application’s security.
DISCLAIMER: This project is still in early development and the API is not stable yet. Please be aware that the API is subject to change. Please feel free to contribute to the project by opening an issue or a pull request if you believe we could make it better with your findings so that we can reach to a stable version. Many tests are missing and not even planned until the both api and software design reach a solid foundation after having enough experimentation and usage in alpha stage.
- Why Choose Authura?
- Features
- Upcoming Features
- Quick Start
- Configuration
- Database Schema
- API Endpoints
- Security Considerations
- Production Checklist
While numerous authentication services exist, many are tailored to work with their own databases or schemas, often creating a disconnect between your application and the auth service. This can lead to a lack of control, which is even worse when using hosted solutions like Auth0 or Supabase, where critical components of your application are managed externally which does not always feel right depending on your use case.
Authura, by design choice, does not have a database of its own. It is designed to work with your existing database schema and backend system. Also, it tries to be as simple as possible and does not have a lot of features out of the box and be stateless as much as possible without the need to employ a lot of external services like Redis or many tables while keeping security and performance in mind.
Authura aims to restore this control to developers, offering a secure and user-friendly authentication service that integrates effortlessly with your existing infrastructure.
- JWT-based Authentication: Provides stateless authentication through access and refresh tokens, managed with separate signing keys.
- Email Verification: Implements email verification for password resets and changes, also managed statelessly with a dedicated signing key.
- User Invitations: Facilitates the invitation of users in closed environments.
- Self-Service Signup: Allows users to register independently, with the option to disable this feature if necessary.
- Third-Party Authentication: Supports user authentication via existing accounts from providers such as Google and GitHub.
- Multi-Tenant Support: Enables secure operation for multiple tenants within a single instance.
- Seamless Hasura Integration: Integrates effectively with Hasura, while also supporting Postgres and SQLite out of the box.
- Custom Backend Options: Allows for the integration of custom backend solutions as required.
- Flexible JWT Claims System: Offers the ability to extend the JWT claims system using environment variables or custom extensions.
- CORS Support: Essential for facilitating frontend integration.
- SMTP Email Service: Includes a dummy mode for development purposes.
- Rate Limiting: Implements basic rate limiting by IP, configurable for requests per minute, with additional options planned for future releases.
- User Impersonation: Permits administrators to act as users for support purposes.
- Captcha Integration: Enhances security by filtering out bots, with support for Turnstile available by default.
- Webhook Support: Enables integration with external services through webhooks.
- Email Templates: Allows for the customization of email templates for verification, reset password, change email, and invite.
docker build -t authura .
docker run -p 9000:9000 --env-file .env authura
go run cmd/main.go
The service is configured entirely through environment variables.
# Server Settings
HOST=0.0.0.0 # Server host (default: localhost)
PORT=9000 # Server port (default: 9000)
ALLOWED_ORIGINS=localhost:9000 # CORS allowed origins (comma-separated)
ALLOWED_HOSTS=localhost:9000 # CORS allowed hosts (comma-separated)
HOST_HEADER=Host # Host header name (default: Host)
RATE_PER_MINUTE=60 # Rate limiting per minute (default: 60)
# Various Configuration
ADMIN_ROLE=admin # Admin role name (default: admin)
DEFAULT_ROLE=user # Default role name (default: user)
VERIFY_REDIRECT_URL=http://localhost:9000/form # Verification redirect URL
EXTERNAL_URL=http://localhost:9000 # External URL for email links
SIGNUP_DISABLED=false # Disable self-service registration
IMPERSONATION_ENABLED=false # Enable user impersonation
# Captcha Configuration
CAPTCHA=turnstile # Optional: Captcha provider, when set, captcha will be enabled
TURNSTILE_SECRET_KEY=your-secret-key # Required when captcha is enabled
TURNSTILE_SITE_KEY=your-site-key # Required when captcha is enabled and /form endpoint is used
TURNSTILE_SCRIPT=https://challenges.cloudflare.com/turnstile/v0/api.js # Required when captcha is enabled and /form endpoint is used (default: https://challenges.cloudflare.com/turnstile/v0/api.js)
# Multi-tenant Configuration
MULTI_TENANT_ENABLED=true # Enable multi-tenant based on host header
# JWT Configuration
JWT_SECRET=your-secret-key # Required: JWT signing key
JWT_REFRESH_SECRET=refresh-key # Required: Refresh token signing key
JWT_VERIFY_SECRET=verify-key # Required: Email verification token key
JWT_EXPIRATION=5m # JWT token expiration (default: 5m)
JWT_REFRESH_EXPIRATION=720h # Refresh token expiration (default: 720h)
JWT_VERIFY_EXPIRATION=1h # Verification token expiration (default: 1h)
# JWT Extensions
JWT_EXTENSIONS=hasura,env # Optional: Comma-separated list of extensions
# JWT Env Extension Configuration (Required when using env extension)
JWT_ENV_EXTENSION_NAMESPACE=custom # Required when using env extension
JWT_ENV_EXTENSION_CLAIMS={"x-custom-claim-id": "{{.ID}}"} # Required when using env extension
# Backend Configuration
AUTH_BACKEND=hasura # Options: postgres, hasura, http, sqlite
# PostgreSQL Backend Settings
POSTGRES_DSN=postgresql://user:pass@localhost:5432/dbname
[email protected] # Optional: Initial admin user
POSTGRES_INITIAL_USER_PASSWORD=secure-password # Optional: Initial admin password
# Hasura Backend Settings
HASURA_URL=http://localhost:8080/v1/graphql
HASURA_GRAPHQL_ADMIN_SECRET=your-admin-secret
# SQLite Backend Settings
SQLITE_DB_PATH=./authura.db # Required when using sqlite backend
# HTTP Backend Settings
HTTP_URL=http://localhost:8080/users # Required when using http backend
HTTP_AUTH_HEADER_KEY=Authorization # Default: Authorization
HTTP_AUTH_HEADER_VALUE=Bearer your-access-token # Optional: If not provided, header will not be added to the request
# SMTP Configuration
MAIL_HOST=smtp.example.com # SMTP host (use 'dummy' for development)
MAIL_PORT=587 # Required when using a real SMTP server
[email protected] # Required when using a real SMTP server
MAIL_PASS=password # Required when using a real SMTP server
[email protected] # Required when using a real SMTP server
# Hook Configuration
HOOK_URL=http://localhost:8080/authura/hook # Required when using a webhook
HOOK_AUTH_HEADER_KEY=Authorization # Default: Authorization
HOOK_AUTH_HEADER_VALUE=Bearer your-access-token # Optional: If not provided, header will not be added to the request
# Provider Configuration
OAUTH_PROVIDERS=google,github # Optional: Comma-separated list of providers
# Google Provider Configuration
GOOGLE_CLIENT_ID=your-google-client-id # Required when using google provider
GOOGLE_CLIENT_SECRET=your-google-client-secret # Required when using google provider
# Github Provider Configuration
GITHUB_CLIENT_ID=your-github-client-id # Required when using github provider
GITHUB_CLIENT_SECRET=your-github-client-secret # Required when using github provider
Either you choose to use Hasura or Postgres, you need to create the following schema. You can always extend it to your needs but this is the minimum required schema. Please note that any extra columns you add should be nullable; otherwise, the service will not work.
CREATE TABLE "public"."users" (
"id" SERIAL NOT NULL PRIMARY KEY,
"email" VARCHAR NOT NULL,
"role" VARCHAR NOT NULL,
"password" VARCHAR NOT NULL,
"tenant_id" VARCHAR,
"verified_at" TIMESTAMP WITH TIME ZONE
);
CREATE UNIQUE INDEX ON "public"."users" ("email", "tenant_id");
Below are the API endpoints available for this service. Each endpoint is described in detail with the required input and expected output. You can either use json
or form-urlencoded
for the request body.
For the sake of simplicity, json
is used for demonstration purposes, but you can use form-urlencoded
if you prefer.
For endpoints that require authentication, you need to pass the Authorization
header with the value Bearer <access_token>
where <access_token>
is the access token you received from the /token
endpoint.
Also rate limiting is applied to all endpoints by default. You can configure the rate limiting by setting the RATE_PER_MINUTE
environment variable.
Accept: application/json
header should be provided to make sure the response is in JSON format.Content-Type: application/json
header should be provid if the request body is in JSON format. Otherwise, the request body will be ignored and the request will be considered as aform-urlenedcoded
request.X-Tenant-ID
header is only required when multi-tenancy is enabled (Can be configured usingTENANT_ID_HEADER
environment variable).Authorization
: The authorization header with the valueBearer <access_token>
where<access_token>
is the access token you received from the/token
endpoint. This is required for certain endpoints like/user
,/change-password
, etc. those are user management endpoints and are not available to unauthenticated users.
This is used to signup a new user. On success, an email will be sent to the user to verify their email address. You should configure a valid redirect URL by setting the VERIFY_REDIRECT_URL
environment variable or provide a valid redirect URL in the request body. If you don't provide a redirect URL, the user will be redirected to the default redirect URL which is http://localhost:9000/form
which is very useful for development purposes.
User will be redirected to the redirect URL after verifying their email address. The redirect URL will contain token
and action
query parameters which will be used to verify the email address.
Request parameters:
email
: The email address of the user.redirect_url
(optional): The URL to redirect the user to after signing up. If not provided, the user will be redirected to the default redirect URL which ishttp://localhost:9000/form
which is very useful for development purposes.
Response:
- Empty response.
Example:
$ curl -X POST http://localhost:9000/signup \
-H "X-Tenant-ID: tenant-id" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "redirect_url": "http://localhost:9000/form"}'
{}
This is not actually an endpoint. This is used to render a very simple form for the developer to instantly test the signup, verify email, change password, etc. flows for development purposes.
Request parameters (url query parameters):
action
: The action to perform. Can besignup
,verify-email
,change-password
, etc.token
: The token you received in the email.
Example:
% curl "http://localhost:9000/form?action=verify-email&token=a_valid_verification_token"
<form method="post" action="/verify-email">
<input name="token" value="a_valid_verification_token">
<input type="password" name="password" placeholder="Password">
<button type="submit">verify email</button>
</form>
This is used to verify the email address. token
is the token you received in the email. You can also pass password
to verify the email and create the user if they don't exist. In the response, you will receive both the user information and the tokens which can be used to authenticate the user.
Request parameters:
token
: The token you received in the email.password
(optional): The password to verify the email and create the user if they don't exist. Otherwise, the user will be created with a random password where the user should be forced to change it after login.
Response:
user
: The user object.id
: The user id.email
: The user email.role
: The user role.verified_at
: The timestamp when the user was verified.
tokens
: The tokens object.access_token
: The access token.refresh_token
: The refresh token.
Example:
$ curl -X POST http://localhost:9000/verify-email \
-H "X-Tenant-ID: tenant-id" \
-H "Content-Type: application/json" \
-d '{"token": "verification-token", "password": "a_valid_password"}'
{"user":{"id":1,"email":"[email protected]","role":"user","verified_at":"2025-02-05T17:48:18.223224+01:00"},"tokens":{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpvaG5kb2VAZXhhbXBsZS5jb20iLCJleHAiOjE3Mzg3NzQzOTgsImh0dHBzOi8vaGFzdXJhLmlvL2p3dC9jbGFpbXMiOnsieC1oYXN1cmEtYWxsb3dlZC1yb2xlcyI6WyJ1c2VyIl0sIngtaGFzdXJhLWRlZmF1bHQtcm9sZSI6InVzZXIiLCJ4LWhhc3VyYS10ZW5hbnQtaWQiOiJleGFtcGxlLmNvbSIsIngtaGFzdXJhLXVzZXItaWQiOiIyIn0sImlhdCI6MTczODc3NDA5OCwicm9sZSI6InVzZXIiLCJzdWIiOiIyIiwidGlkIjoiZXhhbXBsZS5jb20ifQ.Kgem_eZ6pRZQ_7KY-3AfeoplRjHHy1A66pAlVbzBs-Y","refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpvaG5kb2VAZXhhbXBsZS5jb20iLCJleHAiOjE3NDEzNjYwOTgsInRpZCI6ImV4YW1wbGUuY29tIn0.posjnEJEpPpJM1BIe2pXpmqWoh6TmmPSHLPsxuDJWbE"}}
This endpoint is used to generate access and refresh tokens for a user. User should provide their email and password to generate the tokens.
This endpoint also supports impersonation of a user by providing the password
parameter with an empty value but with an admin access token as in the Authorization
header. In this case, the user will be impersonated as the user specified in the email
parameter.
Request parameters:
email
: The email address of the user.password
(optional in case of impersonation): The password of the user.
$ curl -X POST http://localhost:9000/token \
-H "X-Tenant-ID: tenant-id" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]", "password": "password"}'
Tokens should be refreshed before they expire. You can use the /refresh
endpoint to refresh the tokens.
Request parameters:
refresh_token
: The refresh token.
Response:
access_token
: The access token.refresh_token
: The refresh token.
Example:
curl -X POST http://localhost:9000/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "refresh-token"}'
This endpoint is used to get the current user information.
Request parameters:
- None
Response:
user
: The user object.id
: The user id.email
: The user email.role
: The user role.verified_at
: The timestamp when the user was verified.
Example:
$ curl -X GET http://localhost:9000/user \
-H "Authorization: Bearer access-token"
{"user":{"id":1,"email":"[email protected]","role":"user","verified_at":"2025-02-05T17:48:18.223224+01:00"}}
This endpoint is used to change the user password.
Request parameters:
old_password
: The old password of the user.new_password
: The new password of the user.
Response:
- Empty response.
Example:
$ curl -X POST http://localhost:9000/change-password \
-H "Authorization: Bearer access-token" \
-d '{"old_password": "old-password", "new_password": "new-password"}'
{}
This endpoint is used to change the user email address. User will be sent an email with a link to verify the new email address which has the same flow follows the /verify-email
endpoint.
Request parameters:
email
: The new email address of the user.
Response:
- Empty response.
Example:
$ curl -X POST http://localhost:9000/change-email \
-H "Authorization: Bearer access-token" \
-d '{"email": "[email protected]"}'
{}
This endpoint is used to invite a new user to the system. Only users with the admin
role can invite new users. An email will be sent to the user to verify their email address which also has the same flow follows the /verify-email
endpoint.
Request parameters:
email
: The email address of the user to invite.role
: The role of the user to invite.
Response:
- Empty response.
Example:
$ curl -X POST http://localhost:9000/invite \
-H "Authorization: Bearer access-token" \
-d '{"email": "[email protected]", "role": "user"}'
{}
This endpoint is used to reset the user password. User will be sent an email with a link to reset their password which has the same flow follows the /verify-email
endpoint.
Request parameters:
email
: The email address of the user to reset the password.
Response:
- Empty response.
Example:
$ curl -X POST http://localhost:9000/reset-password \
-H "Authorization: Bearer access-token" \
-d '{"email": "[email protected]"}'
{}
Hooks facilitate integration with external services via webhooks. You can configure the hook URL using the HOOK_URL
environment variable. Unless you set the HOOK_URL
environment variable, hooks will not be enabled.
HOOK_URL
: The endpoint for the hook.HOOK_AUTH_HEADER_KEY
: The key for the authorization header. Default isAuthorization
.HOOK_AUTH_HEADER_VALUE
: The value for the authorization header. This is optional and if not provided, the header will not be added to the request.
-
get_user
: Triggered after a user object is retrieved from the backend. If the hook returns a non-success status code, the request will fail, and the response body will contain the hook's response.- Request Body: The user object.
- Response Body: A
hook_data
object.
-
create_or_update_user
: Invoked before a user is created or updated in the backend. If the hook returns a non-success status code, the user creation or update will be aborted, and the response body will reflect the hook's response.- Request Body: The user object.
- Response Body: A
hook_data
object.
Hooks are invoked with the user object received from the backend in the request body as a JSON object. The specific type of hook is determined by the action
parameter in the query string.
The response will be a JSON object containing arbitrary key-value pairs, which will be included under the hook_data
key in the user object. This hook_data
will also be part of the JWT payload, allowing for integration with other services as needed.
$ curl "http://localhost:9000/authura/hook?action=get_user" \
-H "Authorization: Bearer access-token" \
-d '{"id": 1, "email": "[email protected]", "role": "user", "tenant_id": "example.com", "verified_at": "2025-02-05T17:48:18.223224+01:00"}'
{"app_specific_data": "some_data"}
This is how the JWT payload will appear:
{
"iat": 1738774398,
"exp": 1738774698,
"sub": "1",
"email": "[email protected]",
"role": "user",
"tenant_id": "example.com",
"verified_at": "2025-02-05T17:48:18.223224+01:00",
... other claims,
"hook_data": {"app_specific_data": "some_data"}
}
- Minimum 8 characters
- At least one uppercase letter
- At least one lowercase letter
- At least one number
- At least one special character
You can use the JWT_EXTENSIONS
environment variable to choose the JWT extension you want to use. The default is no extension. You can use as many extensions as you want as long as they implement the JwtExtension
interface. Currently, there are two extensions available: hasura
and env
. Please use a comma to separate multiple extensions like below:
JWT_EXTENSIONS=hasura,env
This extension will produce the claims under the https://hasura.io/jwt/claims
key, which is the required format for Hasura.
{
"https://hasura.io/jwt/claims": {
"x-hasura-default-role": "user",
"x-hasura-allowed-roles": ["user"],
"x-hasura-user-id": "1"
}
}
This extension will help you to add your own custom claims to the JWT token. You can use the JWT_ENV_EXTENSION_NAMESPACE
and JWT_ENV_EXTENSION_CLAIMS
environment variables to configure this extension.
Example:
JWT_ENV_EXTENSION_NAMESPACE=custom
JWT_ENV_EXTENSION_CLAIMS={"x-custom-claim-id": "{{.ID}}"}
This will produce the claims under the custom
key.
{
... other claims,
"custom": {
"x-custom-claim-id": "1"
}
}
- Email Verification: Required by default for security.
- JWT Tokens: Stateless by design; revocation requires stateful backend implementation which is not a case for now.
- SMTP Configuration:
- Use 'dummy' mode for development (logs to console).
- Configure real SMTP for production.
- Security:
- Always use HTTPS in production.
- Keep JWT secrets secure and unique.
- Regularly rotate secrets in production.
- Signup:
- Signup is enabled by default, so consider disabling it in case you have no such need.
- You can disable it by setting
SIGNUP_DISABLED=true
.
- Multi-tenancy:
- Multi-tenancy is disabled by default, so consider enabling it in case you have no such need.
- You can enable it by setting
MULTI_TENANT_ENABLED=true
.
- Configure secure SMTP server
- Set strong, unique JWT secrets
- Enable HTTPS
- Configure appropriate CORS settings
- Set up proper logging
- Configure verification redirect URL
- Review and set appropriate token expirations