Table of Contents

Configuration

In development, the authentication configuration is used by the development server launched by the "neos run" command. The configuration has to be done in a YAML configuration file of the cluster. When the authentication configuration changes, there is no need to regenerate.

In production, the authentication configuration is used by the gateway. The configuration must be done in the gateway configuration file.

Configuring authentication can be complex, so the framework provides presets to make it easy and to fit the most common scenarios. It is also possible to provide a complete configuration without preset to address more specific cases.

Presets

The standard development server (default)

By default, when a cluster is created via the neos init command, an authentication configuration is defined in the configuration.auth.yml file:

Authentication:
  Preset: NeosDevAuth

We provide a standard authentication server that can be used by clusters under development.

This authentication server offers a connection via a unique identifier and password, but also a connection via the Groupe Isagri Active Directory.

During development, you can log in with your Groupe Isagri Active Directory account or delete the authentication configuration file.

The registration of new users is not yet publicly available because it will be managed by the "Users" transversal module. This development is in progress.

It is important to note that the authentication server is not yet ready to be used in production. It will be improved as we go along as we plan to make it the standard solution we offer to all collaborators.

The source code of the project is available on Azure DevOps.

External providers

It is also possible to set up an authentication through an external provider.

This scenario is ideal to avoid having the responsibility of the authentication server.

Here you will find guides for several external provider (the list will grow as time goes on):

If you need an authentication with several providers at the same time (for example a Google connection, a Microsoft connection and a classic login/password connection), you will have to use a custom authentication server (like Auth0, Okta or an homemade authentication server).

Manual configuration

If the framework does not provide a preset for the desired authentication server, you can specify a complete configuration using this template :

Authentication:
  Authority: # (required) The URL of the authentication server.
  ClientId: # (required) The resource identifier of the cluster.
  ClientSecret: # (optional) The resource secret of the cluster.
  Scopes: # (optional) The list of scopes so that the cluster must have access.
  McpScopes: # (optional) The list of scopes advertised to MCP clients (defaults to Scopes when not set).
  UserPropertiesMap: # (optional)
    Identifier: # (optional) The name of the token claim used to retrieve the user's identifier to populate the `Identifier` property of the Neos user.
    Email: # (optional) The name of the token claim used to retrieve the user's email to populate the `Email` property of the Neos user.
    FirstName: # (optional) The name of the token claim used to retrieve the user's first name to populate the `FirstName` property of the Neos user.
    LastName: # (optional) The name of the token claim used to retrieve the user's last name to populate the `LastName` property of the Neos user.
    TenantIdentifier: # (optional) The name of the token claim holding the identifier of the tenant the user is admitted into on a multi-tenant cluster (see TenantIdentifier below).
    CustomProperty1: # (optional) The name of the token claim used to populate the user's additional properties of the Neos user.
    # ...
  ServiceAccountClaimNames: # (optional) For client credentials flow, the claim names to use for service account identifier, in order of preference. Default values are: "azp", "appid", "sub".
    - azp
    - appid
    - sub
  AdditionalJwtAuthorities: # (optional) Additional JWT authorities for validating tokens from other identity providers. Useful when using Azure AD B2C for user authentication while also accepting client credentials tokens from Azure AD.
    - Name: # (required) A unique name for this authority (will be used as the authentication scheme name).
      Authority: # (required) The authority URL (e.g., https://login.microsoftonline.com/{tenant}/v2.0).
      ValidAudiences: # (required) The valid audiences for tokens from this authority.
        - audience1
        - audience2
      ValidIssuers: # (optional) The valid issuers for tokens from this authority. If not specified, the issuer will be validated against the authority's metadata.
        - issuer1
        - issuer2
      IssuerPattern: # (optional) A pattern to match against the token issuer for routing purposes. Example: "login.microsoftonline.com" to match all Azure AD issuers.
  CookieLifetimeInHours: # (optional) The cookie lifetime in hours. 48 hours by default.
  TenantClaimAdmission: # (optional) The users admitted into the tenant of the TenantIdentifier claim: Always (default) or UsersWithoutTenant (see TenantIdentifier below).

Example with a custom authentication server :

Authentication:
  Authority: https://localhost:5001
  ClientId: b68c22cc-6e48-4ff8-bf3f-49f70e388376
  Scopes:
    - custom_scope
  UserPropertiesMap:
    Name: username
    Email: principal_email
  CookieLifetimeInHours: 72

McpScopes

An MCP client does not always request the same scope as the cluster's own web login: the web login can use the bare identity provider App ID URI as its scope, while an MCP client may need the full API scope URI to be granted an access token for the protected resource it exposes. Setting the MCP scope directly in Scopes would then break the web login (for example, an Azure AD B2C custom policy can then fail to return an id_token, since the requested scope combination differs from what the login flow expects).

McpScopes lets you configure the scope advertised to MCP clients independently of Scopes. When it is not set (or set to an empty list), it defaults to Scopes, preserving the previous behavior.

Authentication:
  # ...
  Scopes:
    - b68c22cc-6e48-4ff8-bf3f-49f70e388376 # used by the web login
  McpScopes:
    - https://xxx.onmicrosoft.com/b68c22cc-6e48-4ff8-bf3f-49f70e388376/API.DefaultAccount # used by MCP clients
    - offline_access # lets MCP clients renew their access token without signing in again

