From 671b4c612bc365ca19823db4a4ed18ff1afeea81 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 4 Sep 2023 13:28:18 -0500 Subject: [PATCH 1/9] DRIVERS-2616 OIDC-SASL Follow-Up --- source/auth/tests/mongodb-oidc.rst | 334 ++++++----------------------- 1 file changed, 64 insertions(+), 270 deletions(-) diff --git a/source/auth/tests/mongodb-oidc.rst b/source/auth/tests/mongodb-oidc.rst index 724841e0e0..f6496210c3 100644 --- a/source/auth/tests/mongodb-oidc.rst +++ b/source/auth/tests/mongodb-oidc.rst @@ -5,31 +5,38 @@ MongoDB OIDC Drivers MUST test the following scenarios: - ``Callback-Driven Auth`` -- ``AWS Automatic Auth`` - ``Callback Validation`` -- ``Cached Credentials`` - ``Speculative Authentication`` - ``Reauthentication`` +- ``Separate Connections Avoid Extra Callback Calls`` .. sectnum:: -Drivers MUST be able to authenticate using either authentication or provider -name if there are multiple principals configured on the server. Note that -``directConnection=true`` and ``readPreference=secondaryPreferred`` are needed because the server is a secondary on a replica set, on port ``27018``. +Drivers MUST be able to authenticate against a server configured with either one or two configured identity providers. + +Note that typically the preconfigured Atlas Dev clusters are used for testing. The URIs can be fetched +from the ``drivers/oidc`` Secrets vault, see vault instructions . Use ``OIDC_ATLAS_URI_SINGLE`` for ``MONGODB_URI_SINGLE`` and +``OIDC_ATLAS_URI_MULTI`` for ``OIDC_ATLAS_URI_MULTI``. + +If using local servers is preferred, using the ``drivers-evergreen-tools`` Local Testing method , +use ``mongodb://localhost/?authMechanism=MONGODB-OIDC`` for ``MONGODB_URI_SINGLE`` and +``mongodb://localhost:27018/?authMechanism=MONGODB-OIDC&directConnection=true&readPreference=secondaryPreferred`` +for ``MONGODB_URI_MULTI`` because the other server is a secondary on a replica set, on port ``27018``. + +The driver MUST generate valid local tokens at the location given by ``OIDC_TOKEN_DIR``, see ``drivers-evergreen-tools`` for example . +. Drivers will need to be able to internally query and clear the cached credentials to verify usage for testing purposes. Clearing the cache means removing all data from the cache, including ``OIDCMechanismServerStep1`` information. -Drivers MUST set the ``AWS_WEB_IDENTITY_TOKEN_FILE`` environment variable -to the location of valid ``test_user1`` credentials at the beginning of each -test, unless otherwise specified. - -Unless otherwise specified, tests will use a URL -of the form ``mongodb://localhost/?authMechanism=MONGODB-OIDC``. +The default OIDC client used in the tests will be configured with ``MONGODB_URI_SINGLE`` and a valid request callback handler +that returns the ``test_user1`` local token. +https://wiki.corp.mongodb.com/display/DRIVERS/Using+AWS+Secrets+Manager+to+Store+Testing+Secrets +https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/auth_oidc/README.md Callback-Driven Auth ==================== @@ -39,149 +46,65 @@ is one principal configured. Single Principal Implicit Username ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create a request callback returns a valid token. -- Create a client that uses the default OIDC url and the request callback. +- Create default OIDC client. - Perform a ``find`` operation. that succeeds. - Close the client. Single Principal Explicit Username ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create a request callback that returns a valid token. -- Create a client with a url of the form ``mongodb://test_user1@localhost/?authMechanism=MONGODB-OIDC`` and the OIDC request callback. +- Create a client with ``MONGODB_URI_SINGLE``, a username of ``test_user1``, and the OIDC request callback. - Perform a ``find`` operation that succeeds. - Close the client. Multiple Principal User 1 ~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create a request callback that returns a valid token. -- Create a client with a url of the form ``mongodb://test_user1@localhost:27018/?authMechanism=MONGODB-OIDC&directConnection=true&readPreference=secondaryPreferred`` and a valid OIDC request callback. +- Create a client with ``MONGODB_URI_MULTI``, a username of ``test_user1``, and the OIDC request callback. - Perform a ``find`` operation that succeeds. - Close the client. Multiple Principal User 2 ~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. - Create a request callback that reads in the generated ``test_user2`` token file. -- Create a client with a url of the form ``mongodb://test_user2@localhost:27018/?authMechanism=MONGODB-OIDC&directConnection=true&readPreference=secondaryPreferred`` and a valid OIDC request callback. +- Create a client with ``MONGODB_URI_MULTI``, a username of ``test_user2``, and the OIDC request callback. - Perform a ``find`` operation that succeeds. - Close the client. Multiple Principal No User ~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create a client with a url of the form ``mongodb://localhost:27018/?authMechanism=MONGODB-OIDC&directConnection=true&readPreference=secondaryPreferred`` and a valid OIDC request callback. +- Create a client with ``MONGODB_URI_MULTI``, no username, and the OIDC request callback. - Assert that a ``find`` operation fails. - Close the client. Allowed Hosts Blocked ~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create a client that uses the OIDC url and a request callback, and an - ``ALLOWED_HOSTS`` that is an empty list. +- Create a default OIDC client, with an ``ALLOWED_HOSTS`` that is an empty list. - Assert that a ``find`` operation fails with a client-side error. - Close the client. - Create a client that uses the url ``mongodb://localhost/?authMechanism=MONGODB-OIDC&ignored=example.com`` a request callback, and an - ``ALLOWED_HOSTS`` that contains ["example.com"]. + ``ALLOWED_HOSTS`` that contains ``["example.com"]``. - Assert that a ``find`` operation fails with a client-side error. - Close the client. -Lock Avoids Extra Callback Calls -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- Clear the cache. -- Create a request callback that returns a token that will expire soon, and - a refresh callback. Ensure that the request callback has a time delay, and - that we can record the number of times each callback is called. -- Spawn two threads or async operations that do the following: - - Create a client with the callbacks. - - Run a find operation that succeeds. - - Close the client. - - Create a new client with the callbacks. - - Run a find operation that succeeds. - - Close the client. -- Join the two threads or simultaneously call the async operations -- Ensure that the request callback has been called once, and the refresh - callback has been called twice, or that no async function has been - entered simultaneously. - -AWS Automatic Auth -================== - -Drivers MUST be able to authenticate using the "aws" provider workflow -simulating an EC2 instance with an enabled web identity token provider, -generated by Drivers Evergreen Tools. - -Single Principal -~~~~~~~~~~~~~~~~ -- Create a client with a url of the form ``mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PROVIDER_NAME:aws``. -- Perform a ``find`` operation that succeeds. -- Close the client. - -Multiple Principal User 1 -~~~~~~~~~~~~~~~~~~~~~~~~~ -- Create a client with a url of the form ``mongodb://localhost:27018/?authMechanism=MONGODB-OIDC&authMechanismProperties=PROVIDER_NAME:aws&directConnection=true&readPreference=secondaryPreferred``. -- Perform a ``find`` operation that succeeds. -- Close the client. - -Multiple Principal User 2 -~~~~~~~~~~~~~~~~~~~~~~~~~ -- Set the ``AWS_WEB_IDENTITY_TOKEN_FILE`` environment variable - to the location of valid ``test_user2`` credentials. -- Create a client with a url of the form ``mongodb://localhost:27018/?authMechanism=MONGODB-OIDC&authMechanismProperties=PROVIDER_NAME:aws&directConnection=true&readPreference=secondaryPreferred``. -- Perform a ``find`` operation that succeeds. -- Close the client. - -Allowed Hosts Ignored -~~~~~~~~~~~~~~~~~~~~~ -- Create a client with a url of the form ``mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PROVIDER_NAME:aws``, and an - ``ALLOWED_HOSTS`` that is an empty list. -- Assert that a ``find`` operation succeeds. -- Close the client. - Callback Validation =================== Valid Callbacks ~~~~~~~~~~~~~~~ -- Clear the cache. -- Create request and refresh callback that validate their inputs and return - a valid token. The request callback must return a token that expires in - one minute. +- Create request callback that validates its inputs and returns a valid token. - Create a client that uses the above callbacks. - Perform a ``find`` operation that succeeds. Verify that the request callback was called with the appropriate inputs, including the timeout parameter if possible. Ensure that there are no unexpected fields. - Close the client. -- Create a new client with the same configuration. -- Perform a ``find`` operation that succeeds. Verify that the refresh - callback was called with the appropriate inputs, including the timeout - parameter if possible. -- Close the client. Request Callback Returns Null ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. - Create a client with a request callback that returns ``null``. - Perform a ``find`` operation that fails. - Close the client. -Refresh Callback Returns Null -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create request callback that returns a valid token that will expire in a - minute, and a refresh callback that returns ``null``. -- Perform a ``find`` operation that succeeds. -- Close the client. -- Create a new client with the same configuration. -- Perform a ``find`` operation that fails. -- Close the client. - Request Callback Returns Invalid Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. - Create a client with a request callback that returns data not conforming to the ``OIDCRequestTokenResult`` with missing field(s). - Perform a ``find`` operation that fails. @@ -191,107 +114,12 @@ Request Callback Returns Invalid Data - Perform a ``find`` operation that fails. - Close the client. -Refresh Callback Returns Missing Data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create request callback that returns a valid token that will expire in a - minute, and a refresh callback that returns data not conforming to - the ``OIDCRequestTokenResult`` with missing field(s). -- Create a client with the callbacks. -- Perform a ``find`` operation that succeeds. -- Close the client. -- Create a new client with the same callbacks. -- Perform a ``find`` operation that fails. -- Close the client. - -Refresh Callback Returns Extra Data -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create request callback that returns a valid token that will expire in a - minute, and a refresh callback that returns data not conforming to - the ``OIDCRequestTokenResult`` with extra field(s). -- Create a client with the callbacks. -- Perform a ``find`` operation that succeeds. -- Close the client. -- Create a new client with the same callbacks. -- Perform a ``find`` operation that fails. -- Close the client. - -Cached Credentials -================== - -Drivers MUST ensure that they are testing the ability to cache credentials. -Unless otherwise specified, the tests MUST be performed with the callback-driven workflow with a provided request and refresh callback. If -desired, the caching tests MAY be done using mock server responses. -The following tests assume a global cache is in use. If a different -cache scheme is in use, appropriate tests MUST be written to ensure that -the cache is performing as intended. - -Cache with refresh -~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create a new client with a request callback that gives credentials that - expire in on minute. -- Ensure that a ``find`` operation adds credentials to the cache. -- Close the client. -- Create a new client with the same request callback and a refresh callback. -- Ensure that a ``find`` operation results in a call to the refresh callback. -- Close the client. - -Cache with no refresh -~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create a new client with a request callback that gives credentials that - expire in one minute. -- Ensure that a ``find`` operation adds credentials to the cache. -- Close the client. -- Create a new client with the a request callback but no refresh callback. -- Ensure that a ``find`` operation results in a call to the request callback. -- Close the client. - -Cache key includes callback -~~~~~~~~~~~~~~~~~~~~~~~~~~~ -If the driver does not support using callback references or hashes as part of -the cache key, skip this test. This test ensures that the callback is -considered as part of the cache key. - -- Clear the cache. -- Create a new client with a request callback that does not give an - ```expiresInSeconds``` value. -- Ensure that a ``find`` operation adds credentials to the cache. -- Close the client. -- Create a new client with a different request callback. -- Ensure that a ``find`` operation replaces the one-time use entry and adds a new entry to the cache. -- Close the client. - -Error clears cache -~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create a new client with a valid request callback that gives credentials - that expire within 5 minutes and a refresh callback that gives invalid - credentials. -- Ensure that a ``find`` operation adds a new entry to the cache. -- Close the client. -- Create a new client with the same parameters. -- Ensure that a subsequent ``find`` operation results in an error. -- Ensure that the cache value cleared. -- Close the client. - -AWS Automatic workflow does not use cache -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create a new client that uses the AWS automatic workflow. -- Ensure that a ``find`` operation does not add credentials to the cache. -- Close the client. - Speculative Authentication ========================== We can only test the successful case, by verifying that ``saslStart`` is not called. -- Clear the cache. -- Create a client with a request callback that returns a valid token - that will not expire soon. +- Create a client with a request callback that returns a valid token. - Set a fail point for ``saslStart`` commands of the form: .. code:: javascript @@ -316,10 +144,6 @@ is not called. - Perform a ``find`` operation that succeeds. - Close the client. -- Create a new client with the same properties without clearing the cache. -- Set a fail point for ``saslStart`` commands. -- Perform a ``find`` operation that succeeds. -- Close the client. Reauthentication ================ @@ -329,15 +153,12 @@ operation. Succeeds ~~~~~~~~ -- Clear the cache. -- Create request and refresh callbacks that return valid credentials - that will not expire soon. -- Create a client with the callbacks and an event listener. The following +- Create a default OIDC client and add an event listener. The following assumes that the driver does not emit ``saslStart`` or ``saslContinue`` events. If the driver does emit those events, ignore/filter them for the purposes of this test. - Perform a ``find`` operation that succeeds. -- Assert that the refresh callback has not been called. +- Assert that the request callback has been called once. - Clear the listener state if possible. - Force a reauthenication using a ``failCommand`` of the form: @@ -362,7 +183,7 @@ Succeeds remove the ``failCommand`` after the test to prevent leakage. - Perform another find operation that succeeds. -- Assert that the refresh callback has been called once, if possible. +- Assert that the request callback has been called twice. - Assert that the ordering of list started events is [``find``], , ``find``. Note that if the listener stat could not be cleared then there will and be extra ``find`` command. @@ -370,69 +191,44 @@ Succeeds - Assert that a ``find`` operation failed once during the command execution. - Close the client. -Retries and Succeeds with Cache -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create request and refresh callbacks that return valid credentials - that will not expire soon. -- Perform a ``find`` operation that succeeds. -- Force a reauthenication using a ``failCommand`` of the form: - -.. code:: javascript - - { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "find", "saslStart" - ], - "errorCode": 391 - } - } - -- Perform a ``find`` operation that succeeds. -- Close the client. - -Retries and Fails with no Cache -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create request and refresh callbacks that return valid credentials - that will not expire soon. -- Perform a ``find`` operation that succeeds (to force a speculative auth). -- Clear the cache. -- Force a reauthenication using a ``failCommand`` of the form: +Fails +~~~~~ +- Create a default OIDC client. +- Perform a find operation that succeeds (to force a speculative auth). +- Assert that the request callback has been called once. +- Force a reauthenication using a failCommand of the form: .. code:: javascript - - { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "find", "saslStart" - ], - "errorCode": 391 - } + { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find", "saslStart" + ], + "errorCode": 391 } + } -- Perform a ``find`` operation that fails. +- Perform a find operation that fails. +- Assert that the request callback has been called twice. - Close the client. Separate Connections Avoid Extra Callback Calls ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Clear the cache. -- Create request and refresh callbacks that return tokens that will not expire - soon. Ensure that we can record the number of times each callback is called. -- Create two clients using the callbacks -- Peform a find operation on each client that succeeds. -- Ensure that the request callback has been called once and the refresh - callback has not been called. -- Force a reauthenication on the first client using a ``failCommand`` of the +The following test assumes that the driver will be able to share a cache between +two MongoClient objects, or ensure that the same MongoClient is used with two +different connections. If that is not possible, the test may be skipped. + +- Create a request callback that returns valid, and ensure that we can record the number + of times the callback is called. +- Create two clients using the callbacks, or a single client and two connection objects. +- Peform a find operation on each client/connection that succeeds. +- If using a single client, share the underlying cache between clients. +- Ensure that the request callback has been called twice. +- Force a reauthenication on the first client/connection using a ``failCommand`` of the form: .. code:: javascript @@ -451,9 +247,7 @@ Separate Connections Avoid Extra Callback Calls } - Perform a ``find`` operation that succeds. -- Ensure that the request callback has been called once and the refresh - callback has been called once. -- Repeat the ``failCommand`` and ``find`` operation on the second client. -- Ensure that the request callback has been called once and the refresh - callback has been called once. -- Close both clients. \ No newline at end of file +- Ensure that the request callback has been called three times. +- Repeat the ``failCommand`` and ``find`` operation on the second client/connection. +- Ensure that the request callback has been called three times. +- Close all clients/connections. From 74d2a84eb7ccd622a704b9806935bf4e8517c1eb Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Mon, 4 Sep 2023 20:51:35 -0500 Subject: [PATCH 2/9] finish refactor --- source/auth/auth.rst | 409 ++++++++---------- .../auth/tests/legacy/connection-string.yml | 33 +- source/auth/tests/mongodb-oidc.rst | 73 +++- 3 files changed, 243 insertions(+), 272 deletions(-) diff --git a/source/auth/auth.rst b/source/auth/auth.rst index 6b16be5bc2..b6b9dc6e0f 100644 --- a/source/auth/auth.rst +++ b/source/auth/auth.rst @@ -39,7 +39,7 @@ Definitions Credential The pieces of information used to establish the authenticity of a user. This is composed of an identity and some form of evidence such as a password or a certificate. -FQDN +FQDN Fully Qualified Domain Name Mechanism @@ -247,8 +247,8 @@ Caching credentials in SCRAM In the implementation of SCRAM authentication mechanisms (e.g. SCRAM-SHA-1 and SCRAM-SHA-256), drivers MUST maintain a cache of computed SCRAM credentials. -The cache entries SHOULD be identified by the password, salt, iteration count, -and a value that uniquely identifies the authentication mechanism (e.g. "SHA1" +The cache entries SHOULD be identified by the password, salt, iteration count, +and a value that uniquely identifies the authentication mechanism (e.g. "SHA1" or "SCRAM-SHA-256"). The cache entry value MUST be either the ``saltedPassword`` parameter or the @@ -443,7 +443,7 @@ Many languages will have the ability to utilize 3rd party libraries. The server GSSAPI ~~~~~~ -:since: +:since: 2.4 Enterprise 2.6 Enterprise on Windows @@ -663,7 +663,7 @@ source MUST be specified. Defaults to the database name if supplied on the connection string or ``admin``. password - MUST be specified. + MUST be specified. mechanism MUST be "SCRAM-SHA-1" @@ -743,33 +743,33 @@ MONGODB-AWS :since: 4.4 -MONGODB-AWS authenticates using AWS IAM credentials (an access key ID and a secret access key), `temporary AWS IAM credentials `_ obtained from an -`AWS Security Token Service (STS) `_ +MONGODB-AWS authenticates using AWS IAM credentials (an access key ID and a secret access key), `temporary AWS IAM credentials `_ obtained from an +`AWS Security Token Service (STS) `_ `Assume Role `_ request, an OpenID Connect ID token that supports `AssumeRoleWithWebIdentity `_, or temporary AWS IAM credentials assigned to an `EC2 instance `_ or ECS task. Temporary credentials, in addition to an access key ID and a secret access key, includes a security (or session) token. -MONGODB-AWS requires that a client create a randomly generated nonce. It is -imperative, for security sake, that this be as secure and truly random as possible. Additionally, the secret access key and only the secret access key is sensitive. Drivers MUST take proper precautions to ensure we do not leak this info. +MONGODB-AWS requires that a client create a randomly generated nonce. It is +imperative, for security sake, that this be as secure and truly random as possible. Additionally, the secret access key and only the secret access key is sensitive. Drivers MUST take proper precautions to ensure we do not leak this info. All messages between MongoDB clients and servers are sent as BSON V1.1 Objects in the payload field of saslStart and saslContinue. -All fields in these messages have a "short name" which is used in the serialized +All fields in these messages have a "short name" which is used in the serialized BSON representation and a human-readable "friendly name" which is used in this specification. They are as follows: -==== ==================== ================= ============================================================================================================================================== +==== ==================== ================= ============================================================================================================================================== Name Friendly Name Type Description ==== ==================== ================= ============================================================================================================================================== -r client nonce BinData Subtype 0 32 byte cryptographically secure random number +r client nonce BinData Subtype 0 32 byte cryptographically secure random number p gs2-cb-flag int32 The integer representation of the ASCII charater 'n' or 'y', i.e., ``110`` or ``121`` s server nonce BinData Subtype 0 64 bytes total, 32 bytes from the client first message and a 32 byte cryptographically secure random number generated by the server -h sts host string FQDN of the STS service +h sts host string FQDN of the STS service a authorization header string Authorization header for `AWS Signature Version 4 `_ d X-AMZ-Date string Current date in UTC. See `AWS Signature Version 4 `_ t X-AMZ-Security-Token string Optional AWS security token -==== ==================== ================= ============================================================================================================================================== +==== ==================== ================= ============================================================================================================================================== Drivers MUST NOT advertise support for channel binding, as the server does not support it and legacy servers may fail authentication if drivers advertise -support. The client-first-message MUST set the gs2-cb-flag to the integer representation +support. The client-first-message MUST set the gs2-cb-flag to the integer representation of the ASCII character ``n``, i.e., ``110``. Conversation @@ -785,7 +785,7 @@ Client First .. code:: javascript - { + { "r" : new BinData(0, "dzw1U2IwSEtgaWI0IUxZMVJqc2xuQzNCcUxBc05wZjI="), "p" : 110 } @@ -794,7 +794,7 @@ Server First .. code:: javascript - { + { "s" : new BinData(0, "dzw1U2IwSEtgaWI0IUxZMVJqc2xuQzNCcUxBc05wZjIGS0J9EgLwzEZ9dIzr/hnnK2mgd4D7F52t8g9yTC5cIA=="), "h" : "sts.amazonaws.com" } @@ -817,9 +817,9 @@ Client First .. code:: javascript - { - "saslStart" : 1, - "mechanism" : "MONGODB-AWS" + { + "saslStart" : 1, + "mechanism" : "MONGODB-AWS" "payload" : new BinData(0, "NAAAAAVyACAAAAAAWj0lSjp8M0BMKGU+QVAzRSpWfk0hJigqO1V+b0FaVz4QcABuAAAAAA==") } @@ -828,8 +828,8 @@ Server First .. code:: javascript { - "conversationId" : 1, - "done" : false, + "conversationId" : 1, + "done" : false, "payload" : new BinData(0, "ZgAAAAVzAEAAAAAAWj0lSjp8M0BMKGU+QVAzRSpWfk0hJigqO1V+b0FaVz5Rj7x9UOBHJLvPgvgPS9sSzZUWgAPTy8HBbI1cG1WJ9gJoABIAAABzdHMuYW1hem9uYXdzLmNvbQAA"), "ok" : 1.0 } @@ -859,16 +859,16 @@ The following example shows a finished Authorization header. .. code:: javascript - Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7 + Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/iam/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=5d672d79c15b13162d9279b0855cfba6789a8edb4c82c400e06b5924a6f2b5d7 The following diagram is a summary of the steps drivers MUST follow to calculate the signature. .. image:: includes/calculating_a_signature.png ======================== ====================================================================================================== -Name Value +Name Value ======================== ====================================================================================================== -HTTP Request Method POST +HTTP Request Method POST URI / Content-Type* application/x-www-form-urlencoded Content-Length* 43 @@ -888,7 +888,7 @@ Body Action=GetCallerIdentity&Version=2011-06-15 Region Calculation `````````````````` -To get the region from the host, the driver MUST follow the algorithm expressed in psuedocode below. :: +To get the region from the host, the driver MUST follow the algorithm expressed in psuedocode below. :: if the host is invalid according to the rules described earlier the region is undefined and the driver must raise an error. @@ -899,21 +899,21 @@ To get the region from the host, the driver MUST follow the algorithm expressed else // the valid host string contains no periods and is not "aws.amazonaws.com" the region is "us-east-1" -Examples are provided below. +Examples are provided below. ============================== ========= ====================================================== -Host Region Notes +Host Region Notes ============================== ========= ====================================================== -sts.amazonaws.com us-east-1 the host is "sts.amazonaws.com"; use `us-east-1` -sts.us-west-2.amazonaws.com us-west-2 use the second label +sts.amazonaws.com us-east-1 the host is "sts.amazonaws.com"; use `us-east-1` +sts.us-west-2.amazonaws.com us-west-2 use the second label sts.us-west-2.amazonaws.com.ch us-west-2 use the second label -example.com com use the second label +example.com com use the second label localhost us-east-1 no "``.``" character; use the default region -sts..com second label is empty -.amazonaws.com starts with a period -sts.amazonaws. ends with a period -"" empty string -"string longer than 255" string longer than 255 bytes +sts..com second label is empty +.amazonaws.com starts with a period +sts.amazonaws. ends with a period +"" empty string +"string longer than 255" string longer than 255 bytes ============================== ========= ====================================================== `MongoCredential`_ Properties @@ -940,8 +940,8 @@ mechanism_properties Obtaining Credentials ````````````````````` -Drivers will need AWS IAM credentials (an access key, a secret access key and optionally a session token) to complete the steps in the `Signature Version 4 Signing Process -`_. If a username and password are provided drivers +Drivers will need AWS IAM credentials (an access key, a secret access key and optionally a session token) to complete the steps in the `Signature Version 4 Signing Process +`_. If a username and password are provided drivers MUST use these for the AWS IAM access key and AWS IAM secret key, respectively. If, additionally, a session token is provided Drivers MUST use it as well. If a username is provided without a password (or vice-versa) or if *only* a session token is provided Drivers MUST raise an error. In other words, regardless of how Drivers obtain credentials the only valid combination of credentials is an access key ID and a secret access key or an access key ID, a secret access key and a session token. AWS recommends using an SDK to "take care of some of the heavy lifting @@ -1083,7 +1083,7 @@ credentials. Querying the URI will return the JSON response: "SecretAccessKey": , "Token": } - + EC2 endpoint ____________ If the environment variable ``AWS_CONTAINER_CREDENTIALS_RELATIVE_URI`` is unset, drivers MUST use the EC2 endpoint, @@ -1132,7 +1132,7 @@ To re-direct queries from the EC2 endpoint to the mock server, replace the link- $ TOKEN=`curl -X PUT "http://localhost:8000/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 30"` $ ROLE_NAME=`curl http://localhost:8000/latest/meta-data/iam/security-credentials/ -H "X-aws-ec2-metadata-token: $TOKEN"` $ curl http://localhost:8000/latest/meta-data/iam/security-credentials/$ROLE_NAME -H "X-aws-ec2-metadata-token: $TOKEN" - + The JSON response from both the actual and mock EC2 endpoint will be in this format: .. code:: javascript @@ -1147,8 +1147,8 @@ The JSON response from both the actual and mock EC2 endpoint will be in this for "Expiration": } -From the JSON response drivers -MUST obtain the ``access_key``, ``secret_key`` and ``security_token`` which will be used during the `Signature Version 4 Signing Process +From the JSON response drivers +MUST obtain the ``access_key``, ``secret_key`` and ``security_token`` which will be used during the `Signature Version 4 Signing Process `_. Caching Credentials @@ -1180,65 +1180,16 @@ MONGODB-OIDC :since: 7.0 Enterprise -MONGODB-OIDC authenticates using an `OIDC `_ access tokens. Drivers MUST support -both Callback-driven OIDC and Automatic OIDC Authentication for AWS. - - -Conversation -```````````` - -Authenticating using the MONGODB-OIDC mechanism will require 1 or 2 round trips between the MongoDB driver and server. The requests from the driver and the replies from the server are described by the following IDL structs which are encoded in the payload as octet sequences defining BSON objects: - -.. code:: idl - - PrincipalStepRequest: - description: Driver’s opening request in saslStart - fields: - n: - description: "Name of the OIDC user Principal" - type: string - optional: true - -Note that the principal name is optional as it may be provided by the IDP in environments where only one IDP is used. The username provided by the user MUST be used as the principalName. - -.. code:: idl - - IdPServerInfo: - description: "The information used by callbacks to authenticate with the Identity Provider." - fields: - issuer: - description: >- - URL which describes the Authentication Server. This identifier should be - the iss of provided access tokens, and be viable for RFC8414 - metadata discovery and RFC9207 identification. - type:string - clientId: - description: "Unique client ID for this OIDC client" - type: string - requestScopes: - description: "Additional scopes to request from IDP" - type: array - optional: true - -Server will use principalName (n) if provided in the driver’s PrincipalStepRequest to select an appropriate IDP. This IDP's configuration will be returned in the server’s response that will be used by the end-user to acquire an Access Token. - -This Access Token will be used as the JWT in the driver’s JwtStepRequest to complete authentication. - -.. code:: idl - - JwtStepRequest: - description: "Client's request with signed token" - fields: - jwt: - description: "Compact serialized JWT with signature" - cpp_name: JWT - type: string +MONGODB-OIDC authenticates using an `OIDC `_ access tokens. +Drivers MAY implement the human authentication workflow with ``REQUEST_TOKEN_CALLBACK``, but it is only currently required in the Node driver, +to support usage in the MongoDB Shell. The machine authentication workflow is currently not supported. `MongoCredential`_ Properties ````````````````````````````` username - MUST NOT be specified in automatic authentication. Drivers MUST allow the user to specify this in the callback-driven authentication. If a user omits this when multiple OIDC providers are configured, the server will produce an error during authentication. + MUST NOT be specified in machine authentication. Drivers MUST allow the user to specify this in the human authentication. + If a user omits this when multiple OIDC providers are configured, the server will produce an error during authentication. source MUST be "$external". Defaults to ``$external``. @@ -1250,20 +1201,13 @@ mechanism MUST be "MONGODB-OIDC" mechanism_properties - PROVIDER_NAME - Drivers MUST allow the user to specify a name for using a service - to obtain credentials that is one of ["aws"]. REQUEST_TOKEN_CALLBACK - Drivers MUST allow the user to specify a callback of the form - "onRequest" (defined below), if the driver supports - providing objects as mechanism property values. Otherwise the driver MUST allow it as a MongoClientOption. - REFRESH_TOKEN_CALLBACK - Drivers MUST allow the user to specify a callback of the form - "onRefresh" (defined below), if the driver supports + Drivers MUST allow the user to specify a callback of the form "onRequest" (defined below), if the driver supports providing objects as mechanism property values. Otherwise the driver MUST allow it as a MongoClientOption. ALLOWED_HOSTS The list of allowed hostnames or ip-addresses (ignoring ports) for - MongoDB connections. The hostnames may include a leading "*." wildcard, which allows for matching (potentially nested) subdomains. ALLOWED_HOSTS is a + MongoDB connections. The hostnames may include a leading "*." wildcard, which allows for matching + (potentially nested) subdomains. ALLOWED_HOSTS is a security feature and MUST default to ``["*.mongodb.net", '*.mongodb-dev.net", "*.mongodbgov.net", "localhost", "127.0.0.1", "::1"]``. When ``MONGODB-OIDC`` authentication is attempted against a hostname @@ -1272,151 +1216,164 @@ mechanism_properties callbacks. This value MUST not be allowed in the URI connection string. The hostname check MUST be performed after SRV record resolution, if applicable. + This property is only applicable when ``REQUEST_TOKEN_CALLBACK`` is given. -Drivers MUST NOT send a PrincipalStepRequest when performing automatic authentication -or when there is a cached IdPServerResponse. Drivers must instead use ``saslStart`` with a JwtStepRequest. - -Speculative Authentication -``````````````````````````````````` -Drivers MUST implement speculative authentication for MONGODB-OIDC during the ``hello`` handshake. If there is an unexpired access token, the JwtStepRequest SASL command will be used as the speculation command. If there is no cache value, the PrincipalStepRequest will be used as the speculation command. The driver MUST NOT call any callbacks during speculative authentication. +Interfaces +`````````` -User Provided Callbacks -``````````````````````` +Authenticating using the MONGODB-OIDC mechanism will require 1 or 2 round trips between the MongoDB driver and server. +The requests from the driver and the replies from the server are described by the following interfaces which are encoded +in the payload as octet sequences defining BSON objects: -Drivers MUST allow the user to provide callbacks for token request and -token refresh. The driver MUST provide a way for the both callbacks to be either automatically -canceled, or to cancel itself. This can be as a timeout argument to the -callback, a cancellation context passed to the callback, or some other -language-appropriate mechanism. The timeout duration MUST be 5 minutes, -to account for the fact that there may be human interaction involved. +.. code:: typescript -Callbacks can be synchronous and/or asynchronous, depending on the driver -and/or language. Asynchronous callbacks should be preferred when other -operations in the driver use asynchronous functions. + // Driver’s opening request in saslStart. + interface PrincipalStepRequest { + // Name of the OIDC user Principal. + n?: str; + } -The driver MUST pass the following information to the request callback: ``IdpServerInfo``, and either a ``timeoutSeconds`` or ``timeoutContext`` object for the callback. The signature of the callback is up to the driver's discretion, but the driver MUST ensure that, in the future, callbacks may have additional optional parameters passed to them. An example might look like: +Note that the principal name is optional as it may be provided by the IdP in environments where only one IdP is used. +If given, then ``username`` provided by the user MUST be used as the Principal ``(n)``. -.. code: typescript +.. code:: typescript - function onRequest(info: IdpServerInfo, params: RequestParameters): IdpServerResponse + // The information used by a REQUEST_TOKEN_CALLBACK to authenticate with the Identity Provider. + interface IdpInfo { + // URL which describes the Authentication Server. This identifier should be + // the iss of provided access tokens, and be viable for RFC8414 + // metadata discovery and RFC9207 identification. + issuer: str; -In this example, one of the timeout values would then need to be present on ``RequestParameters``. ``IdpServerResponse`` is defined as: + // Unique client ID for this OIDC client. + clientId: str; -.. code:: idl + // Additional scopes to request from IdP. + requestScopes?: Array; + } - IdPServerResponse: - description: "The result of a token request" - strict: false - fields: - accessToken: - description: "The OIDC access token" - type: string - expiresInSeconds: - description: "The expiration time in seconds from the current time" - type: int - optional: true - refreshToken: - description: "The OIDC refresh token" - type: str - optional: true +The server will use Principal ``(n)`` if provided in the driver’s ``PrincipalStepRequest`` to select an appropriate IdP. +This IdP's configuration will be returned in the server’s response that will be used by the end-user to acquire an Access Token. -The token refresh callback must take the same arguments as the request callback, as well as the ``refreshToken`` string given by the ``IdpServerResponse``, and might look like the following:: +This Access Token will be used as the JWT in the driver’s ``JwtStepRequest`` to complete authentication. .. code:: typescript - function onRefresh(info: IdpServerInfo, params: RefreshParameters): IdpServerResponse + // Client's request with signed token. + interface JwtStepRequest: + // Compact serialized JWT with signature. + jwt: str; + } -Before calling a callback, the driver MUST acquire a lock unique to the cache key. The driver MUST ensure that credentials have not changed between when the lock was requested and when it was acquired. The lock MUST be released -when the callback call as finished or errored. -This is because request callbacks may involve human interaction, and refresh -callbacks could use refresh tokens that can only be used once. +The IdP response that is expected to be returned by the ``REQUEST_TOKEN_CALLBACK`` is as follows: -If either callback does not return an object in the correct form of ``IdpServerResponse``, the driver MUST raise an error either using the type system or by raising an error when non-optional properties are missing . The driver MUST NOT attempt to validate -the token(s) directly. It is expected that if the server changes the expected fields, the SASL exchange will be updated with a version parameter. Drivers do not need to attempt to provide old-driver-new-server compatibility. + .. code:: typescript -If the refresh callback is given and the request callback is not given, -the driver MUST raise an error. If PROVIDER_NAME is given and one or more -callbacks are given, the driver MUST raise an error. + // The result of a token request. + interface IdPResponse { + // The oidc access token. + accessToken: str; -If no callbacks are given, the driver MUST enforce that a PROVIDER_NAME -mechanism_properties is set and one of ("aws",). -The callback mechanism can be used to support both callback-based or automatic workflows that are not explicitly implemented -by drivers. If there is no callback and no PROVIDER_NAME, or the -PROVIDER_NAME is set but credentials cannot be automatically obtained, -the driver MUST raise an error. + // The OIDC refresh token. + refreshToken?: str; + // The expiration time in seconds from the current time (ignored). + expiresInSeconds?: str; + } -Supported Service Providers -``````````````````````````` +Conversation +```````````` +If using the ``PrincipalStepRequest`` first, the conversation will look like: -Drivers MUST support obtaining credentials for a service for "aws", given -by the PROVIDER_NAME mechanism property. In all cases the acquired token -will be given as the ``jwt`` argument and the JwtStepRequest MUST be made immediately, as part of speculative authentication if appropriate, skipping the PrincipalStepRequest. -Drivers MUST raise an error if both a PROVIDER_NAME and username are -given, since using a service will not use the username. +| C: :javascript:`{saslStart: 1, mechanism: "MONGODB-OIDC", payload: BinData(0, "...")}` +| S: :javascript:`{conversationId : 1, payload: BinData(0,"..."), done: false, ok: 1}` +| C: :javascript:`{saslContinue: 1, conversationId: 1, payload: BinData(0, "...")}` +| S: :javascript:`{conversationId: 1, payload: BinData(0,".."), done: true, ok: 1}` -AWS -___ +If using the ``JwtStepRequest`` directly, the conversation will look like: -When the PROVIDER_NAME mechanism property is set to "aws", the driver MUST -attempt to read the value given by the ``AWS_WEB_IDENTITY_TOKEN_FILE`` and -interpret it as a file path. The contents of the file are read as the -access token. If the path does not exist or cannot be read, or the environment variable does not exist, the driver MUST raise an error. +| C: :javascript:`{saslStart: 1, mechanism: "MONGODB-OIDC", payload: BinData(0, "...")}` +| S: :javascript:`{conversationId : 1, payload: BinData(0,"..."), done: true, ok: 1}` +Drivers MUST NOT send a ``PrincipalStepRequest`` when performing machine authentication +or when there is a cached Access Token. Drivers must instead use ``saslStart`` with a ``JwtStepRequest``. -Caching Credentials -``````````````````` +Caching +``````` +Drivers MUST cache the ``IdPInfo``, Access Token, and Refresh Token associated with each +``MongoClient``. If any operation fails the driver MUST clear the Access Token. The Refresh Token is handled differently, +see the ``Reauthentication`` section below. + +Drivers MUST also use a token generation id that is incremented whenever a new Access Token is retrieved. +When a connection succeeds, the token generation id MUST be associated with or stored on the connection object. +This value is used in the ``Reauthentication`` section below. + +Speculative Authentication +`````````````````````````` +Drivers MUST implement speculative authentication for MONGODB-OIDC during the ``hello`` handshake. +If there is a cached Access Token the ``JwtStepRequest`` SASL command will be used as the speculation command. +If there is no cached Access Token, the ``PrincipalStepRequest`` will be used as the speculation command. +The driver MUST NOT call the callback during speculative authentication. + +REQUEST_TOKEN_CALLBACK +`````````````````````` +The driver MUST provide a way for the ``REQUEST_TOKEN_CALLBACK`` to be either automatically +canceled, or to cancel itself. This can be as a timeout argument to the +callback, a cancellation context passed to the callback, or some other +language-appropriate mechanism. The timeout duration MUST be 5 minutes, +to account for the fact that there may be human interaction involved. +This callback is not subject to CSOT. -Drivers MUST use caching for callback-based authentication.. -When an authentication request is made and there is an available cached response, -the driver MUST use the cached Access Token from that response, if it has not expired. +Callbacks can be synchronous and/or asynchronous, depending on the driver +and/or language. Asynchronous callbacks should be preferred when other +operations in the driver use asynchronous functions. -A cache MUST be able to store the IDPServerInfo, the IdPServerResponse tokens, and the known expiration time of the access token. -The cache is kept alive even if the Access Token is expired to preserve the IdPServerInfo response, as well as -account for the Refresh Token, which typically has an (unknown) lifetime that -is longer than the access token lifetime. Drivers MUST ensure that the cache does not leak memory, by an appropriate time or space-based cache and auditing the cache at a regular interval. +The driver MUST pass the following information to the callback: +``IdpInfo``, and either a ``timeoutSeconds`` or ``timeoutContext`` object for the callback. +The signature of the callback is up to the driver's discretion, but the driver MUST ensure that, +in the future, callbacks may have additional optional parameters passed to them. +An example might look like: -If a global cache is used, the cache keys MUST include the username (or empty string) and the -actually used socket address and port for the current server. The cache key -MUST also include hashes of the callback function(s), if hash comparisons are possible in the driver language. In the case of a global cache, using the socket address and port accounts for the case when two different servers use the same username but could be configured differently. -There is an edge case where if the same username is used and two aliases -to the same local host address are given, there will be duplicate user/service -interactions, unless the driver can resolve the local host address as well. -Note that because we use the server socket address, there will different cache -keys for each member of a replica set. +.. code: typescript -The driver MUST cache the IdPServerInfo as part of the cache value, -to enable skipping the PrincipalStepRequest on subsequent authentications of the same -cache key. + interface RequestParameters { + // Timeout in seconds for the callback. Optionally, timeoutContext instead if applicable to language. + timeoutSeconds: int; -A global cache SHOULD be preferred, to prevent multiple browser interactions -in the case of an Authentication Code workflow. However, drivers or dev tools -can choose to use their own caching scheme if appropriate for their language/ -environment. + // The refresh token, if applicable, to be used by the callback to request a new token from the issuer. + refreshToken?: str; + } -A cached Access Token will expire 5 minutes before the ``expiresInSeconds`` -time, if given. If there is no ``expiresInSeconds``, the token must be considered expired as soon as the ensuing JwtStepRequest is started. If a cached value is found but its -Access Token is rejected by the server with a ``ReauthenticationRequired`` error, the Access Token must be marked expired and the Refresh callback MUST be called (if given) with the IdPServerInfo and Refresh Token, and it will return a new IdpServerResponse. If the Refresh Callback fails, the error is raised to the user. If the Refresh Callback succeeds, the new Access Token MUST be sent using a JwtStepRequest. If the request fails with a ReauthenticationRequired error, the cache should be cleared, and a PrincipalStepRequest MUST be sent. Next, the Request Callback should be called. If the callback fails, the error is raised to the user. -If the callback succeeds, the new Access Token MUST be sent using a JwtStepRequest. If the request fails with a ReauthenticationRequired error, that error MUST be propagated to the user. -The driver MUST have a guard or a flag in place to differentiate between a JwtStepRequest ReauthenticationRequired failure that takes place after a PrincipalStepRequest has been made to prevent an infinite loop. +.. code: typescript + + function onRequest(info: IdpInfo, params: RequestParameters): IdpResponse -If there is no refresh callback and no unexpired Access Token, the request callback will be called. Multithreaded drivers MUST ensure that there is at most one concurrent invocation of the above fallback logic for a given cache key. +Before calling the callback, the driver MUST acquire a lock unique to the ``MongoClient``. +The driver MUST ensure that credentials have not changed between when the lock was requested and when it was acquired. The lock MUST be released +when the callback call as finished or errored. +This is because the ``REQUEST_TOKEN_CALLBACK`` may involve human interaction, and refresh tokens migh only be able to be used used once. -If a cached value is used and the authentication step fails or times out, the driver MUST clear the -cached value. +If the callback does not return an object in the correct form of ``IdpResponse``, the driver MUST raise an error either using the type +system or by raising an error when non-optional properties are missing . +The driver MUST NOT attempt to validate the token(s) directly. +It is expected that if the server changes the expected fields, the SASL exchange will be updated with a version parameter. +Drivers do not need to attempt to provide old-driver-new-server compatibility. +If no callback is given, the driver MUST must raise a validation error. Reauthentication ```````````````` -When reauthentication is requested by the server (as a 391 error code) and MONGODB-OIDC is in use, the driver MUST -ensure that the Access Token that was most-recently used to authenticate this connection is not used for subsequent authentication, by marking it as expired. If non-expired Access Token is available in the cache, it should be used as usual. If a refresh -callback is given, it will be called as usual. Otherwise the IdPServerResponse will be cleared if present and authentication will proceed from the request callback. - -If the ``sasl`` step(s) fail with a 391 error code and the payload of the command contained ``jwt`` , the driver MUST clear the IdPServerResponse and -attempt to authenticate one more time starting from -``PrincipalStepRequest``. An initial reauthentication may fail for various reasons, such as token expiration or identity provider reconfiguration, so a second reauthentication might be needed. - -The driver MUST account for the case of multiple connections hitting a reauthentication error at different times, to prevent unnecessary callback calls. If another connection has already reauthenticated, then the Access Token should not be expired. The driver can either cache a token generation id per connection as well as in the main cache, or some other equivalent method to track whether a reauthentication has already occurred. +When reauthentication is requested by the server (as a 391 error code) and MONGODB-OIDC is in use, the driver MUST perform a reauthentication. +The driver MUST account for the case of multiple connections hitting a reauthentication error at different times. +The driver MUST also account for a reauthenication that results from an IdP configuration change on the server. +To accomplish these goal, the following algorithm is used to handle a reauthenication error: + +- First, see if the Access Token on the MongoClient is different than the Access Token on the connection. + - If they are different, optimisitically try to authenticate. On error, continue. +- Next, perform a ``PrincipalNameRequest`` to determine if the ``IdpInfo`` has changed. + - If it has changed, clear the current Access Token and Refresh Token, and continue. +- If we have a Refresh Token, attempt to authenticate. On error, clear the Refresh Token and continue. +- Attempt to authenticate. Raise any errors to the user. ------------------------- Connection String Options @@ -1441,7 +1398,7 @@ authSource For MONGODB-CR, SCRAM-SHA-1 and SCRAM-SHA-256 authMechanisms, the authSource defaults to the database name if supplied on the connection string or ``admin``. authMechanismProperties=PROPERTY_NAME:PROPERTY_VALUE,PROPERTY_NAME2:PROPERTY_VALUE2 - A generic method to set mechanism properties in the connection string. + A generic method to set mechanism properties in the connection string. For example, to set REALM and CANONICALIZE_HOST_NAME, the option would be ``authMechanismProperties=CANONICALIZE_HOST_NAME:forward,SERVICE_REALM:AWESOME``. @@ -1553,8 +1510,8 @@ As a URI, those have to be UTF-8 encoded and URL-escaped, e.g.: - mongodb://%E2%85%A8:IV@mongodb.example.com/admin - mongodb://%E2%85%A8:I%C2%ADV@mongodb.example.com/admin --------------------------- -Speculative Authentication +-------------------------- +Speculative Authentication -------------------------- See the speculative authentication section in the `MongoDB Handshake spec `_. @@ -1604,22 +1561,22 @@ Q: It's possible to continue using authenticated sockets even if new sockets fai Yes, that's technically true. The issue with doing that is for drivers using connection pooling. An application would function normally until an operation needed an additional connection(s) during a spike. Each new connection would fail to authenticate causing intermittent failures that would be very difficult to understand for a user. Q: Should a driver support multiple credentials? - No. + No. - Historically, the MongoDB server and drivers have supported multiple credentials, one per authSource, on a single connection. It was necessary because early versions of MongoDB allowed a user to be granted privileges - to access the database in which the user was defined (or all databases in the special case of the "admin" database). But with the introduction of role-based access control in MongoDB 2.6, that restriction was + Historically, the MongoDB server and drivers have supported multiple credentials, one per authSource, on a single connection. It was necessary because early versions of MongoDB allowed a user to be granted privileges + to access the database in which the user was defined (or all databases in the special case of the "admin" database). But with the introduction of role-based access control in MongoDB 2.6, that restriction was removed and it became possible to create applications that access multiple databases with a single authenticated user. - Role-based access control also introduces the potential for accidental privilege escalation. An application may, for example, authenticate user A from authSource X, and user B from authSource Y, thinking that + Role-based access control also introduces the potential for accidental privilege escalation. An application may, for example, authenticate user A from authSource X, and user B from authSource Y, thinking that user A has privileges only on collections in X and user B has privileges only on collections in Y. But with role-based access control that restriction no longer exists, and it's possible that user B has, for example, more privileges on collections in X than user A does. Due to this risk it's generally safer to create a single user with only the privileges required for a given application, and authenticate only that one user in the application. - In addition, since only a single credential is supported per authSource, certain mechanisms are restricted to a single credential and some credentials cannot be used in conjunction (GSSAPI and X509 both use the "$external" database). + In addition, since only a single credential is supported per authSource, certain mechanisms are restricted to a single credential and some credentials cannot be used in conjunction (GSSAPI and X509 both use the "$external" database). + + Finally, MongoDB 3.6 introduces sessions, and allows at most a single authenticated user on any connection which makes use of one. Therefore any application that requires multiple authenticated users will not be able to make use of any feature that builds on sessions (e.g. retryable writes). - Finally, MongoDB 3.6 introduces sessions, and allows at most a single authenticated user on any connection which makes use of one. Therefore any application that requires multiple authenticated users will not be able to make use of any feature that builds on sessions (e.g. retryable writes). - - Drivers should therefore guide application creators in the right direction by supporting the association of at most one credential with a MongoClient instance. + Drivers should therefore guide application creators in the right direction by supporting the association of at most one credential with a MongoClient instance. Q: Should a driver support lazy authentication? No, for the same reasons as given in the previous section, as lazy authentication is another mechanism for allowing multiple credentials to be associated with a single MongoClient instance. @@ -1647,7 +1604,7 @@ Q: Why does SCRAM sometimes SASLprep and sometimes not? problem, MongoDB decided that the best user experience on upgrade and lowest technical risk of implementation is to require drivers to continue to not SASLprep usernames in SCRAM-SHA-256. - + Q: Should drivers support accessing Amazon EC2 instance metadata in Amazon ECS? No. While it's possible to allow access to EC2 instance metadata in ECS, for security reasons, Amazon states it's best practice to avoid this. (See `accessing EC2 metadata in ECS `_ and `IAM Roles for Tasks `_) diff --git a/source/auth/tests/legacy/connection-string.yml b/source/auth/tests/legacy/connection-string.yml index 9f8aab4a72..d4fee90d4c 100644 --- a/source/auth/tests/legacy/connection-string.yml +++ b/source/auth/tests/legacy/connection-string.yml @@ -401,44 +401,13 @@ tests: mechanism: MONGODB-OIDC mechanism_properties: REQUEST_TOKEN_CALLBACK: true -- description: should recognise the mechanism with aws device (MONGODB-OIDC) - uri: mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PROVIDER_NAME:aws - valid: true - credential: - username: - password: - source: "$external" - mechanism: MONGODB-OIDC - mechanism_properties: - PROVIDER_NAME: aws -- description: should recognise the mechanism when auth source is explicitly specified - and with aws device (MONGODB-OIDC) - uri: mongodb://localhost/?authMechanism=MONGODB-OIDC&authSource=$external&authMechanismProperties=PROVIDER_NAME:aws - valid: true - credential: - username: - password: - source: "$external" - mechanism: MONGODB-OIDC - mechanism_properties: - PROVIDER_NAME: aws - description: should throw an exception if username and password are specified (MONGODB-OIDC) uri: mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC callback: - oidcRequest valid: false credential: -- description: should throw an exception if username and deviceName are specified - (MONGODB-OIDC) - uri: mongodb://principalName@localhost/?authMechanism=MONGODB-OIDC&PROVIDER_NAME:gcp - valid: false - credential: -- description: should throw an exception if specified deviceName is not supported - (MONGODB-OIDC) - uri: mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PROVIDER_NAME:unexisted - valid: false - credential: -- description: should throw an exception if neither deviceName nor callbacks specified +- description: should throw an exception if no callbacks specified (MONGODB-OIDC) uri: mongodb://localhost/?authMechanism=MONGODB-OIDC valid: false diff --git a/source/auth/tests/mongodb-oidc.rst b/source/auth/tests/mongodb-oidc.rst index f6496210c3..ffa3a151dd 100644 --- a/source/auth/tests/mongodb-oidc.rst +++ b/source/auth/tests/mongodb-oidc.rst @@ -15,28 +15,20 @@ Drivers MUST test the following scenarios: Drivers MUST be able to authenticate against a server configured with either one or two configured identity providers. -Note that typically the preconfigured Atlas Dev clusters are used for testing. The URIs can be fetched -from the ``drivers/oidc`` Secrets vault, see vault instructions . Use ``OIDC_ATLAS_URI_SINGLE`` for ``MONGODB_URI_SINGLE`` and +Note that typically the preconfigured Atlas Dev clusters are used for testing, in Evergreen and localy. The URIs can be fetched +from the ``drivers/oidc`` Secrets vault, see `vault instructions`_. Use ``OIDC_ATLAS_URI_SINGLE`` for ``MONGODB_URI_SINGLE`` and ``OIDC_ATLAS_URI_MULTI`` for ``OIDC_ATLAS_URI_MULTI``. -If using local servers is preferred, using the ``drivers-evergreen-tools`` Local Testing method , +If using local servers is preferred, using the `Local Testing`_ method, use ``mongodb://localhost/?authMechanism=MONGODB-OIDC`` for ``MONGODB_URI_SINGLE`` and ``mongodb://localhost:27018/?authMechanism=MONGODB-OIDC&directConnection=true&readPreference=secondaryPreferred`` for ``MONGODB_URI_MULTI`` because the other server is a secondary on a replica set, on port ``27018``. -The driver MUST generate valid local tokens at the location given by ``OIDC_TOKEN_DIR``, see ``drivers-evergreen-tools`` for example . -. - -Drivers will need to be able to internally query and clear the cached -credentials to verify usage for testing purposes. Clearing the cache -means removing all data from the cache, including ``OIDCMechanismServerStep1`` -information. - The default OIDC client used in the tests will be configured with ``MONGODB_URI_SINGLE`` and a valid request callback handler -that returns the ``test_user1`` local token. +that returns the ``test_user1`` local token in ``OIDC_TOKEN_DIR`` as the "access_token", and a dummy "refresh_token". -https://wiki.corp.mongodb.com/display/DRIVERS/Using+AWS+Secrets+Manager+to+Store+Testing+Secrets -https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/auth_oidc/README.md +.. _Local Testing: https://github.com/mongodb-labs/drivers-evergreen-tools/blob/master/.evergreen/auth_oidc/README.md#local-testing +.. _vault instructions: https://wiki.corp.mongodb.com/display/DRIVERS/Using+AWS+Secrets+Manager+to+Store+Testing+Secrets Callback-Driven Auth ==================== @@ -191,6 +183,59 @@ Succeeds - Assert that a ``find`` operation failed once during the command execution. - Close the client. +Succeeds no refresh +~~~~~~~~~~~~~~~~~~~ +- Create a default OIDC client with a request callback that does not return + a refresh token. +- Perform a ``find`` operation that succeeds. +- Assert that the request callback has been called once. +- Force a reauthenication using a ``failCommand`` of the form: + +.. code:: javascript + + { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "errorCode": 391 + } + } + +- Perform a ``find`` operation that succeeds. +- Assert that the request callback has been called twice. +- Close the client. + +Succeeds after refresh fails +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +- Create a default OIDC client. +- Perform a ``find`` operation that succeeds. +- Assert that the request callback has been called once. +- Force a reauthenication using a ``failCommand`` of the form: + +.. code:: javascript + + { + "configureFailPoint": "failCommand", + "mode": { + "times": 2 + }, + "data": { + "failCommands": [ + "find", "saslContinue" + ], + "errorCode": 391 + } + } + +- Perform a ``find`` operation that succeeds. +- Assert that the request callback has been called three times. +- Close the client. + Fails ~~~~~ - Create a default OIDC client. From 814044e480d654f2cbfa31ab58999c6e314c0405 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 5 Sep 2023 08:21:36 -0500 Subject: [PATCH 3/9] syntax --- source/auth/tests/mongodb-oidc.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/source/auth/tests/mongodb-oidc.rst b/source/auth/tests/mongodb-oidc.rst index ffa3a151dd..9eb5cada24 100644 --- a/source/auth/tests/mongodb-oidc.rst +++ b/source/auth/tests/mongodb-oidc.rst @@ -244,6 +244,7 @@ Fails - Force a reauthenication using a failCommand of the form: .. code:: javascript + { "configureFailPoint": "failCommand", "mode": { From 1a0fa3fe72bf2d5ad93b4875f2643192c0c9b710 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 5 Sep 2023 12:08:14 -0500 Subject: [PATCH 4/9] Update source/auth/auth.rst Co-authored-by: Anna Henningsen --- source/auth/auth.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/source/auth/auth.rst b/source/auth/auth.rst index b6b9dc6e0f..82420f711b 100644 --- a/source/auth/auth.rst +++ b/source/auth/auth.rst @@ -1181,8 +1181,7 @@ MONGODB-OIDC :since: 7.0 Enterprise MONGODB-OIDC authenticates using an `OIDC `_ access tokens. -Drivers MAY implement the human authentication workflow with ``REQUEST_TOKEN_CALLBACK``, but it is only currently required in the Node driver, -to support usage in the MongoDB Shell. The machine authentication workflow is currently not supported. +Drivers MAY implement the human authentication workflow with ``REQUEST_TOKEN_CALLBACK``. `MongoCredential`_ Properties ````````````````````````````` From d22ff82d6966f7e22ed5b12b4656c3accb92bd29 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 5 Sep 2023 12:08:26 -0500 Subject: [PATCH 5/9] Update source/auth/auth.rst Co-authored-by: Anna Henningsen --- source/auth/auth.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/auth/auth.rst b/source/auth/auth.rst index 82420f711b..6177c41fc2 100644 --- a/source/auth/auth.rst +++ b/source/auth/auth.rst @@ -1295,7 +1295,7 @@ If using the ``JwtStepRequest`` directly, the conversation will look like: | S: :javascript:`{conversationId : 1, payload: BinData(0,"..."), done: true, ok: 1}` Drivers MUST NOT send a ``PrincipalStepRequest`` when performing machine authentication -or when there is a cached Access Token. Drivers must instead use ``saslStart`` with a ``JwtStepRequest``. +or when there is a cached Access Token. Drivers MUST instead use ``saslStart`` with a ``JwtStepRequest``. Caching ``````` From c646bcf30eae56b92030c0da0130487f2ffe2758 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 5 Sep 2023 12:11:21 -0500 Subject: [PATCH 6/9] address review --- source/auth/auth.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/source/auth/auth.rst b/source/auth/auth.rst index 6177c41fc2..dc5aacb826 100644 --- a/source/auth/auth.rst +++ b/source/auth/auth.rst @@ -1205,7 +1205,7 @@ mechanism_properties providing objects as mechanism property values. Otherwise the driver MUST allow it as a MongoClientOption. ALLOWED_HOSTS The list of allowed hostnames or ip-addresses (ignoring ports) for - MongoDB connections. The hostnames may include a leading "*." wildcard, which allows for matching + MongoDB connections. The hostnames may include a leading "\*." wildcard, which allows for matching (potentially nested) subdomains. ALLOWED_HOSTS is a security feature and MUST default to ``["*.mongodb.net", '*.mongodb-dev.net", "*.mongodbgov.net", "localhost", "127.0.0.1", "::1"]``. @@ -1339,6 +1339,9 @@ An example might look like: // Timeout in seconds for the callback. Optionally, timeoutContext instead if applicable to language. timeoutSeconds: int; + // The version of the callback parameter interface. + version: int; + // The refresh token, if applicable, to be used by the callback to request a new token from the issuer. refreshToken?: str; } From da89ffaeca7c3a9221fe2ad88a663b90b2cf9df4 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 19 Sep 2023 21:58:35 -0500 Subject: [PATCH 7/9] Update source/auth/auth.rst Co-authored-by: Matt Dale <9760375+matthewdale@users.noreply.github.com> --- source/auth/auth.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/auth/auth.rst b/source/auth/auth.rst index dc5aacb826..ed9fc5819e 100644 --- a/source/auth/auth.rst +++ b/source/auth/auth.rst @@ -1353,7 +1353,7 @@ An example might look like: Before calling the callback, the driver MUST acquire a lock unique to the ``MongoClient``. The driver MUST ensure that credentials have not changed between when the lock was requested and when it was acquired. The lock MUST be released when the callback call as finished or errored. -This is because the ``REQUEST_TOKEN_CALLBACK`` may involve human interaction, and refresh tokens migh only be able to be used used once. +This is because the ``REQUEST_TOKEN_CALLBACK`` may involve human interaction, and refresh tokens might only be able to be used used once. If the callback does not return an object in the correct form of ``IdpResponse``, the driver MUST raise an error either using the type system or by raising an error when non-optional properties are missing . From 8a631ed1cea39d439e367fc36ec911f7883385c4 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 19 Sep 2023 22:05:20 -0500 Subject: [PATCH 8/9] address review --- source/auth/auth.rst | 20 ++++++++++---------- source/auth/tests/mongodb-oidc.rst | 3 ++- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/source/auth/auth.rst b/source/auth/auth.rst index ed9fc5819e..c4e0b6be00 100644 --- a/source/auth/auth.rst +++ b/source/auth/auth.rst @@ -1187,7 +1187,7 @@ Drivers MAY implement the human authentication workflow with ``REQUEST_TOKEN_CAL ````````````````````````````` username - MUST NOT be specified in machine authentication. Drivers MUST allow the user to specify this in the human authentication. + MUST NOT be specified in machine authentication. Drivers MUST allow the user to specify this in the human authentication workflow. If a user omits this when multiple OIDC providers are configured, the server will produce an error during authentication. source @@ -1229,7 +1229,7 @@ in the payload as octet sequences defining BSON objects: // Driver’s opening request in saslStart. interface PrincipalStepRequest { // Name of the OIDC user Principal. - n?: str; + n: Optional; } Note that the principal name is optional as it may be provided by the IdP in environments where only one IdP is used. @@ -1242,13 +1242,13 @@ If given, then ``username`` provided by the user MUST be used as the Principal ` // URL which describes the Authentication Server. This identifier should be // the iss of provided access tokens, and be viable for RFC8414 // metadata discovery and RFC9207 identification. - issuer: str; + issuer: string; // Unique client ID for this OIDC client. - clientId: str; + clientId: string; // Additional scopes to request from IdP. - requestScopes?: Array; + requestScopes: Optional>; } The server will use Principal ``(n)`` if provided in the driver’s ``PrincipalStepRequest`` to select an appropriate IdP. @@ -1261,7 +1261,7 @@ This Access Token will be used as the JWT in the driver’s ``JwtStepRequest`` t // Client's request with signed token. interface JwtStepRequest: // Compact serialized JWT with signature. - jwt: str; + jwt: string; } The IdP response that is expected to be returned by the ``REQUEST_TOKEN_CALLBACK`` is as follows: @@ -1271,13 +1271,13 @@ The IdP response that is expected to be returned by the ``REQUEST_TOKEN_CALLBACK // The result of a token request. interface IdPResponse { // The oidc access token. - accessToken: str; + accessToken: string; // The OIDC refresh token. - refreshToken?: str; + refreshToken: Optional; // The expiration time in seconds from the current time (ignored). - expiresInSeconds?: str; + expiresInSeconds: Optional; } Conversation @@ -1343,7 +1343,7 @@ An example might look like: version: int; // The refresh token, if applicable, to be used by the callback to request a new token from the issuer. - refreshToken?: str; + refreshToken: Optional; } .. code: typescript diff --git a/source/auth/tests/mongodb-oidc.rst b/source/auth/tests/mongodb-oidc.rst index 9eb5cada24..50cd4e500c 100644 --- a/source/auth/tests/mongodb-oidc.rst +++ b/source/auth/tests/mongodb-oidc.rst @@ -266,7 +266,8 @@ Separate Connections Avoid Extra Callback Calls ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The following test assumes that the driver will be able to share a cache between two MongoClient objects, or ensure that the same MongoClient is used with two -different connections. If that is not possible, the test may be skipped. +different connections. Otherwise, the test would have a race condition. +If neither is possible, the test may be skipped. - Create a request callback that returns valid, and ensure that we can record the number of times the callback is called. From 5af02a41aace96418035c8cea9a3986643349da8 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Tue, 19 Sep 2023 22:06:17 -0500 Subject: [PATCH 9/9] add qa --- source/auth/auth.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/auth/auth.rst b/source/auth/auth.rst index c4e0b6be00..46ea9db614 100644 --- a/source/auth/auth.rst +++ b/source/auth/auth.rst @@ -1208,7 +1208,7 @@ mechanism_properties MongoDB connections. The hostnames may include a leading "\*." wildcard, which allows for matching (potentially nested) subdomains. ALLOWED_HOSTS is a security feature and MUST default to - ``["*.mongodb.net", '*.mongodb-dev.net", "*.mongodbgov.net", "localhost", "127.0.0.1", "::1"]``. + ``["*.mongodb.net", '*.mongodb-qa.net", '*.mongodb-dev.net", "*.mongodbgov.net", "localhost", "127.0.0.1", "::1"]``. When ``MONGODB-OIDC`` authentication is attempted against a hostname that does not match any of list of allowed hosts, the driver MUST raise a client-side error without invoking any user-provided