Skip to content

Commit 5626950

Browse files
authored
[ADR 022] Authorization (#394)
* Authorization ADR * Deep dive with worked examples
1 parent 2665de7 commit 5626950

File tree

3 files changed

+411
-0
lines changed

3 files changed

+411
-0
lines changed

custom-words.txt

+2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ AOSP
77
Bitwarden
88
bitwardensecret
99
bytemark
10+
Casbin
1011
clickjacking
1112
CODEOWNERS
1213
CQRS
1314
CTAP2
1415
dockerized
16+
dotfile
1517
F-Droid
1618
Gitter
1719
HKDF
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
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

Comments
 (0)