Include offline_access so the identity provider issues a refresh token along with the access token. Without it, an MCP client cannot renew its access token and the user must sign in again each time it expires. The cluster's token endpoint relays the refresh token requests of MCP clients to the identity provider.

TenantIdentifier

On a multi-tenant cluster, a user can only reach the tenants they are a member of. When the identity provider knows the tenant of each user, map the TenantIdentifier user property to the claim holding the tenant Identifier (the value sent in the Neos-Tenant header, not the tenant name):

Authentication:
  # ...
  UserPropertiesMap:
    TenantIdentifier: extension_TenantIdentifier
Important

The tenant identifier is case-sensitive: the claim value must match the Identifier of the tenant exactly. For a tenant whose identifier is Neos, a claim worth NEOS or neos admits the user into no tenant, and a warning is logged.

A claim value containing & or = is refused the same way: these characters separate the properties of the Neos-User header, which forwards the claim to the cluster.

The claim then admits the user into this tenant, even when they are not yet a member of it:

  • A user with no tenant is redirected to the tenant of the claim instead of receiving a 403 response.
  • A user who is a member of other tenants also sees the tenant of the claim on the tenant selection page.
  • The tenant of the claim is only added when it exists, has a data persistence on the cluster and is active. Otherwise a warning is logged and the request is handled as if the claim were absent.

Unlike the other user properties, the TenantIdentifier claim is only forwarded to the cluster in the Neos-User header when the proxy admits the user into its tenant, as described above. A refused claim is never forwarded, so neither the requests of the user nor the inter-cluster calls made while handling them can reach a tenant the proxy refused. When a request names no tenant, the cluster lets the user select the tenant of a forwarded claim, like a tenant they are a member of.

With CreateUserAccountIfNotExists, the account is then created in this tenant at the first login, and the user becomes a member of it.

Caution

The claim grants access to a tenant without any action of an administrator. Map it to an attribute that users cannot change themselves in the identity provider.

TenantClaimAdmission defines which users the claim admits:

Value Behavior
Always (default) Every user is admitted into the tenant of the claim, in addition to the tenants they are a member of.
UsersWithoutTenant Only a user who is not yet a member of any tenant of the cluster is admitted. A member of another tenant keeps their tenants, a request for the tenant of the claim receives a 403 response and a warning is logged.
Authentication:
  # ...
  UserPropertiesMap:
    TenantIdentifier: extension_TenantIdentifier
  TenantClaimAdmission: UsersWithoutTenant
Note

The proxy decides the admission for the cluster that receives the request of the user. An admitted claim then stays in the Neos-User header that the inter-cluster calls forward, and the clusters they reach do not apply their own TenantClaimAdmission. With UsersWithoutTenant, a user admitted into a tenant on a cluster where they have no tenant can thus reach this tenant on another cluster through an inter-cluster call, even when they are a member of another tenant there.

When TenantIdentifier is not mapped, nothing changes: only the members of a tenant can reach it.

ServiceAccountClaimNames

When using client credentials flow (service-to-service authentication), the framework needs to identify the calling service. The ServiceAccountClaimNames property defines which JWT claims to check for the service account identifier, in order of preference.

Default values are: azp (authorized party), appid (Azure AD application ID), and sub (subject). These are standard JWT claims used by different identity providers to identify service principals.

Example with Azure AD:

Authentication:
  # ...
  ServiceAccountClaimNames:
    - appid          # Azure AD uses 'appid' for service principals
    - sub

AdditionalJwtAuthorities

In scenarios where you need to accept tokens from multiple identity providers, you can configure additional JWT authorities. This is useful when your application uses one identity provider for user authentication (e.g., Azure AD B2C) but also needs to accept service-to-service tokens from another provider (e.g., Azure AD).

Each additional authority requires:

  • Name : The name of the additional authority
  • Authority: The URL of the identity provider
  • ValidAudiences: The list of valid audiences (resource identifiers) that this authority issues tokens for
  • ValidIssuers (optional): Specific valid issuers. If not specified, the issuer is validated against the authority's metadata
  • IssuerPattern (optional): A pattern to match against the token issuer for routing purposes

Example with Azure AD B2C for users and Azure AD for service accounts:

Authentication:
  Preset: AzureAdB2C
  # ... (Azure AD B2C configuration for users)
  AdditionalJwtAuthorities:
    - Name: AzureADServicePrincipal
      Authority: https://login.microsoftonline.com/{tenant}/v2.0
      ValidAudiences:
        - api://your-app-id
      ValidIssuers:
        - https://sts.windows.net/{tenant}/
      IssuerPattern: login.microsoftonline.com

If you want to use a custom authentication server, the authentication server must comply with the OpenID Connect standard and allow the "Authorization Code Flow + PKCE" flow.

If you want to use the standard development authentication server that we offer in production and/or you want to adapt a behavior, you must fork the project, adjust it and deploy it by yourself.

User synchronization with authentication provider

The UserPermissions module can create the users of your cluster in the authentication provider (NeosAuth, Microsoft Entra External ID, Okta or Azure AD B2C), with the UserPermissions:AuthProvider configuration property. See User Synchronization.

Production

In production, authentication is configured at the Gateway level.

It must be put in the gateway secret, please see this article for more information.