Skip to content

Commit a909f80

Browse files
addaleaxalexbevinirinchev
authored
MONGOSH-1906 Add OIDC workforce auth requirements docs (#1731)
Co-authored-by: Alex Bevilacqua <[email protected]> Co-authored-by: Nikola Irinchev <[email protected]>
1 parent 44cde52 commit a909f80

File tree

3 files changed

+277
-0
lines changed

3 files changed

+277
-0
lines changed

.github/CODEOWNERS

+3
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,8 @@
4646
# URI Options
4747
/source/uri-options/ @mongodb/dbx-spec-owners-uri-options
4848

49+
# Workforce OIDC
50+
/docs/workforce-human-oidc-auth.md @mongodb/dbx-spec-owners-workforce-oidc
51+
4952
# Workflows and code ownership file
5053
/.github/ @mongodb/dbx-leadership

docs/README.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# MongoDB Non-Drivers Specifications
2+
3+
Supporting material for various MongoDB Drivers Specifications lives here.
4+
5+
## Authentication
6+
7+
- [Requirements for Client Applications implementing Workforce (Human) OIDC Auth For MongoDB](./workforce-human-oidc-auth.md)

docs/workforce-human-oidc-auth.md

+267
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
# Requirements for Client Applications implementing Workforce (Human) OIDC Auth For MongoDB
2+
3+
# Overview
4+
5+
## Abstract
6+
7+
MongoDB offers OpenID Connect (OIDC) authentication and authorization for database users. OIDC auth in clients generally
8+
falls into one of two categories: either Workflow OIDC targeting programmatic users, which is
9+
[fully specified here](https://github.com/mongodb/specifications/blob/master/source/auth/auth.md#mongodb-oidc) and does
10+
not involve user interaction, or Workforce OIDC targeting human users, which authenticate explicitly through means such
11+
as browsers.
12+
13+
## Audience
14+
15+
This document is intended for authors and maintainers of MongoDB client applications that implement Workforce OIDC
16+
authentication, or those who wish to understand existing implementations of it. This document does *not* describe the
17+
server implementation of OIDC, nor the details of OIDC protocol interactions that are part of external standards and
18+
generally implemented through the use of a third-party library.
19+
20+
## Meta
21+
22+
The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and
23+
"OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).
24+
25+
## Token acquisition flows
26+
27+
Currently, all workforce OIDC clients in MongoDB support one or two mechanisms for acquiring OIDC tokens:
28+
29+
- Authorization Code Flow with PKCE (short: Auth Code, PKCE is pronounced “pixie”)
30+
- Device Authorization Grant (short: Device Auth)
31+
32+
Auth Code Flow is generally the preferred mechanism. If an application decides to only support one of these flows, that
33+
should be the typical choice. If an application supports both flows, it MAY default to Auth Code Flow and fall back to
34+
Device Auth only if
35+
36+
- The former is unavailable under specific circumstances, which are described in the Auth Code Flow section; and
37+
- The user has explicitly indicated that Device Auth is enabled for this connection attempt.
38+
39+
In both of these cases, the general chain of events is as follows:
40+
41+
1. The user passes a connection string to the client application and indicates (possibly through said connection string)
42+
that they desire to use `MONGODB-OIDC` as their authentication mechanism.
43+
2. The application connects to the MongoDB endpoint and asks it for information about the Identity Provider (IdP)[^1] it
44+
is supposed to authenticate with. If there was a username in the connection string, the application sends that as
45+
well.
46+
3. The MongoDB endpoint responds with information about the IdP, in the form of an `issuer` URL identifying the IdP and
47+
other metadata about it.
48+
4. The application looks up the public metadata document for the IdP, which includes information about user-facing URL
49+
endpoints.
50+
5. The application composes a URL based on those endpoints and presents it to the user in some form (the details depend
51+
on the exact flow chosen).
52+
6. The user visits that URL, which can result in any number of steps to authenticate the user.
53+
7. The application receives a token from the IdP (the details again depend on the exact flow chosen).
54+
8. The application presents this token to the MongoDB endpoint.
55+
56+
Since in these steps the client application performs actions depending on information supplied by external sources, this
57+
document specifies mechanisms that help ensure that these actions are carried out securely. MongoDB clients are “public
58+
clients” in the sense of the OIDC specs (i.e. they do not possess a shared secret with the Identity Provider), and must
59+
behave accordingly.
60+
61+
Since these steps require the involvement of a human being, the application should be set up to gracefully handle
62+
timeouts and provide the user with a way to abort the connection attempt.
63+
64+
# General requirements
65+
66+
An application implementing workforce OIDC needs to generally comply with OpenID and OAuth 2.0 standards.
67+
68+
In particular:
69+
70+
- Any HTTP calls made to non-local servers MUST be made using HTTPS.
71+
- IdP metadata discovery MUST be implemented as described in [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414).
72+
73+
Using a well-tested and standards-compliant third-party library for core OIDC logic for the respective ecosystem is
74+
highly recommended. If this is not possible, implementers need to pay special attention to the specifications referenced
75+
in this document.
76+
77+
## Token management
78+
79+
After a successful authentication, applications SHOULD periodically attempt to use the OIDC token refresh mechanism in
80+
order to exchange the access token it uses for a fresh one. The refresh interval SHOULD be determined by the token
81+
expiry time indicated by the Identity Provider. If the refresh attempt fails, the application MAY ask or suggest the
82+
user to re-authenticate from scratch, even if the currently used access token has not expired yet.
83+
84+
Under normal circumstances, the application provides an access token received from the Identity Provider to the driver.
85+
The application SHOULD provide a toggle to users that can be used to indicate that it should use the ID token, rather
86+
than the access token, in its place.[^2]
87+
88+
## OIDC Scopes
89+
90+
The initial request to the IdP involves specifying the `scope` parameter. This is a space-separated unordered list of
91+
strings which contains:
92+
93+
- The scopes listed in the `requestScopes` field of the IdP response from the MongoDB endpoint, and
94+
- The `openid` and `offline_access` scopes[^3]. If the IdP metadata document contains a `scopes_supported` field, and
95+
this field does not list one of these scopes, then the respective scope SHOULD NOT be added to the `scope`
96+
parameter.
97+
98+
# Authorization Code Flow
99+
100+
In this flow, the client application uses a local HTTP server to receive a response from the Identity Provider, as
101+
described in [RFC8252](https://datatracker.ietf.org/doc/html/rfc8252).[^4] The application follows these steps:
102+
103+
1. Generate a code challenge for PKCE using cryptographically random data, as described in
104+
[RFC7636](https://datatracker.ietf.org/doc/html/rfc7636).
105+
2. Launch a local HTTP server. The default (incoming) redirect URL for MongoDB applications is
106+
`http://localhost:27097/redirect`, which MAY be configurable. If the application allows configuring the URL, the
107+
port MAY be specified as `0` to allow listening on an arbitrary port. The application listens on the host and port
108+
listed in the URL. The application MUST listen on all addresses that the hostname resolves to through
109+
`getaddrinfo()`, and MUST listen on the same port in all cases. If listening on any address fails, or
110+
`getaddrinfo()` did not return any addresses, it must abort this process and the application MAY fall back to
111+
Device Auth.
112+
1. To preserve privacy, pages served from this local HTTP server MUST NOT reference external resources directly.
113+
They MAY provide external links that require user interaction.
114+
2. The HTTP server SHOULD set default headers of `Content-Security-Policy: default-src 'self'` and
115+
`Referrer-Policy: no-referrer`, or similarly restrictive defaults.
116+
3. Compose the URL for Auth Code Flow, using the code challenge generated earlier and the redirect URL from the
117+
previous step, with the port replaced with the actual port if it was specified as `0`.
118+
1. The application MUST add a `state` parameter containing cryptographically random data
119+
([RFC6749](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1) specifies this as optional but
120+
recommended).
121+
2. The application SHOULD allow the user to indicate that it should add a `nonce` parameter containing
122+
cryptographically random data to the authentication request, as defined in the OpenID Connect specification,
123+
which is then later embedded in the ID token itself. This option SHOULD be on-by-default. (Some Identity
124+
Providers may effectively require a `nonce` option, but since it is an OpenID Connect feature and not an OAuth
125+
2.0 feature, we allow users to disable this if their Identity Provider does not support it.)
126+
4. The application SHOULD, instead of directly presenting the URL from the previous step to the user, register that URL
127+
as an outgoing HTTP 307 redirect from the local HTTP server. The redirect base URL SHOULD contain a piece of
128+
cryptographically random data. The advantages of this approach are that:
129+
1. The application can ensure that the URL that is passed to the browser in the next step does not contain special
130+
characters (other than the typical `/` and `:` present in URLs) which would need to be escaped when opening a
131+
browser through a shell command.
132+
2. It allows tracking whether the URL has been accessed, i.e. get a definite confirmation of whether the user
133+
successfully opened it in a browser.
134+
5. Open a browser pointing at that URL, or instruct the user to do so. The former approach is preferred. If opening a
135+
browser fails, the application MAY fall back to Device Auth.
136+
6. Wait until one of the following events occurs:
137+
1. The local outgoing redirect URL is accessed (if any).
138+
2. Opening the browser fails.
139+
3. A timeout occurs or the connection attempt is aborted.
140+
7. Wait until the incoming redirect URL is accessed in a valid way, i.e. using GET or POST with the correct `state`
141+
parameter and without an `error` parameter. If the incoming redirect URL is accessed with a mismatching `state`
142+
parameter or the `error` parameter is set, the application SHOULD display a helpful error page. If an `iss`
143+
parameter is present, it MUST be validated according to [RFC9207](https://datatracker.ietf.org/doc/html/rfc9207),
144+
i.e. compare equal to the `issuer` string in the issuer metadata document. Subsequent requests to the incoming
145+
redirect URL should be rejected.
146+
1. If error parameters such as `error`, `error_description` and/or `error_uri` are displayed to the user, they MUST
147+
be validated to match the `NQSCHAR` definition of [RFC6749](https://datatracker.ietf.org/doc/html/rfc6749) (and
148+
in the case of `error_uri`, a valid URI) before doing so, and they MUST be escaped as appropriate for the
149+
chosen display format.
150+
8. The application MUST redirect from the incoming redirect URL to a different URL on the same local HTTP server as a
151+
HTTP 303 redirect, removing all query parameters and/or the request body. The target page SHOULD indicate
152+
successful authentication to the Identity Provider, and SHOULD clarify that the user is not authenticated with the
153+
MongoDB endpoint yet. It MAY be updated once the application successfully authenticates against the MongoDB
154+
endpoint to reflect that.
155+
9. Stop listening on and close the local HTTP server. As browsers may keep lingering open connections, the application
156+
SHOULD ensure that these connections do not prevent the application from progressing to the next step (i.e. do not
157+
block closing the server).
158+
10. Verify the parameters it received against the initial PKCE code challenge.
159+
11. Perform a request to the OIDC token endpoint using the code received from the IdP on the incoming redirect URL.
160+
12. Provide the token it received from the token endpoint to the MongoDB driver.
161+
162+
The developer tools team owns example templates for the local HTTP server at
163+
[https://github.com/mongodb-js/devtools-shared/tree/main/packages/oidc-http-server-pages](https://github.com/mongodb-js/devtools-shared/tree/main/packages/oidc-http-server-pages),
164+
and publishes templates that can be accessed at e.g.
165+
[https://unpkg.com/@mongodb-js/oidc-http-server-pages/dist/templates.gz](https://unpkg.com/@mongodb-js/oidc-http-server-pages/dist/templates.gz).
166+
167+
# Device Auth Grant
168+
169+
This flow is optional, and MUST NOT be used unless either:
170+
171+
- The application has been unable to open a browser locally (e.g. in headless environments), or unable to listen on a
172+
local HTTP endpoint, and the user has explicitly indicated that falling back to this flow is acceptable; or
173+
- The user has explicitly requested this flow.
174+
175+
It is significantly simpler than the Auth Code Flow, but generally considered the less secure one between the two, and
176+
more susceptible to e.g. phishing attacks.
177+
178+
The application follows these steps:
179+
180+
1. Reach out to the IdP device authorization endpoint. This should result in a URL and a “user code”.
181+
2. Present the URL and the user code to the user, expecting the user to open that URL manually (possibly on a different
182+
device).
183+
3. Repeatedly poll the token endpoint for an access token.
184+
4. Provide the token it received from the token endpoint to the MongoDB driver.
185+
186+
# Diagnostics
187+
188+
It is recommended to log the following events for diagnostic purposes:
189+
190+
- All outgoing HTTP calls, minus sensitive information (e.g. URLs, with search parameter values redacted)
191+
- All inbound HTTP calls, minus sensitive information
192+
- Starting to listen on the local HTTP server, including port and address(es), and whether that was successful or not
193+
- Incoming redirect to the local HTTP server accessed and whether the redirect was accepted or rejected
194+
- Closing of the local HTTP server
195+
- Browser opening and whether it were successful or not
196+
- Acquisition of new tokens from the Identity Provider, and in particular whether an ID token was present or not
197+
- Token refresh attempts and whether they were successful or not
198+
199+
# Appendix: Relevant standards (non-exhaustive)
200+
201+
- [RFC6749](https://datatracker.ietf.org/doc/html/rfc6749): The OAuth 2.0 Authorization Framework
202+
- [RFC6819](https://datatracker.ietf.org/doc/html/rfc6819): OAuth 2.0 Threat Model and Security Considerations
203+
- [RFC7636](https://datatracker.ietf.org/doc/html/rfc7636): Proof Key for Code Exchange by OAuth Public Clients (PKCE)
204+
- [RFC8252](https://datatracker.ietf.org/doc/html/rfc8252): OAuth 2.0 for Native Apps
205+
- [RFC8414](https://datatracker.ietf.org/doc/html/rfc8414): OAuth 2.0 Authorization Server Metadata
206+
- [RFC8628](https://datatracker.ietf.org/doc/html/rfc8628): OAuth 2.0 Device Authorization Grant
207+
- [RFC8707](https://datatracker.ietf.org/doc/html/rfc8707): Resource Indicators for OAuth 2.0
208+
- [RFC9207](https://datatracker.ietf.org/doc/html/rfc9207): OAuth 2.0 Authorization Server Issuer Identification
209+
- [RFC9449](https://datatracker.ietf.org/doc/html/rfc9449): OAuth 2.0 Demonstrating Proof of Possession (DPoP)
210+
- [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)
211+
- [Draft](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics): OAuth 2.0 Security Best Current
212+
Practice
213+
- [Risk of phishing Access Tokens from clients using OIDC Authentication](https://docs.google.com/document/d/1TdcBtRu4yNXQkI7ZdKWZlSIaWs29tIQblyS3805nK1A/edit?tab=t.0)
214+
215+
# Appendix A: Multiple MongoClients
216+
217+
Some applications may require support for multiple concurrent MongoClients using the same OIDC tokens. In this case, a
218+
token set (access token, ID token and refresh token) may be re-used, if and only if:
219+
220+
- The `username` parameter is equal
221+
- The IdP metadata provided by the MongoDB endpoint is equal
222+
- Both token sets contain an ID token and the respective `aud` and `sub` claims[^5] of the ID tokens are equal, OR
223+
neither token set contains an ID token
224+
225+
If an application supports multiple MongoClients, it MUST ensure that only one token acquisition flow is in use at the
226+
same time, and coordinate token refresh intervals accordingly.
227+
228+
The Developer Tools team maintains an implementation that integrates with multiple MongoClient instances at
229+
[https://github.com/mongodb-js/oidc-plugin](https://github.com/mongodb-js/oidc-plugin), which can be used as a reference
230+
implementation (and which can generally be used in other applications based on the Node.js driver, although as a
231+
standalone package it is not considered a supported product of MongoDB).
232+
233+
# Appendix B: Future intentions for endpoint restrictions
234+
235+
Currently, users who connect to a host other than localhost or an Atlas hostname need to specify this host in the
236+
`ALLOWED_HOSTS` auth mechanism property. In the future, MongoDB is hoping to support Demonstrating Proof of Possession
237+
(DPoP, [RFC9449](https://datatracker.ietf.org/doc/html/rfc9449)) which will allow lifting this restriction. The goal
238+
here of either of these mechanisms is to prevent users from connecting to untrusted endpoints that could advertise
239+
attacker-controlled IdP metadata and intercept tokens intended for other clusters (or even non-MongoDB OIDC
240+
applications).
241+
242+
We would also like to adopt [RFC8707](https://datatracker.ietf.org/doc/html/rfc8707), but have not decided on a specific
243+
format for expressing MongoDB clusters as resource URLs.
244+
245+
## Changelog
246+
247+
- 2024-11-14: Initial version.
248+
249+
[^1]: Technically, this refers to an Authorization Server (AS). Inside MongoDB, the usage of AS and IdP has been
250+
considered more or less interchangeable.
251+
252+
[^2]: At the time of writing, there are Identity Providers which do not support JWT access tokens, but which do provide
253+
JWT ID tokens that are usable for authentication. This requires opt-in on both the server configuration side and the
254+
client side.
255+
256+
[^3]: The `offline_access` scope is what provides the ability to refresh tokens without performing a full
257+
re-authentication. Some particularly security-sensitive customers may choose this approach at the expense of a
258+
greatly diminished UX, where users need to frequently re-authenticate.
259+
260+
[^4]: [RFC8252](https://datatracker.ietf.org/doc/html/rfc8252) lays out alternatives to this approach, such as registering
261+
a custom URI scheme handler or registering “Claims” on specific HTTPS URLs, but we’ve decided not to pursue these
262+
approaches in any existing applications, since some of our applications are CLI applications without an installation
263+
step where neither of these alternative approaches is feasible.
264+
265+
[^5]: OIDC as a protocol allows for `aud` claims that contain multiple strings, instead of a single one. MongoDB endpoints
266+
are [restricted](https://jira.mongodb.org/browse/SERVER-86607) to single-value `aud` claims, although client
267+
applications do not need to be concerned with this restriction.

0 commit comments

Comments
 (0)