In the past, I leveraged the open-source Identity Server version 4 (Read about my experience with it here). But recently, Identity Server has become proprietary, offering price plans that are not always seen as favorable. Especially if a company wants to support on premise instances of the app as opposed to SAAS. That means a different license needed that is called Redistribution license. The price is now mostly dependent on the number of clients used, and the more affordable plans can only be used for 5 clients. So if we exceed this limit of 5 clients, we’re going to get the following error in the logs in verbose mode. Here’s a document describing what you will receive in the logs.
Your license for Duende IdentityServer only permits 5 number of clients. You have processed requests for 6. The clients used were: ["client6", "client2", "client5", "client4", "client3", "client1"].
So imagine you’ve bought the license for redistribution business edition for 10000 and that is only for 5 clients. But for unlimited clients the price is 35000 per year! Now we have about 27 clients and 5 client would not be enough and the price is a lot to swallow. So, many people may wonder if they can share clients in order to save money and keep the client count to a maximum of 5? This is what I’m going to answer in this article.
What kind of security risks can arise if we share a Identity Server client for multiple applications?
Sharing the same client between multiple applications can introduce several security risks in an OAuth 2.0 environment managed by an identity server. Some of these risks include:
Authorization Confusion: Sharing the same client credentials (client ID and secret) between multiple applications can result in authorization confusion. For example, if one of the applications is compromised and the client credentials are stolen, the attacker could potentially use those credentials to access resources on behalf of other applications. This could result in unauthorized access to sensitive data and actions that were not intended by the other applications.
Token Confusion: When multiple applications share the same client, they also share the same OAuth 2.0 token endpoint. This could lead to token confusion, where tokens issued for one application could be used by another application. This could result in unauthorized access to resources and actions that were not intended by the user. It’s important to ensure that the client’s scope is appropriate for both applications. If the scope is too broad, it could result in one application having access to data or functionality that it should not have.
Difficulty in Revoking Access: Sharing the same client between multiple applications can make it difficult to revoke access to specific applications. For example, if a user revokes access to one of the applications, it would also revoke access to all the other applications sharing the same client. This could result in unintended consequences and make it difficult to manage access revocation. When sharing clients, you should take the lifespan of tokens issued by the identity server into consideration. Long expiration times could pose a security risk, while tokens that expire too quickly might inconvenience the user.
Reduced Granularity: Using a shared client between multiple applications can result in reduced granularity in access control. It may not be possible to define specific scopes and permissions for each individual application, which could result in broader access than necessary. This could increase the risk of unauthorized access to sensitive data and actions.
Difficulty in Debugging: Using a shared client between multiple applications can make it difficult to debug and troubleshoot issues with individual applications. If an issue occurs with one application, it may be difficult to determine whether the issue is with the client or with the other applications that are sharing the client.
Token Validation: When clients are shared, the identity server needs to be configured to properly validate tokens issued to each application. If the token is not validated correctly, it could result in unauthorized access to resources.
Client credentials: It’s essential to ensure that the client credentials are securely stored and transmitted when shared between applications. Client secrets should be encrypted or hashed to protect against unauthorized access.
Third-party dependencies: If the client uses third-party dependencies, it’s essential to consider the security implications of those dependencies when sharing clients. Vulnerabilities in third-party dependencies can pose a security risk to the entire system.
In general, it’s recommended to create separate clients for each application to minimize the security risks associated with sharing clients. This approach provides more granular access control, reduces the risk of confusion and unauthorized access, and makes it easier to manage access revocation and troubleshoot issues with individual applications.
More Specific Security Risks Based on Duende Identity Server Configuration for each Client
Imagine we have a database that hold our client’s configuration. These configurations can be many things related to the client, but the most notable ones are as the following.
Here is a brief explanation of each configuration:
- RequireClientSecret: A boolean value indicating whether the client application requires a secret to be presented when requesting access tokens from Identity Server 4.
- RequirePkce: A boolean value indicating whether the client application requires Proof Key for Code Exchange (PKCE) to be used for authorization code grant type.
- AllowAccessTokensViaBrowser: A boolean value indicating whether access tokens can be transmitted via browser for the client application.
- AllowOfflineAccess: A boolean value indicating whether the client application can request refresh tokens and access tokens without user interaction.
- IdentityTokenLifetime: The lifetime of the identity token in seconds.
- AccessTokenLifetime: The lifetime of the access token in seconds.
- AuthorizationCodeLifetime: The lifetime of the authorization code in seconds.
- RefreshTokenUsage: A flag indicating how refresh tokens should be used. 0 indicates that refresh tokens are one-time use only, while 1 indicates that refresh tokens can be used multiple times.
- AlwaysSendClientClaims: A boolean value indicating whether client claims should be sent in every token issued by Identity Server 4.
Users of Identity Server often store the configuration in a [Clients] table. If we inspect the values for each configuration, we will see that they share a lot of similar values in many column. But there are some column which have different values based on the application. However, sharing those configurations by sharing clients can specifically pose problems. I go into more detail about why sharing client can create problem for each specific configuration.
What are the security implications of sharing Pkce between apps when Using Identity Server
PKCE (Proof Key for Code Exchange) is a security feature that is used to protect against authorization code interception attacks when using OAuth 2.0 for authentication. When using PKCE, the client app generates a secret key that is used to encrypt the authorization code before it is exchanged for an access token. This helps to prevent interception and reuse of the authorization code by a third party.
In general, PKCE secrets should not be shared between different applications. Each client app should generate its own PKCE secret and use it only for its own authentication flows. Sharing PKCE secrets between apps would increase the risk of the secret being compromised or leaked, which would undermine the security benefits provided by PKCE.
That being said, if you are developing multiple apps that have similar authentication flows and use the same OAuth 2.0 provider, it may be tempting to reuse PKCE secrets between those apps to simplify development. However, doing so would weaken the security of the authentication flows and increase the risk of unauthorized access. It’s always best to generate unique PKCE secrets for each client app, even if they are developed by the same team or company.
When do we want to Allow Access Tokens Via Browser and what is the Risk associated with doing it for multiple applications?
In general, access tokens should be kept secure and not exposed to the user or the user’s browser, as this could increase the risk of the token being stolen or misused by a third party.
However, there are some scenarios where it may be appropriate to allow access tokens to be obtained via a browser. For example:
Single-page applications (SPAs): SPAs are web applications that run entirely in the browser and do not require page reloads. Because SPAs do not have a server-side component, they typically obtain access tokens directly from the OAuth 2.0 provider via the browser.
Mobile and desktop applications: Mobile and desktop applications may use a browser to obtain an access token via an OAuth 2.0 authorization flow. This can be done using an embedded browser component or by launching the system browser and redirecting back to the application once the flow is complete.
Development and testing: During development and testing, it may be useful to obtain access tokens via a browser for debugging purposes. However, this should only be done in a secure environment and not in production.
In general, it is important to carefully consider the security implications of allowing access tokens to be obtained via a browser. Access tokens should be protected and not exposed to the user or the user’s browser unless there is a compelling reason to do so.
Which type of applications should allow Offline Access to the token, what is the security Risk Associated with doing so?
Offline access refers to the ability of an application to obtain a long-lived access token that can be used to access resources or perform actions on behalf of the user even when the user is not actively using the application.
Offline access can be appropriate for some types of applications, such as:
Applications that perform background tasks: Some applications may need to perform background tasks on behalf of the user, even when the user is not actively using the application. For example, a fitness app might need to periodically sync data with a backend service, even when the user is not using the app.
Applications that store data locally: Some applications may store data locally on the user’s device and need to access that data even when the user is not actively using the application.
However, granting offline access to an access token can increase the security risk associated with the token. This is because an offline access token can be used to access resources or perform actions on behalf of the user even when the user is not actively using the application. If the token falls into the wrong hands, it could be used to perform malicious actions.
To mitigate the security risk associated with offline access, it’s important to carefully consider which applications should be granted this privilege. In general, offline access should only be granted to trusted applications that have a legitimate need for it. It’s also important to monitor access token usage and revoke tokens that are no longer needed or that have been compromised.
Is it a good idea for applications to share Identity Server Token Lifetime/Access Token Lifetime/ Authorization Code Lifetime?
Sharing lifetime tokens between applications is generally not a good idea. In the following section I describe what each of these are and then discuss if it’s a good idea to share them.
Identity tokens, also known as ID tokens, are a type of JSON web token (JWT) that contains claims about the authenticated user. ID tokens are typically used to verify the identity of the user and provide information such as the user’s name and email address.
Identity Server Access Token
In Identity server, access token is a type of token that is used by clients to access protected resources or APIs on behalf of a user. The access token contains information about the user’s identity and the permissions granted to the client to access the protected resources.
The main difference between an access token and an identity token is that the access token is used to authorize access to protected resources or APIs, while the identity token is used to authenticate the user’s identity. The access token is also typically more short-lived than the identity token and can be refreshed or revoked as needed.
In Identity Server, the Authorization Code Lifetime is a configuration setting that specifies the maximum amount of time that an authorization code can be used to obtain an access token.
When a user logs in or authorizes a client application to access their protected resources, Identity Server generates an authorization code that is sent back to the client. The client can then exchange this code for an access token that can be used to access protected resources on behalf of the user.
The security and other implications of share Identity Server token lifetime
Sharing token lifetime between applications is generally not a good idea. Access tokens have a finite lifetime, after which they expire and become invalid. The token lifetime is typically set by the OAuth 2.0 provider and can be customized by the application developer.
Applications should not share token lifetimes because it can lead to security vulnerabilities. For example, if two applications share a token lifetime, an access token obtained by one application could potentially be used by the other application after the token has expired. This could lead to unauthorized access to resources or actions that the user did not intend.
In addition, sharing token lifetimes can make it more difficult to manage token revocation and refresh. When an access token is revoked, it should be invalidated for all applications that were granted access with that token. If two applications share a token lifetime, revoking a token for one application would also invalidate the token for the other application, which could cause unintended consequences.
Finally, sharing token lifetimes can make it harder to debug and troubleshoot issues with individual applications. If an access token is not working as expected for one application, it may be difficult to determine whether the issue is with the token itself or with the other application that is sharing the token lifetime.
In general, it’s best to assign unique token lifetimes to each application and not share them between applications. This will help to ensure that access tokens are used only by the applications for which they were intended and reduce the risk of security vulnerabilities.
Should all applications use Refresh Token, do all of them need it? what happens in terms of security if we use it for all apps?
Not all applications need to use refresh tokens, as it depends on the specific requirements of the application and the user experience that is desired. Refresh tokens are typically used in OAuth 2.0 flows to obtain new access tokens when the original access token expires. This can help provide a seamless user experience by allowing the application to maintain access to the user’s resources without requiring the user to repeatedly re-authenticate.
However, there are potential security risks associated with using refresh tokens. If a refresh token is stolen, it can be used to obtain new access tokens without requiring the user to re-authenticate. This means that an attacker who obtains a refresh token could potentially have ongoing access to the user’s resources until the refresh token is revoked.
For this reason, it’s important to carefully consider the use of refresh tokens in each application and to implement appropriate security measures to protect them. This might include encrypting the refresh token when it’s stored, using short-lived refresh tokens, and implementing mechanisms to revoke refresh tokens if they’re suspected of being compromised.
In general, applications that require ongoing access to a user’s resources, such as email or calendar applications, may benefit from using refresh tokens. However, applications that have short-lived access needs, such as a one-time authorization for a single action, may not need to use refresh tokens. Ultimately, the decision to use refresh tokens should be based on the specific requirements of the application and the desired user experience, taking into account the potential security risks. But using the same policy for all of them (by using one client) might pose security risks.
Should all applications Send Client Claims, do all of them need it? what happens in terms of security if we do that for all apps?
Not all applications need to send client claims in OAuth 2.0 flows. Client claims are custom JSON objects that can be sent by the client application to the authorization server during the authentication process. The content and purpose of these claims are defined by the client application and can be used to provide additional information about the client to the authorization server.
Whether or not to send client claims depends on the specific requirements of the application and whether the additional information provided by the client claims is necessary for the authorization server to make an informed decision about granting access to the user’s resources.
Sending client claims does not necessarily pose a security risk, as long as the claims are properly validated and the content of the claims is not used to make security-critical decisions without proper authentication and authorization checks. However, sending unnecessary client claims can potentially introduce additional complexity and attack vectors to the authentication process, and may increase the risk of vulnerabilities.
It’s important to consider the potential security implications of sending client claims in each application and to carefully evaluate whether the benefits of sending additional information outweigh the potential risks. If client claims are sent, they should be properly validated by the authorization server and the content of the claims should be carefully controlled to prevent unauthorized access to user resources.
What can be do if we don’t afford the higher price plans and we want to keep the clients as low as 5?
Don’t use Duende Identity Server
You’ve heard me! There are other alternatives like auth0 that do not have absurd price plans and their pricing is more flexible. There might be other alternatives also with better price.
Continue using the Version 4
Another option is to fork the version 4 that was free and use that instead without relying on official support. But that doesn’t seem like a good idea to me because using an unsupported version could lead to potential security risks. It’s important to have an up-to-date system that receives regular security patches and updates.
Upgrade the current plan to Unlimited Starter (That can define unlimited clients)
Provided you have massive quantities of money.
Analyze the security needs of applications and categorize them based on their security needs
This can be an option too, which is not perfect, but it’s better than sharing clients without analysis. By doing so then we can have batch of multiple applications that have almost the same security needs. Subsequently they can share a client with lower level of risks (for example application that needs to share the access token via browser like the SPA apps). Here’s an example of how we can categorize our clients based on their security needs.
These clients require the highest level of security and must be treated with the utmost care. They include clients that require a client secret, require PKCE, and do not allow access tokens via the browser or offline access.
These clients require a moderate level of security and include clients that require a client secret, require PKCE, and allow access tokens via the browser but not offline access.
These clients require the lowest level of security and include clients that require a client secret, do not require PKCE, and allow access tokens via the browser and offline access.
Some Criteria’s to help us categorize our clients into the above security groups
- RequireClientSecret: Clients that require a client secret for access token requests have a higher level of security compared to those that do not require it.
- RequirePkce: Clients that require PKCE for authorization code grant type have a higher level of security compared to those that do not require it.
- AllowAccessTokensViaBrowser: Clients that allow access tokens to be transmitted via browser have a lower level of security compared to those that do not allow it.
- AllowOfflineAccess: Clients that allow offline access have a higher level of security compared to those that do not allow it.
- IdentityTokenLifetime, AccessTokenLifetime, AuthorizationCodeLifetime: Clients that have shorter token lifetimes have a higher level of security compared to those that have longer token lifetimes.
- RefreshTokenUsage: Clients that have one-time use refresh tokens have a higher level of security compared to those that allow multiple refresh token usages.
- AlwaysSendClientClaims: Clients that always send client claims have a higher level of security compared to those that do not.
More explanations about these setting to help with out analysis
Here I’m going to list the settings based on the security feature they provide. There might be multiple settings that are concerned with one security feature.
- RequireClientSecret: This setting requires a client secret to be passed with the client’s requests to authenticate itself. It is considered a confidentiality measure to ensure that only the client application can access its own data.
- RequirePkce: This setting requires Proof Key for Code Exchange (PKCE) to be used during authorization code flow. This ensures that the authorization code can only be exchanged by the same application that originally requested it, preventing attacks such as code interception or replay.
- AlwaysSendClientClaims: This setting enables the client to send additional claims with the authorization request. This may include sensitive information, so it should only be used if necessary.
- AllowAccessTokensViaBrowser: This setting determines whether or not access tokens can be sent to the browser. If enabled, it could be a security risk as access tokens are sensitive information that should not be exposed to the client-side.
- AllowOfflineAccess: This setting determines whether or not a refresh token can be issued. This is useful for scenarios where the client may need to access the protected resource when the user is not present, but it should be used with caution as it could increase the risk of unauthorized access.
- IdentityTokenLifetime: This setting determines how long the identity token is valid for. The identity token is used to authenticate the user and is typically short-lived for security reasons.
- AccessTokenLifetime: This setting determines how long the access token is valid for. The access token is used to authorize the client to access protected resources and should also be short-lived to minimize the risk of unauthorized access.
- AuthorizationCodeLifetime: This setting determines how long the authorization code is valid for. The authorization code is used to obtain an access token and should be short-lived to prevent interception or replay attacks.
- RefreshTokenUsage: This setting determines whether or not the refresh token can be used more than once. If set to “0”, the refresh token can only be used once, which is more secure. If set to “1”, the refresh token can be used multiple times which could be a security risk if the token is stolen.
Other Considerations that makes Sharing Clients Between application a BAD IDEA
- Let me clarify that even if applications have identical security requirements, sharing tokens isn’t recommended. This is because tokens contain specific information about a session, a user, and permissions, which should be unique for each application and session for the sake of traceability and granularity of access controls.
- It’s important to remember that security requirements can and often do change over time as new threats emerge and applications evolve. Therefore, regular security reviews and updates to the categorization and the policies applied to each category would be needed to maintain a strong security posture.
- While pricing is indeed a crucial factor, other factors such as the product’s features, the company’s reputation, customer service, etc., should also be considered.
- Lastly, when categorizing your applications based on their security needs, privacy and data protection requirements are also taken into consideration, especially if you operate in regions with strict data protection laws, like the European Union’s GDPR.
In this blog post, I delve into the complex world of Identity Server and explore the potential security risks that arise when sharing clients between applications. I begin by addressing the pricing and license limitations of Identity Server, shedding light on the challenges faced by companies who prefer on-premise instances over SAAS. While sharing clients may seem like a cost-effective solution, I’ve uncovered the hidden dangers it poses.
The heart of the article lies in our comprehensive analysis of the security implications that accompany shared clients in an OAuth 2.0 environment. I discuss the risks of authorization confusion, token confusion, difficulties in access revocation, reduced granularity in access control, and the challenges of debugging. Emphasizing the importance of granular access control, I strongly recommend creating separate clients for each application to mitigate these security risks effectively.
Moreover, I explored the specific security risks associated with sharing PKCE secrets, enabling access tokens via the browser, and granting offline access. By examining each scenario, I provide insights into the potential vulnerabilities and advise on the need for careful evaluation and customization based on individual application requirements.
In conclusion, I offered alternative strategies for organizations facing limited budgets or seeking cost-effective solutions. I highlighted the option of exploring alternative identity management solutions, analyzing security needs to categorize applications, or upgrading to plans that accommodate unlimited clients. By understanding the risks and making informed decisions, organizations can strike the optimal balance between security and cost efficiency in their identity management practices.