|
| 1 | +--- |
| 2 | +adr: "0022" |
| 3 | +status: Accepted |
| 4 | +date: 2024-07-22 |
| 5 | +tags: [server] |
| 6 | +--- |
| 7 | + |
| 8 | +# 0022 - Authorization |
| 9 | + |
| 10 | +<AdrTable frontMatter={frontMatter}></AdrTable> |
| 11 | + |
| 12 | +## Context and Problem Statement |
| 13 | + |
| 14 | +Authorization logic decides whether a user is permitted to carry out an action. Our current |
| 15 | +authorization logic is dispersed throughout different layers of our server codebase and follows |
| 16 | +different patterns, some of which are no longer suitable for our changing permissions structures. |
| 17 | + |
| 18 | +We should decide on a single, consistent solution to how we manage authorization in our |
| 19 | +applications. This is primarily concerned with the main Bitwarden server and client code (including |
| 20 | +Password Manager, Admin Console and Provider Portal) but could be extended to other products. |
| 21 | + |
| 22 | +### Terminology |
| 23 | + |
| 24 | +- **Authentication** is the process of verifying a user's identity. ("Who are you?") It tells you |
| 25 | + who they are, but not what they can do. |
| 26 | +- **Authorization** is the process of determining who can access or modify a resource. ("Are you |
| 27 | + allowed to do this?") It may or may not require authentication. |
| 28 | +- **Validation** is the process of determining whether a request is valid according to business |
| 29 | + logic. ("Can this be done?") Unlike authorization, it usually does not depend on the user's |
| 30 | + identity (authentication) or permissions (authorization). For example, a vault item cannot be |
| 31 | + restored unless it has first been (soft) deleted. |
| 32 | +- **Resources** are data on the server that a user may try to access or modify - such as a vault |
| 33 | + item, organization, collection, or group. |
| 34 | +- **User verification** is a Bitwarden-specific UI flow aimed at re-establishing a user's identity |
| 35 | + before they perform a sensitive action. e.g. entering your password again before viewing an api |
| 36 | + key. This is an aspect of authentication, because it relates to verifying the user's identity. |
| 37 | + |
| 38 | +### Current patterns |
| 39 | + |
| 40 | +To date, we have broadly used the following authorization patterns: |
| 41 | + |
| 42 | +- In the individual user context, we match the user ID in the JWT to the resource being accessed. |
| 43 | + This is usually a one-to-one match (e.g. the JWT user ID should match the cipher user ID) without |
| 44 | + additional requirements. |
| 45 | +- In organizational contexts, we use role-based authorization, where the user is assigned a role in |
| 46 | + an organization and permission is granted or denied based on that role. This closely tracks |
| 47 | + [how access control is presented to the user](https://bitwarden.com/help/user-types-access-control/). |
| 48 | + However, the expansion of collections has complicated this simple model, and it now looks more |
| 49 | + like |
| 50 | + [attribute-based access control](https://en.wikipedia.org/wiki/Attribute-based_access_control). |
| 51 | + |
| 52 | +This logic is spread throughout controllers in the API layer, JWT claims accessed via |
| 53 | +`CurrentContext`, the core service layer, and database queries. |
| 54 | + |
| 55 | +### Requirements |
| 56 | + |
| 57 | +Our authorization requirements have increased in complexity, particularly with the release of |
| 58 | +collection management enhancements. Today, the outcome of an authorization decision relevant to an |
| 59 | +organization resource could depend on the user's role, their collection relationships, organization |
| 60 | +settings, provider relationships, and other factors. |
| 61 | + |
| 62 | +A solution to this problem should: |
| 63 | + |
| 64 | +- Separate authorization logic from other concerns. |
| 65 | +- Centralize authorization logic in a single location or pattern as much as possible. |
| 66 | +- Separate authorization logic from authorization checks/enforcement. |
| 67 | +- Be reusable between endpoints that access the same resource. |
| 68 | +- Support a range of authorization logic (e.g. based on role, resource, relationships, etc.). |
| 69 | + |
| 70 | +## Considered Options |
| 71 | + |
| 72 | +### ASP.NET Core resource-based authorization |
| 73 | + |
| 74 | +#### Summary |
| 75 | + |
| 76 | +[Resource-based authorization](https://learn.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased?view=aspnetcore-8.0) |
| 77 | +makes authorization decisions based on the **resource** being accessed, the **user** accessing the |
| 78 | +resource, and the **operations** (actions) they wish to take on the resource. |
| 79 | + |
| 80 | +To define authorization logic, developers implement one or more `IAuthorizationHandler` classes for |
| 81 | +the resource being accessed. |
| 82 | + |
| 83 | +Authorization checks are handled by the default implementation of `IAuthorizationService`, which |
| 84 | +calls each authorization handler for the resource to find at least 1 that will authorize the action. |
| 85 | + |
| 86 | +#### Advantages |
| 87 | + |
| 88 | +- Included in ASP.NET - standard C# code, no additional dependencies. |
| 89 | +- Already used by Secrets Manager and for some collection operations in Password Manager. |
| 90 | +- Handlers provide good encapsulation of authorization logic, separate to other concerns and the |
| 91 | + `AuthorizationService` implementation itself. |
| 92 | +- The fixed `AuthorizationService` interface enforces a consistent usage across our teams. |
| 93 | +- Teams can write and have code ownership over their own authorization handlers. |
| 94 | +- Flexibly supports additional sources of authorization (e.g. scoped user API keys) by defining |
| 95 | + additional handlers. |
| 96 | + |
| 97 | +#### Disadvantages |
| 98 | + |
| 99 | +- The interface works well for specific resources, but less well for bulk read operations (e.g. |
| 100 | + reading all items the user has access to). Bulk read operations are likely to duplicate some of |
| 101 | + the authorization logic in another class or database query. |
| 102 | +- Server-side solution only - client code needs to maintain its own logic to enable/disable UI flows |
| 103 | + (although given the offline requirements of our clients, this is difficult to avoid). |
| 104 | +- We need to be mindful of database calls within authorization handlers, as they may be called in a |
| 105 | + loop for each resource being authorized. |
| 106 | + |
| 107 | +### Third-party solution |
| 108 | + |
| 109 | +Including but not limited to: |
| 110 | + |
| 111 | +- [OpenFGA](https://openfga.dev), an open-source implementation of Google's |
| 112 | + [Zanzibar authorization system](https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/) |
| 113 | + (used in Google Docs). |
| 114 | +- [Casbin](https://casbin.org/), an open-source authorization library supporting a variety of |
| 115 | + different models. |
| 116 | + |
| 117 | +Each has its own domain-specific language (DSL) used to define authorization rules as well as a |
| 118 | +storage layer to store access information about the users and objects in the application. The |
| 119 | +storage is generally a separate database to the Bitwarden database and must be kept up-to-date with |
| 120 | +users' roles and relationships in Bitwarden. |
| 121 | + |
| 122 | +#### Advantages |
| 123 | + |
| 124 | +- The DSLs appear to be a flexible and robust way to define an authorization model. |
| 125 | +- Support for complex permission structures. |
| 126 | +- Reduces load on the Bitwarden database, designed for high performance. |
| 127 | +- Single source of truth, strongly separated from our application logic. |
| 128 | + |
| 129 | +#### Disadvantages |
| 130 | + |
| 131 | +- Vendor lock-in for a core part of our architecture. |
| 132 | +- New concepts and a DSL unfamiliar to most developers - would probably require a single team to |
| 133 | + become experts in this and maintain it for all teams, which does not work well with our team |
| 134 | + structure. |
| 135 | +- Very costly to implement in terms of engineering resources, particularly for developers, BRE and |
| 136 | + SRE. |
| 137 | +- Risk of the authorization data store becoming out of step with the main Bitwarden database. |
| 138 | +- May be over-engineered for our current requirements and scale. |
| 139 | + |
| 140 | +### Custom solution |
| 141 | + |
| 142 | +We could develop our own custom solution from scratch, however we have not identified any clear |
| 143 | +advantage over choosing an existing solution above. |
| 144 | + |
| 145 | +## Decision Outcome |
| 146 | + |
| 147 | +Chosen option: **ASP.NET Core resource-based authorization**. |
| 148 | + |
| 149 | +### Positive Consequences |
| 150 | + |
| 151 | +- Lowest cost/effort implementation. |
| 152 | +- Fits team structure. |
| 153 | +- Existing code can be refactored incrementally. |
| 154 | + |
| 155 | +### Negative Consequences |
| 156 | + |
| 157 | +- Some duplication of read logic between discrete reads and bulk reads. |
| 158 | + |
| 159 | +### Plan |
| 160 | + |
| 161 | +- See the [Authorization deep dive](../deep-dives/authorization.md) for implementation details and |
| 162 | + examples. |
0 commit comments