Skip to content

Conversation

@kevinAlbs
Copy link
Collaborator

Summary

Support ENVIRONMENT:azure for MONGODB-OIDC.

  • Extend mcd-azure.h to support Azure IMDS requests for OIDC. This internal header previously only supported Azure IMDS requests to Azure Key Vault for In-Use Encryption.
  • Add a percent encoding utility to encode the token resource string.

Patch build: https://spruce.mongodb.com/version/6904afbc379d3a0007e1ac83

Testing

Evergreen testing is described in the drivers-evergreen-tools README.md. Testing follows a similar pattern to the testazurekms-task: build test-libmongoc on an Evergreen host, create a remote Azure VM with a matching OS, copy the binary, run the test.

oidc-compile-azure.sh includes a temporary workaround to install UV (as was done in #2163).

@kevinAlbs kevinAlbs marked this pull request as ready for review October 31, 2025 17:27
@kevinAlbs kevinAlbs requested a review from a team as a code owner October 31, 2025 17:27
@kevinAlbs kevinAlbs requested review from connorsmacd and removed request for mdb-ad November 10, 2025 14:30
Comment on lines 31 to 40
cmake \
-DENABLE_SASL=OFF \
-DENABLE_SNAPPY=OFF \
-DENABLE_ZSTD=OFF \
-DENABLE_ZLIB=OFF \
-DENABLE_SRV=OFF \
-DENABLE_CLIENT_SIDE_ENCRYPTION=OFF \
-DENABLE_EXAMPLES=OFF \
-DENABLE_SRV=OFF \
-S. -Bcmake-build
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
cmake \
-DENABLE_SASL=OFF \
-DENABLE_SNAPPY=OFF \
-DENABLE_ZSTD=OFF \
-DENABLE_ZLIB=OFF \
-DENABLE_SRV=OFF \
-DENABLE_CLIENT_SIDE_ENCRYPTION=OFF \
-DENABLE_EXAMPLES=OFF \
-DENABLE_SRV=OFF \
-S. -Bcmake-build
cmake_flags=(
-DENABLE_SASL=OFF
-DENABLE_SNAPPY=OFF
-DENABLE_ZSTD=OFF
-DENABLE_ZLIB=OFF
-DENABLE_SRV=OFF
-DENABLE_CLIENT_SIDE_ENCRYPTION=OFF
-DENABLE_EXAMPLES=OFF
-DENABLE_SRV=OFF
)
cmake "${cmake_flags[@]}" -Bcmake-build

Avoid awkward backslashes + redundant -S for current working directory as source.

-DENABLE_EXAMPLES=OFF \
-DENABLE_SRV=OFF \
-S. -Bcmake-build
cmake --build cmake-build --target test-libmongoc --parallel
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
cmake --build cmake-build --target test-libmongoc --parallel
cmake --build cmake-build --target test-libmongoc

Ninja generator already uses max parallelism by default.

# shellcheck source=.evergreen/scripts/use-tools.sh
. "$(dirname "${BASH_SOURCE[0]}")/use-tools.sh" paths # Sets MONGOC_DIR

cd "$MONGOC_DIR"
Copy link
Contributor

Choose a reason for hiding this comment

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

Working directory is already set to mongoc via the EVG task definition:

bash_exec(
    working_dir="mongoc",
    add_expansions_to_env=True,
    command_type=EvgCommandType.TEST,
    script='.evergreen/scripts/oidc-azure-compile.sh',
),

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed unnecessary cd and include of use-tools.sh.

Comment on lines 48 to 52
tar -czf test-libmongoc.tar.gz \
.evergreen/scripts/oidc-azure-test.sh \
./cmake-build/src/libmongoc/test-libmongoc \
src/libmongoc/tests/json \
src/libbson/tests/json
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
tar -czf test-libmongoc.tar.gz \
.evergreen/scripts/oidc-azure-test.sh \
./cmake-build/src/libmongoc/test-libmongoc \
src/libmongoc/tests/json \
src/libbson/tests/json
files=(
.evergreen/scripts/oidc-azure-test.sh
cmake-build/src/libmongoc/test-libmongoc
src/libmongoc/tests/json
src/libbson/tests/json
)
tar -czf test-libmongoc.tar.gz "${files[@]}"

Avoid awkward backslashes.

# Install required OpenSSL runtime library.
sudo apt install -y libssl-dev

./cmake-build/src/libmongoc/test-libmongoc -d --match '/auth/unified/*' --match '/oidc/*'
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this include -f/--no-fork?

Aside: I think most scripts use the shorter -l instead of --match.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I left it out --no-fork for now. Leaving out --no-fork has the benefit of reporting all failing tests, but I expect is slower. In this case, it is likely a small difference since the forked tests take 3 seconds only. Another comment notes "ASAN does not play well with spawned processes", but this task does not currently use ASAN.

Updated to use -l for consistency with run-tests.sh.

const char *const opt_imds_host,
int opt_port,
const char *opt_extra_headers,
int opt_timeout_ms,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
int opt_timeout_ms,
int32_t opt_timeout_ms,

We really need to address CDRIVER-1329 and CDRIVER-4589 sometime soon so that new API does not continue to be burdened by int32_t compatibility issues. 🫠

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Switched to mlib_duration for the timeout parameter. Submitted #2168 to replace the (now redundant) mcd-time.h with mlib utilities.

Comment on lines 59 to 70
if (timeout_us) {
int64_t remaining_ms = (*timeout_us - bson_get_monotonic_time()) / 1000;
if (remaining_ms <= 0) {
// No time remaining. Immediately fail.
mongoc_oidc_callback_params_cancel_with_timeout(params);
goto fail;
}
if (mlib_narrow(&max_duration_ms, remaining_ms)) {
// Requested timeout too large to fit. Cap at INT_MAX.
max_duration_ms = mlib_maxof(int);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (timeout_us) {
int64_t remaining_ms = (*timeout_us - bson_get_monotonic_time()) / 1000;
if (remaining_ms <= 0) {
// No time remaining. Immediately fail.
mongoc_oidc_callback_params_cancel_with_timeout(params);
goto fail;
}
if (mlib_narrow(&max_duration_ms, remaining_ms)) {
// Requested timeout too large to fit. Cap at INT_MAX.
max_duration_ms = mlib_maxof(int);
}
}
if (timeout_us) {
const mlib_timer timer = {.expires_at = mlib_duration(*timeout_us, us)};
if (mlib_timer_is_expired(timer, NULL)) {
// No time remaining. Immediately fail.
mongoc_oidc_callback_params_cancel_with_timeout(params);
goto fail;
}
if (mlib_narrow(&max_duration_ms, mlib_milliseconds_count(mlib_timer_remaining(timer)))) {
// Requested timeout too large to fit. Cap at INT_MAX.
max_duration_ms = mlib_maxof(int);
}
}

Suggest implementing new timer/expiry/duration code in terms of mlib utilities when able.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Implemented with slight tweak (.expires_at is a mlib_time_point, not a mlib_duration).

Comment on lines 214 to 217
int timeout_ms = 3 * 1000; // Default 3 second timeout
if (opt_timeout_ms > 0) {
timeout_ms = opt_timeout_ms;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
int timeout_ms = 3 * 1000; // Default 3 second timeout
if (opt_timeout_ms > 0) {
timeout_ms = opt_timeout_ms;
}
const mlib_time_point now = mlib_now();
mlib_timer timer = mlib_expires_at(mlib_time_add(now, (3, s))); // Default 3 second timeout.
if (opt_timeout_ms > 0) {
timer = mlib_expires_at(mlib_time_add(now, (opt_timeout_ms, ms)));
}

Suggest implementing new timer/expiry/duration code in terms of mlib utilities when able.


ret = mongoc_oidc_credential_new_with_expires_in(token.access_token, mcd_get_microseconds(token.expires_in));
if (!ret) {
MONGOC_ERROR("Failed to process Azure OIDC access token");
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggest providing more descriptive error messages for users when able, e.g.:

if (!ret) {
  MONGOC_ERROR("Failed to process Azure OIDC access token");

  if (!token.access_token) {
    MONGOC_ERROR("missing Azure OIDC access token string");
  }

  if (token.expires_in < 0) {
    MONGOC_ERROR("Azure OIDC access token expiration must not be a negative value");
  }

  goto fail;
}

Comment on lines +256 to +259
if (mongoc_oidc_callback_params_get_cancelled_with_timeout(params)) {
SET_ERROR("MONGODB-OIDC callback was cancelled due to timeout");
goto unlock_and_return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Confirming: this will surface timeouts as an authentication error via mongoc_cluster_run_command_monitored or _mongoc_cluster_auth_node. Is this correct?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think yes. I expect an error is appropriate since the callback failed to get an auth token. This is intended to give the reason rather than the more generic "MONGODB-OIDC callback failed" message.

@kevinAlbs kevinAlbs requested a review from eramongodb November 12, 2025 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants