Skip to main content

Managing multiple environments (prod/staging/dev)

Throughout the development life-cycle of your platform and product applications, you'll have multiple environments. Common ones include Production and Staging, although there can be many more. This article answers the question of how best to architect your Authress account to better manage these different environments.


The optimal solution encourages having only a single Authress CIAM account. There are many reasons for this:

  • Single ID - every account gets a unique Authress account ID. Having multiple increases complexity in your configuration.
  • Custom Domain - For higher reliability, Authress supports running your domain via DNS subdomain. Every account must have a different DNS. With multiple accounts, along with different Authress account IDs, the url for each account's API has to be different.
  • UX - Access and usage for your Developers and Support Engineers when using Authress is easier and simpler when there is only one account.
  • User Identities - When one of your users that logs in through different Authress accounts, they will get different IDs. Since there is only one user there should only be one userId. Multiple accounts creates multiple user IDs.
  • Role management - roles do not have to be copied between environments, it is set once and it works. With multiple environments, the roles have to remain synced to ensure that the production version of your platform can specify roles that actually exist. Defining the roles in one place, is a frequent implementation of the DRY software design principle.

Having multiple Authress accounts is supported, but not recommended. Where possible the Authress development team has enabled a single Authress account to work for your use case. Each account provides the ability to separate resources with granular resource hierarchy paths. If for some reason it won't work, please let us know.

Resources to manage

When integrating multiple environments in one Authress account, the following aspects need to be explicitly considered:

Service clients

The Authress recommendation is to create separate service clients for every environment. This provides two benefits:

  • Allows better tracking and auditing of resources that are generated/requested/authorized for users and the service client.
  • Separate API keys enable secure separation of access to the Authress API between your environments.


Overall, the same roles that work in one environment, will work for all environments. This is because roles are a collection of permissions (See Permissions and Roles for more information on this.) Permissions are scoped usually to a type of resource, and are of the form resource-type:action such as documents:read. There is no environment included in the permission or role, so roles can be reused. Additionally, reusing roles and permissions is exactly what we want to do, so that every service environment can have the same configuration.

Resource hierarchies

Resource path support hierarchies of ownership. However, the resource path should not contain an environment identifier, such as prod:tenants/{tenantId}. If one is added, then throughout your application, your environment identifier must present. This is brittle. A more reliable solution is for every resource ID that is generated to be unique to the relevant environment. For instance tenants could be generated with a tst_ prefix in non production environments, although in general, simple uuids will guarantee that there are no conflicts and avoids this extra and unnecessary complexity. Some platforms prefix the type of resource as part of the guid. Adding an extra character for the environment can be a great pattern to understand what the type of resource is just by looking at the ID string. doc_tst_0123 versus com_prd_a1 immediately tell us a document created in the test environment as compared to a comment created in the production environment.

These aren't required, but we have found common solutions that make it easy to solve most problems and get up and running with even very complex platforms.

  • Avoiding putting the environment in your resource hierarchy, service:prod:resources/{rId} is a bad idea. However, putting the environment in the IDs themselves, should not cause a problem.
  • Guarantee the generation of globally unique IDs using UUIDs for resources.
  • Generate multiple service clients, one for each environment.
  • Give the service clients Admin (*) to service:resources/* for each type of resource that the service will control.
  • Create and modify access records with predictable IDs. While you can create generic access records, coupling them to either a user, a group, or a resource makes it easy to understand why it exists when doing investigation later. It also enables programmatic modification by convention rather than needing to store the recordId after generation.

Common questions

What's the difference between service:resources/re_123 and service/resources/re_123

The : doesn't have a reserved meaning in resource URIs. That means that service:resources would be the name of the hierarchy in that path, it would be same as if you named your resource: service_resources. Qualifying your resources is important since multiple services in your software might have the same resource type. Take configuration for instance. Two services might both have a configuration resource:

  • The Document Repository service's configuration could be: document-repository/configuration
  • And the Tenant Repository service's configuration might be: tenant-repository/configuration

Having the service name be part of the hierarchy is important and is a frequent naming convention. service/resources/re_123 enables you to give * access to all resources a service owns. This is possible with services/* whereas it cannot be done with service:*. If you plan to grant users or entities access to all resources a service owns without having to list out the services one by one, service_name, then resource-type, followed by the resource_id is a great pattern. An example of this would be: document-repository/documents/doc_123.

If the staging and production service clients both have Admin (*) access to all the services' resources, isn't this a security concern?

Generally, no. The reason is that even though they both have Admin access, the resources they have access to are only the ones that the service itself owns. The only service that checks access to these resources is itself. Concretely:

  • The Staging version theoretically has Admin access to all the production resources.
  • This access is checked by the Authress API when the Staging version attempts to create or edit access records that contain the service's resources
  • The Staging service doesn't have access to the Production version's database

So even though the Staging service will check the same permissions as the Production service version, it can't and won't return the production version of the resources because it doesn't have them in the database.

Using access records helps to further avoid issues here. When granting access to resources, generate an access record with the ID Tenant:{TenantID}:ResourceType:{ResourceID}. This will cause a 409 if the other version of the service attempts to create that access record even though it already exists. You can use the access record Id as a unique constraint to guarantee a new record isn't created for a resource that is already owned by another user.