Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRIVERS-2836 OIDC Spec Cleanup #1551

Merged
merged 31 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6e09e49
DRIVERS-2836 Only invalidate cached OIDC access tokens if the server …
blink1073 Mar 19, 2024
ed669eb
regenerate json
blink1073 Mar 19, 2024
a25518c
restore comment
blink1073 Mar 19, 2024
06e0110
add spec test for Refresh Token Is Passed To The Callback
blink1073 Mar 20, 2024
fd9481a
Merge branch 'master' of github.com:mongodb/specifications into DRIVE…
blink1073 Mar 21, 2024
739ae61
address review
blink1073 Mar 21, 2024
6ed2c94
Update source/auth/auth.md
blink1073 Apr 3, 2024
cef1ad2
Merge branch 'master' of github.com:mongodb/specifications into DRIVE…
blink1073 Apr 3, 2024
7ae7fea
Merge branch 'DRIVERS-2836' of github.com:blink1073/specifications in…
blink1073 Apr 3, 2024
958148e
Update source/auth/tests/mongodb-oidc.md
blink1073 Apr 3, 2024
83952e7
OIDC spec updates
blink1073 Apr 8, 2024
9027606
Merge branch 'master' of github.com:mongodb/specifications into DRIVE…
blink1073 Apr 8, 2024
3d98b29
Merge branch 'DRIVERS-2836' of github.com:blink1073/specifications in…
blink1073 Apr 8, 2024
b4e7905
update auth logic
blink1073 Apr 9, 2024
6815eb4
update error handling
blink1073 Apr 9, 2024
5967353
Update source/auth/auth.md
blink1073 Apr 10, 2024
3242bea
Update source/auth/auth.md
blink1073 Apr 10, 2024
ddd4054
Update source/auth/auth.md
blink1073 Apr 10, 2024
c263ad2
Update source/auth/auth.md
blink1073 Apr 10, 2024
119fce1
address review
blink1073 Apr 10, 2024
0f6f3bc
Update source/auth/tests/mongodb-oidc.md
blink1073 Apr 11, 2024
94ef18d
fix connection string tests
blink1073 Apr 11, 2024
13dc54c
clarify test conditions
blink1073 Apr 16, 2024
134c1f4
clarify spec test
blink1073 Apr 17, 2024
f24a075
fix SASL conversation
blink1073 Apr 19, 2024
5f63fea
fix grammar
blink1073 Apr 19, 2024
9e268e7
convert two unified tests to prose tests
blink1073 Apr 22, 2024
8cef9de
Update source/auth/tests/mongodb-oidc.md
blink1073 Apr 22, 2024
8471762
Update source/auth/tests/mongodb-oidc.md
blink1073 Apr 22, 2024
1716f23
Merge branch 'master' into DRIVERS-2836
blink1073 Apr 22, 2024
531753e
lint
blink1073 Apr 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 32 additions & 26 deletions source/auth/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Drivers SHOULD contain a type called `MongoCredential`. It SHOULD contain some o
- username (string)

- Applies to all mechanisms.
- Optional for MONGODB-X509 and MONGODB-AWS.
- Optional for MONGODB-X509, MONGODB-AWS, and MONGODB-OIDC.

- source (string)

Expand Down Expand Up @@ -1222,8 +1222,7 @@ in the MONGODB-OIDC specification, including sections or blocks that specificall
the same `MongoClient`, the driver MUST raise an error.

- TOKEN_RESOURCE\
The URI of the target resource. This property is currently only used and required by the Azure
built-in OIDC provider integration. If `TOKEN_RESOURCE` is provided and `ENVIRONMENT` is not one of
The URI of the target resource. If `TOKEN_RESOURCE` is provided and `ENVIRONMENT` is not one of
`["azure", "gcp"]` or `TOKEN_RESOURCE` is not provided and `ENVIRONMENT` is one of `["azure", "gcp"]`, the driver
MUST raise an error.

Expand Down Expand Up @@ -1265,9 +1264,9 @@ purposes, and is not meant to be documented as a user-facing feature.

