Configuration
A key concept of SympAuthy is to allow you to deploy a fully configured instance of it without requiring you to connect to an interface. Therefore, all the configurations of SympAuthy must be text-based and deployed alongside it.
For this matter, SympAuthy relies on the configuration mechanism of Micronaut.
Everything is configurable through the use of:
- YAML or JSON configuration files.
- environment variables.
- parameters passed to the binary.
Micronaut Environments
SympAuthy uses Micronaut Environments to allow you to use well-crafted configurations out-of-the-box.
default: Configure every configuration keys listed below with the value indicated in the Default column of the table. It does not enable any feature that is considered unsecure.by-mail: Allow the end-user to sign in/sign-up using a login/password with the login being an email address.- Well-known providers: Pre-built configurations for common third-party providers (Google, Discord, etc.). Activating one by its environment name automatically configures the OAuth2 URLs, scopes, and claim mappings for that provider.
The micronaut environments you are using can be set using the MICRONAUT_ENVIRONMENTS environment variable. ex MICRONAUT_ENVIRONMENTS=default,by-mail,google
Configuration keys
This section describes all the configuration key allowing to configure your instance of SympAuthy.
The configuration key in the sections and in the tables are sorted by alphanumerical order.
Micronaut
Since SympAuthy is constructed using the Micronaut framework, it shares the configuration keys of all Micronaut-based applications to configure some basic features. The table below provides examples of commonly used keys:
| Key | Type | Description | Required Default |
|---|---|---|---|
micronaut.server.port | int | TCP port the server will be listening to. | NO8080 |
The full list is available in the Micronaut documentation.
r2dbc
This authorization server accesses its database through Micronaut R2DBC.
The database must exist before starting SympAuthy. Only the schema (tables, indexes, etc.) is created automatically on first startup.
r2dbc.datasources.default
| Key | Type | Description | Required Default |
|---|---|---|---|
url | string | R2DBC connection URL to the database. | YES |
username | string | Username used to authenticate to the database. | YES |
password | string | Password used to authenticate to the database. | NO |
Supported databases
PostgreSQL
r2dbc:
datasources:
default:
url: r2dbc:postgresql://<host>:<port>/<database>
username: <username>
password: <password>H2 (in-memory, for development only)
r2dbc:
datasources:
default:
url: r2dbc:h2:mem:///sympauthyjavamail
This authorization server can email a user through the use of Micronaut Email.
The SMTP client implementation was chosen because it can be easily integrated with the most commonly used mailing solutions on the market:
| Key | Type | Description | Required Default |
|---|---|---|---|
enabled | boolean | Set to true to enable sending emails. If mails are enable but the configuration is missing, it will fail to start with the following message: JavaMail configuration does not contain any properties. | NO |
authentication | object | Contains the username and the password to authenticate to the SMTP server. | NO |
properties | object | Configuration of the SMTP library using its properties. | YES |
Example:
javamail:
enabled: true
authentication:
username: username
password: password
properties:
mail:
from: noreply@example.com
smtp:
host: ssl.smtp.example.com
port: 465
ssl:
enable: truejavamail.authentication
| Key | Type | Description | Required Default |
|---|---|---|---|
<id> | string | Uniq identifier of the client. | YES |
secret | string | A secret shared between the client and the authorization server. | YES |
allowed-grant-types | array of string | List of OAuth2 grant types this client is allowed to use. Supported values: authorization_code, refresh_token, client_credentials. | YES |
allowed-redirect-uris | array of string | A list of URIs where the client is allowed to ask the redirection of the end-user at the end of the OAuth2 authorize grant flow. Required when authorization_code is in allowed-grant-types. | Conditional |
advanced
This section holds configuration that will change the general behavior of the server.
| Key | Type | Description | Required Default |
|---|---|---|---|
jwt | object | YES | |
user-merging-strategy | string | Deprecated — replaced by auth.user-merging-enabled. | YESby-mail |
keys-generation-strategy | string | YESautoincrement | |
validation-code | object | See advanced.validation-code. | YES |
advanced.hash
| Key | Type | Description | Required Default |
|---|---|---|---|
block-size | int | YES8 | |
cost-parameter | int | YES16384 | |
key-length | int | Number of bytes generated as output of the hashing algorithm. | YES32 |
parallelization-parameter | int | YES1 | |
salt-length | int | Number of random bytes to generate and then use as a salt for the hashing algorithm. | YES256 |
advanced.jwt
| Key | Type | Description | Required Default |
|---|---|---|---|
access-alg | string | Algorithm used to sign access tokens. The algorithm MUST be asymmetric and support a public key. Access tokens are signed with a dedicated key, separate from ID tokens per RFC 9068. | YESrs256 |
public-alg | string | Algorithm used to sign ID tokens and other keys shared publicly. The algorithm MUST be asymmetric and support a public key. | YESrs256 |
private-alg | string | Algorithm used to encrypt internal keys. The algorithm only have to support public key. | YESrs256 |
advanced.validation-code
| Key | Type | Description | Required Default |
|---|---|---|---|
expiration | duration | Duration, after the validation code has been generated, where the server will accept it. | YES10m |
length | int | Number of digit expected in validation code generate by this authorization server. | YES6 |
resend-delay | duration | Duration the end-user has to wait before being able to request a new validation code to be sent. | YES1m |
auth
| Key | Type | Description | Required Default |
|---|---|---|---|
issuer | string | The public URL of this authorization server, embedded as the iss claim in every JWT token it issues. Clients use it to verify that a token was issued by the expected server and to discover the OpenID Connect configuration at <issuer>/.well-known/openid-configuration. | YES<urls.root> |
audience | string | Audience of the JWT tokens issued for end-user authentication (ex. OAuth2 access and refresh tokens). Required by RFC 9068 in access tokens. | NO<urls.root> |
identifier-claims | array of string | List of claim identifiers that uniquely identify a person. These claims are used as the login identifier for password authentication and, when user-merging-enabled is true, as the key for merging accounts across authentication methods. | NO[] |
user-merging-enabled | boolean | When true, accounts that share the same value for any configured identifier-claims are automatically merged across authentication methods (password, third-party providers). When false, each authentication method creates a separate account. | NOfalse |
authorization-code | object | Configuration for the authorization code grant flow. See auth.authorization-code for more details. | NO |
token | object | Configuration related to authentication tokens issued by this app. See auth.token for more details. | NO |
auth.authorization-code
| Key | Type | Description | Required Default |
|---|---|---|---|
expiration | duration | Maximum duration an authorization attempt is valid before it expires. The end-user must complete the entire authorization flow (sign-in, claims collection, MFA, etc.) within this time. | YES30m |
auth.by-password
| Key | Type | Description | Required Default |
|---|---|---|---|
enabled | boolean | Enable password-based authentication. | NOfalse |
auth.token
| Key | Type | Description | Required Default |
|---|---|---|---|
access-expiration | duration | Amount of time the end-user can be considered authenticated after the access token has been issued. | YES1h |
dpop-required | boolean | When true, all token requests must include a DPoP proof. When false, DPoP is opt-in: clients may send a proof to receive a sender-constrained token, or omit it to receive a bearer token. | NOfalse |
refresh-enabled | boolean | If set to true, this app will issue refresh tokens that can be used to obtain a new access token without the end-user having to go through the authentication flow. | YEStrue |
refresh-expiration | duration | Amount of time the refresh token can be used to obtain an access token after it has been issued. The refresh token will never expire if this key is not present. | NO |
claims.<id>
This section holds the configuration of claim that can be collected by this authorization server.
There are two types of claims that can be collected by this authorization server:
Common for both claim types
| Key | Type | Description | Required Default |
|---|---|---|---|
allowed-values | array | List of possible values an user can provide for this claim. If not specified or null, all values will be accepted by the authorization server.Note: Changing this configuration will not change the value collected for existing user. | NOnull |
enabled | boolean | Enable the collection of this claim for end-users. If disabled, the claim will never be stored by this authorization server even if it is made available by a client or a provider. In case this config is changed, it is up to the operator of the authorization server to clear the claim that have been already collected. | NOfalse |
required | boolean | The end-user must provide a value for this claim before being allowed to complete an authorization flow. | NOfalse |
OpenID Connect claims
| Key | Type | Description | Required Default |
|---|---|---|---|
<id> | string | One of the OpenID defined claims. | YES |
Custom claims
| Key | Type | Description | Required Default |
|---|---|---|---|
<id> | string | A string identifying this claim. | YES |
type | string | The type of data the user is expected to input for this claim. | YES |
claims.<id>
The identifier must not contain a dot character.
claims.<id>.type
clients.<id>
This section holds the configuration of all clients that will be authorized to authenticate their users with this authorization server.
| Key | Type | Description | Required Default |
|---|---|---|---|
<id> | string | Uniq identifier of the client. | YES |
<authorizationFlow> | string | The identifier of the authorization flow to use for this client. See authorization flows for more details. | YES |
public | boolean | When true, the client is a public client that does not require a secret. Public clients must use PKCE and cannot use the client credentials grant. | NOfalse |
secret | string | Secret shared between the client and the authorization server. Required for confidential clients (public: false). Must be omitted for public clients. | Conditional |
allowed-grant-types | array of string | List of OAuth2 grant types this client is allowed to use. Supported values: authorization_code, refresh_token, client_credentials. See this section for more details. | YES |
allowed-scopes | array of string | List of scopes the client is allowed to request. Any scope outside this list will be filtered out by this authorization server and will not be granted. If not set or empty, all scopes are allowed. | NO |
default-scopes | array of string | List of scopes that will be requested if the scope parameter is left when calling the authorize endpoint. | NO |
uris | map of string | Named URIs for this client, usable as ${client.uris.<key>} templates in allowed-redirect-uris. Useful for defining base URLs once and referencing them in multiple redirect URIs. | NO |
allowed-redirect-uris | array of string | A list of URIs where the client is allowed to ask the redirection of the end-user at the end of the OAuth2 authorize grant flow. See this section for more details. | Conditional |
clients.<id>.allowed-grant-types
Every client must declare at least one grant type. The server rejects any client configuration without allowed-grant-types at startup.
Supported values:
| Value | Description |
|---|---|
authorization_code | Enables the authorization code flow. The client can redirect end-users to the authorize endpoint. |
refresh_token | Allows the client to exchange a refresh token for new tokens. Requires authorization_code to also be present. |
client_credentials | Allows the client to authenticate directly using its own credentials, without an end-user. Only available to confidential clients (public: false). |
Constraints:
refresh_tokencannot be used withoutauthorization_code. The server rejects this combination at startup.- When
authorization_codeis not in the allowed grant types, the authorize endpoint rejects the client andallowed-redirect-urismust not be configured. - When
refresh_tokenis not in the allowed grant types, no refresh token is issued in the authorization code flow response.
clients.<id>.allowed-redirect-uris
Clients that support the authorization_code grant type must declare at least one redirect URI. The server rejects the configuration at startup if allowed-redirect-uris is missing when authorization_code is allowed, or if allowed-redirect-uris is present when authorization_code is not allowed.
Exact string matching
As required by OAuth 2.1, redirect URIs are compared using exact string matching — no prefix matching, pattern matching, or normalization is applied.
For native applications using loopback redirects, the port component is ignored when the host is 127.0.0.1 or [::1] and the scheme is http or https, as recommended by RFC 8252. This exception does not apply to localhost or to custom-scheme URIs.
Template support
Redirect URIs support ${...} placeholders that are resolved at startup:
| Template | Description |
|---|---|
${urls.root} | The root URL of the authorization server. |
${client.uris.<key>} | A named URI defined in the client's uris map (see above). |
Example configuration:
clients:
admin:
uris:
app: https://admin.example.com
allowed-redirect-uris:
- "${urls.root}/admin/callback"
- "${client.uris.app}/callback"
mobile:
uris:
app: myapp://
allowed-redirect-uris:
- "${client.uris.app}callback"features
This section holds configuration related to features that can be enabled or disabled.
| Key | Type | Description | Required Default |
|---|---|---|---|
allow-access-to-client-without-scope | boolean | Allow the end-user to be redirected back to the client application even when none of the requested authorization scopes have been granted | NOfalse |
email-validation | boolean | Enforce the validation of the end-user's emails. A SMTP must be configured (see javamail) to enable this feature. | NOfalse |
grant-unhandled-scopes | boolean | ⚠️ UNSAFE - DEVELOPMENT ONLY. Automatically grant ALL grantable scopes requested by the client that are not explicitly granted nor declined by any scope granting rule. When enabled: Any grantable scope not explicitly granted nor declined is automatically granted. When disabled: Grantable scopes not explicitly granted are rejected (secure default). This feature only applies to grantable scopes. Consentable scopes always require end-user consent and client scopes have their own granting rules. To know more about scope granting rules, see this documentation. | NOfalse |
mfa
This section controls multi-factor authentication (MFA). MFA adds an extra verification step after the user has authenticated with their primary method (password or third-party provider).
| Key | Type | Description | Required Default |
|---|---|---|---|
required | boolean | When true, every user must complete MFA before the flow can proceed. When false, users may skip MFA. | NOfalse |
totp.enabled | boolean | Enable TOTP (Time-based One-Time Password, RFC 6238) as an MFA method. | NOfalse |
When
mfa.totp.enabledistrue, the corresponding flow URLs (mfa,mfa-totp-enroll,mfa-totp-challenge) must be configured in theflows.<id>section. SympAuthy validates this at startup and refuses to start if any required URL is missing.
flows.<id>
This configuration holds the URL where the end-user will be redirected during its authentication. Sympathy already includes a complete authentication flow.
Web
The flow may be completely customized and served by a completely different server than the authorization server.
SympAuthy derives the allowed CORS origins for the Flow API (
/api/v1/flow/**) from the URIs declared here. For each URI, the origin (scheme://host:port) is extracted and whitelisted. Requests from any other origin are refused. See the Security documentation for details.
| Key | Type | Description | Required Default |
|---|---|---|---|
sign-in | uri | The URL where the end-user will be enabled to sign-in using any supported methods (password or third party providers) | YES /sign-in |
mfa | uri | The URL of the MFA router/method-selection page. | NO |
mfa-totp-enroll | uri | The URL of the TOTP enrollment page. | NO |
mfa-totp-challenge | uri | The URL of the TOTP challenge page. | NO |
error | uri | The URL where the end-user will be redirected if an error occurs during the authentication. | YES /error |
The
mfa,mfa-totp-enroll, andmfa-totp-challengeURLs are required whenmfa.totp.enabledistrue. SympAuthy validates this at startup and fails fast if they are missing.
providers.<id>
This section holds the configuration of a third-party provider (identified by id) the end-user can go through to authenticate itself.
| Key | Type | Description | Required Default |
|---|---|---|---|
id | string | Identifier of the provider. ex. google | YES |
name | string | Name of the provider that will be displayed to users by UI. ex. Google | YES |
oauth2 | object | Info to initiate an OAuth2 authorization code grant flow with the provider. See OAuth2 keys for more details. | NO |
oidc | object | Info to initiate an OpenID Connect flow with the provider. See OIDC keys for more details. | NO |
ui | object | NO | |
user-info | object | Endpoint called to obtain end-user info from the provider. See user info keys for more details. | - OAuth2: YES - OpenID Connect: NO |
Each provider must have exactly one of
oauth2oroidcconfigured — not both. OIDC providers use OpenID Connect Discovery to automatically resolve endpoints from theissuerURL. This means you do not need to specify endpoint URLs or claim mappings manually.
providers.<id>.oidc
| Key | Type | Description | Required Default |
|---|---|---|---|
issuer | url | The OpenID Connect issuer URL. SympAuthy will fetch the discovery document at {issuer}/.well-known/openid-configuration to resolve all endpoints. | YES |
client-id | string | An identifier provided by the provider to identify authentication initiated by this authorization server. | YES |
client-secret | string | A secret provided by the provider. It must only be shared between the provider and this authorization server. | YES |
scopes | string[] | Scopes requested to the provider. The openid scope is always included automatically. | NO[openid] |
userinfo-enabled | boolean | Whether to also call the provider's UserInfo endpoint to fetch additional claims. When false, claims are extracted from the ID token only. | NOfalse |
The discovery document is fetched at startup. If the issuer URL is invalid or unreachable, SympAuthy will fail fast with a clear error message.
When
userinfo-enabledisfalse(the default), SympAuthy extracts user claims directly from the ID token returned by the provider. This is sufficient for most providers (Google, Microsoft, Auth0, etc.) and avoids an extra HTTP call.
providers.<id>.oauth2
| Key | Type | Description | Required Default |
|---|---|---|---|
| client-id | string | An identifier provided by the provider to identify authentication initiated by this authorization server. | YES |
| client-secret | string | A secret provided by the provider. It must only be shared between the provider and this authorization server. | YES |
| scopes | string | Scope requested to the provider to access the info of the user. | YES |
| authorization-url | url | The OAuth2 authorize url where to redirect the end-user to initiate an authentication with this provider. | YES |
| token-url | url | The OAuth2 token endpoint this authorization server should contact to obtain an access tokens | YES |
| token-auth-method | string | How this authorization server should pass the client id and the client secret to the token endpoint. | YES |
providers.<id>.user-info
| Key | Type | Description | Required Default |
|---|---|---|---|
url | url | Endpoint URL to get end-user information from the provider. | YES |
paths | object | Object containing JSONPath to use to extract the end-user info from the response of the UserInfo endpoint of the provider. The key is one of the OpenID defined claims. The value is the JSONPath used to extract the claim value from the response. | YES |
scopes.<id>
| Key | Type | Description | Required Default |
|---|---|---|---|
enabled | boolean | Enable the scope. | NOfalse |
type | string | The scope type. Either consentable or grantable. Custom client scopes are not supported. | NOgrantable |
urls
| Key | Type | Description | Required Default |
|---|---|---|---|
root | absolute url | The url at which the end-user can access the root of the application. | YES |
flow | object | The urls where the end-user will be redirected during authentication. See urls.flow | YES Internal flow |
rules
This section holds the configuration of scope granting rules.
| Key | Type | Description | Required Default |
|---|---|---|---|
user | array of object | Scope granting rules evaluated during authorization_code flows to grant or deny grantable scopes. Expressions evaluate user claims using CLAIM(...) and CLAIM_IS_VERIFIED(...). | NO |
client | array of object | Scope granting rules evaluated during client_credentials flows to grant or deny client scopes. Expressions evaluate client attributes using CLIENT(...). | NO |
rules.user / rules.client
Both user and client arrays share the same rule structure:
| Key | Type | Description | Required Default |
|---|---|---|---|
scopes | array of string | Scopes affected by this rule. Only requested scopes that appear in this list are granted or denied. | YES |
behavior | string | grant to include the scopes in tokens, deny to exclude them. | YES |
order | int | Precedence when multiple rules affect the same scope. A matched rule with greater order overrides matched rules of lower order. A matched deny rule always wins over grant rules of same or lower order. | NO0 |
expressions | array of string | Conditions that must all evaluate to true for the rule to match. | YES |
Example:
rules:
user:
- scopes:
- admin:config:read
- admin:users:read
- admin:users:write
behavior: grant
order: 0
expressions:
- CLAIM("email") = "admin@example.com" && CLAIM_IS_VERIFIED("email")
client:
- scopes:
- users:claims:write
behavior: grant
order: 0
expressions:
- CLIENT("name") = "backoffice"