The most common lifecycle of a resource flows from Creation, to Updates, to Deletion. When a resource is created, access must be granted to the user so that they can read and write to it afterwards. By default, new resources in an application don't imply access control. To grant access to a resource to a user review access record creation. If creation is when access records are created, then reads and updates are when access records are verified.
This article discusses ways to handle the Deletion of resources, and how to ensure that your deletion requests are secure.
All of these actions and examples are available in the available SDKs.
Authress provided security
Authress provides these security features out of the box to help prevent account sub-resource takeover.
Resources and permissions in Authress are cached and those caches are automatically coupled to the JWT access token used to check the permission. When the token for the user is rotated, the caches is no longer valid and therefore requests will be realtime. Fundamentally, when a user that logs out discarding the user's access token will release access to any resource they no longer should be able to access.
This solves most of the access removal security issues. It is important to note, that the live tokens still can access the resources, so they should be treated as such and never exposed while they are active, doing so is a security risk.
Additionally, to better encourage good security practices, access control and authorization requests made to Authress will always contain a
Cache-Control response header set on every response. This header is bounded by the ceiling of the token lifetime, preventing expired tokens from being used directly with Authress. Once tokens expire, so does any cached accessed. If you are utilizing the
Cache-Control header to drive caching, then your caches will automatically be purged at the right time.
When the resource is actually removed from your application or platform, the Authress API supports forcing resource removal. By signalling to Authress that the resource no longer exists, it will DENY actions requiring permissions for that resource and sub-resources. This additionally prevents deleted resources from being taken over until the expiry on the deletion is permanent.
Access record updates on deletion
Even if the token the user has is still valid (that is, if the JWT for your user hasn't expired yet), if the access record is removed that grants the user access, the user will no longer have access. Unlike most solutions, since Authress keeps a logical separation between User Identity and Access Control, valid tokens don't automatically grant access. For more information on this situation see the Academy article on Invalidating user access.
Customer recommended application security implementations
Application security is a two fold responsibility, while Authress provides most of the functionality by default, additional application patterns should be utilized in application.
Resource visibility masking via Soft-Deletes
Application resources that are deleted should be marked internally as
DELETED, but not actually deleted. This is known as a Soft Delete. The benefit of a soft delete is that it allows your software to distinguish between a resource that doesn't exist yet and one that was deleted. It also provides a critical resource for investigation and audit logging. Deleted data can never be recovered, but a soft-delete can be easily recovered when a support case comes up. This allows the application to managed future reads and updates to the resource. It's easy to translate a
DELETED resource to a 404 on request, but it's impossible to handle application access correctly once the resource has been deleted. When there needs to be different handling between a deleted resource and a resource that doesn't exist yet, it's beneficial to save the resource and delete it after a cutoff time. Soft deleting a resource also enables you to prevent a new resource from being created with the same identifier, and thus avoids sub-resource takeover.
Account and tenant caching
In application authorizers usually implemented in API Gateways, can cache additional access control data. Authorizers are usually used to verify the incoming user identity access token. And when your users can be part of a customer tenant with other users, this customer tenant ID can also be cached in your Authorizer. Since the user tenants very rarely change (or never for some applications). It avoids the need to lookup the user's tenants every time a request is made. This provides a preliminary and easy to do--quick validation--in app for security purposes before even making an authorization check via Authress. In most cases this is unnecessary and Authress handles the validations in any cases, but there are some edge cases where this extra level of efforts provides a simple optimization for security when your application already knows the relevant tenant.
Important Do not cache the tenant in the Authorizer when the tenant can change, or the user can be part of multiple tenants. Doing so in these cases, caching the tenant will lead to unexpected behavior your application as well as your users when the tenant changes. In these cases, directly look up the tenants the user has access to using Authress.
If using Authress for Authentication and Login, Authress publishes the tenant information in the JWT when the user logs in. If you aren't using Authress for authentication and user identities, the tenant related data will be stored in Authress access records and then can be fetched using the List user resources endpoint. For example, if your users are part of a customer account, and are granted access, then a simple query to get all the accounts a user has access to would be:
Forcing token invalidation
When a user deletes their own user or removes/deletes a tenant or account, a complete disassociation may be necessary. The best way to achieve this requires logging the user out. Immediately discard the user's JWT access token. Since the permissions and resources are coupled to the JWT the user has, new JWTs even for the same user will force new authorization requests bypassing any caching you've created on your side. Because of this the user will no longer see any application resources as if user signed up for the first time. To terminate the user associated in Authress, Authress provides a GDPR-compliant endpoint which can be used to delete all user access records and all user related data: Delete user.
Async handling of deletion
As with many resources, async handling of deletion is preferred for most actions. That is, users don't usually care that deleted access can still be access for the next few seconds, minutes, or hours. Optimizing the security within the bounds of your use case is a great way to provide the level of control you need without sacrificing time and effort to build an over-engineered system.
202: Accepted on a request to delete a resource, and then deleting it async is known as
Because, there are always concurrency issues and the best practice is to embrace them rather than trying to ensure everything is consistent. Using the eventual consistency paradigm optimizes for this practice and inevitable eventuality. In some cases this may mean that accidentally sub-resources be created when they shouldn't be. A user that deletes a parent resource, should be prevented from creating sub-resources, however with eventually consistency, this isn't always going to be the case in practice. It's easy to design a strategy to clean them up later, since you can't check all of them up front. So treat the parent resource as if it exists as well as the permissions when sub-resources are modified. When a parent resource is deleted, schedule an async action to take place sometime after the user's token expires. Then, at that moment clean up all resources and delete the relevant Authress access records that correspond to the resource that should no longer exist. This removes a burden on optimized caching, cache invalidation, excessive DB reads/writes, and allows for common async access patterns.
Advanced deletion scenarios
Why is it important to delete access records?
For Deletion an application must take special attention to handling updates and possibly removing permissions to a resource so that resource is freed for future usage. Access records are created when a resource is created. That means there is an access record that grants a user access to a specific resource. When that resource is deleted, that user technically will still have access to that resource.
In the case a user has access to
✶ all resources of a particular type, this isn't a problem. But what if a user only has access to one resource? Assume that resource is
resource/001. That corresponds to a real resource in your software. When the resources
001 is deleted, that doesn't affect the user's access. Consider what happens later when a second user comes and attempts to create a new resource. The new user will be granted
✶ to it. If that new resource is the same as the previously deleted resource
001 with the same identifier. Then also the old user will have
✶ to it as well.
This is known as sub-resource takeover.
To prevent sub-resource takeover, make sure to do at least one of the following:
- Prevent recreating resources - persist knowledge of deleted resources IDs and prevent recreation
- Ensure unique resource IDs - Prevent the generation of duplicate resource IDs by using UUIDs or some other secure random format.
- Delete existing Authress access records - On deletion of a resource, follow this article and make the associated calls to delete the relevant access record.
- Update user access on deletion - The easiest thing to do is to remove access for the user to the resource.
Implementing any of these strategies makes it easy to prevent sub-resource takeover, and Authress helps by further supporting the last two.