If enabled, drivers MUST generate a token using a script in the `auth_oidc`
[folder](https://github.com/mongodb-labs/drivers-evergreen-tools/tree/master/.evergreen/auth_oidc#readme) in Drivers
Evergreen Tools. The must then set the `OIDC_TOKEN_FILE` environment variable to the path to that file. At runtime, the
driver MUST use the `OIDC_TOKEN_FILE` environment variable and read the OIDC access token from that path. The driver
MUST use the contents of that file as value in the `jwt` field of the `saslStart` payload.
Evergreen Tools. The driver MUST then set the `OIDC_TOKEN_FILE` environment variable to the path to that file. At
runtime, the driver MUST use the `OIDC_TOKEN_FILE` environment variable and read the OIDC access token from that path.
The driver MUST use the contents of that file as value in the `jwt` field of the `saslStart` payload.

Drivers MAY implement the "test" integration so that it conforms to the function signature of the
[OIDC Callback](#oidc-callback) to prevent having to re-implement the "test" integration logic in the OIDC prose tests.
Expand Down Expand Up @@ -1426,10 +1425,10 @@ The driver MUST pass the following information to the callback:
The callback MUST be able to return the following information:

- `accessToken`: An OIDC access token string. The driver MUST NOT attempt to validate `accessToken` directly.
- `expiresIn`: An optional expiry duration for the access token. Drivers MUST interpret the value 0 as an infinite
duration and error if a negative value is returned. Drivers SHOULD use the most idiomatic type for representing a
duration in the driver's language. Note that the access token expiry value is currently not used in
[Credential Caching](#credential-caching), but is intended to support future caching optimizations.
- `expiresIn`: An optional expiry duration for the access token. Drivers with optional parameters MAY interpret a
missing value as infinite. Drivers MUST error if a negative value is returned. Drivers SHOULD use the most idiomatic
type for representing a duration in the driver's language. Note that the access token expiry value is currently not
used in [Credential Caching](#credential-caching), but is intended to support future caching optimizations.

The signature and naming of the callback API is up to the driver's discretion. Drivers MUST ensure that additional
optional input parameters and return values can be added to the callback signature in the future without breaking
Expand Down Expand Up @@ -1483,7 +1482,7 @@ An example human callback API might look like:
```typescript
interface IdpInfo {
issuer: string;
clientId: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the intended behavior when clientId is None? The code I have written on client side assumes the clientId will always exist.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be None if the user used a Human Callback and the IdP is configured on the server with supportsHumanFlows:False. They shouldn't really be doing that, but it is allowed. See https://jira.mongodb.org/browse/DRIVERS-2773 for details.

clientId: Optional<string>;
requestScopes: Optional<Array<string>>;
}

Expand Down Expand Up @@ -1544,6 +1543,7 @@ An example OIDC one-step SASL conversation with access token string "abcd1234" l
{
saslStart: 1,
mechanism: "MONGODB-OIDC",
db: "$external"
// payload is a BSON generic binary field containing a JwtStepRequest BSON
// document: {"jwt": "abcd1234"}
payload: BinData(0, "FwAAAAJqd3QACQAAAGFiY2QxMjM0AAA=")
Expand Down Expand Up @@ -1618,6 +1618,7 @@ An example OIDC two-step SASL conversation with username "myidp" and access toke
{
saslStart: 1,
mechanism: "MONGODB-OIDC",
db: "$external",
// payload is a BSON generic binary field containing a PrincipalStepRequest
// BSON document: {"n": "myidp"}
payload: BinData(0, "EgAAAAJuAAYAAABteWlkcAAA")
Expand Down Expand Up @@ -1712,9 +1713,10 @@ Use the following algorithm to authenticate a new connection:

- Check if the the *Client Cache* has an access token.
katcharov marked this conversation as resolved.
Show resolved Hide resolved
- If it does, cache the access token in the *Connection Cache* and perform a `One-Step` SASL conversation using the
access token in the *Client Cache*. If the server returns an error, invalidate that access token, sleep 100ms then
continue.
- Call the configured built-in provider integration or the OIDC callback to retrieve a new access token.
access token in the *Client Cache*. If the server returns a Authentication error (18), invalidate that access token.
blink1073 marked this conversation as resolved.
Show resolved Hide resolved
Raise any other errors to the user. On success, exit the algorithm.
- Call the configured built-in provider integration or the OIDC callback to retrieve a new access token. Wait until it
has been at least 100ms since the last callback invocation, to avoid overloading the callback.
- Cache the new access token in the *Client Cache* and *Connection Cache*.
- Perform a `One-Step` SASL conversation using the new access token. Raise any errors to the user.

Expand All @@ -1725,18 +1727,19 @@ def auth(connection):
access_token, is_cache = get_access_token()

# If there is a cached access token, try to authenticate with it. If
# authentication fails, it's possible the cached access token is expired. In
# that case, invalidate the access token, fetch a new access token, and try
# authentication fails with an Authentication error (18),
# invalidate the access token, fetch a new access token, and try
# to authenticate again.
# If the server fails for any other reason, do not clear the cache.
if is_cache:
try:
connection.oidc_cache.access_token = access_token
sasl_start(connection, payload={"jwt": access_token})
return
except ServerError:
invalidate(access_token)
sleep(0.1)
access_token, _ = get_access_token()
except ServerError as e:
if e.code == 18:
invalidate(access_token)
access_token, _ = get_access_token()

connection.oidc_cache.access_token = access_token
sasl_start(connection, payload={"jwt": access_token})
Expand All @@ -1747,14 +1750,15 @@ authenticate a new connection when a [OIDC Human Callback](#oidc-human-callback)

- Check if the *Client Cache* has an access token.
- If it does, cache the access token in the *Connection Cache* and perform a [One-Step](#one-step) SASL conversation
using the access token. If the server returns an error, invalidate the access token token from the *Client Cache*,
clear the *Connection Cache*, and continue.
using the access token. If the server returns an Authentication error (18), invalidate the access token token from
the *Client Cache*, clear the *Connection Cache*, and restart the authentication flow. Raise any other errors to the
user. On success, exit the algorithm.
- Check if the *Client Cache* has a refresh token.
- If it does, call the [OIDC Human Callback](#oidc-human-callback) with the cached refresh token and `IdpInfo` to get
a new access token. Cache the new access token in the *Client Cache* and *Connection Cache*. Perform a
[One-Step](#one-step) SASL conversation using the new access token. If the
[OIDC Human Callback](#oidc-human-callback) or the server return an error, invalidate the access token from the
*Client Cache*, clear the *Connection Cache*, and continue.
[One-Step](#one-step) SASL conversation using the new access token. If the the server returns an Authentication
error (18), clear the refresh token, invalidate the access token from the *Client Cache*, clear the *Connection
Cache*, and restart the authentication flow. Raise any other errors to the user. On success, exit the algorithm.
- Start a new [Two-Step](#two-step) SASL conversation.
- Run a `PrincipalStepRequest` to get the `IdpInfo`.
- Call the [OIDC Human Callback](#oidc-human-callback) with the new `IdpInfo` to get a new access token and optional
Expand All @@ -1776,7 +1780,7 @@ Use the following algorithm to perform speculative authentication:
- If it does, cache the access token in the *Connection Cache* and send a `JwtStepRequest` with the cached access
token in the speculative authentication SASL payload. If the response is missing a speculative authentication
document or the speculative authentication document indicates authentication was not successful, clear the the
*Connection Cache* and continue.
*Connection Cache* and proceed to the next step.
- Authenticate with the standard authentication handshake.

Example code for speculative authentication using the `auth` function described above:
Expand Down Expand Up @@ -2053,6 +2057,8 @@ to EC2 instance metadata in ECS, for security reasons, Amazon states it's best p

## Changelog

- 2024-04-22: Updated OIDC authentication flow and prose tests.

- 2024-04-22: Clarify that driver should not validate `saslSupportedMechs` content.

- 2024-04-03: Added GCP built-in OIDC provider integration.
Expand Down
4 changes: 2 additions & 2 deletions source/auth/tests/legacy/connection-string.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion source/auth/tests/legacy/connection-string.yml
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ tests:
uri: mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=ENVIRONMENT:test
valid: false
credential:
- description: should throw an exception if username is specified for aws (MONGODB-OIDC)
- description: should throw an exception if username is specified for test (MONGODB-OIDC)
uri: mongodb://principalName@localhost/?authMechanism=MONGODB-OIDC&ENVIRONMENT:test
valid: false
credential:
Expand Down
Loading
Loading