Skip to content

GODRIVER-3454 Support custom AWS credential provider.#2228

Draft
qingyang-hu wants to merge 15 commits intomasterfrom
godriver3615
Draft

GODRIVER-3454 Support custom AWS credential provider.#2228
qingyang-hu wants to merge 15 commits intomasterfrom
godriver3615

Conversation

@qingyang-hu
Copy link
Contributor

@qingyang-hu qingyang-hu commented Oct 25, 2025

GODRIVER-3454
GODRIVER-3615

Summary

  • Support custom AWS credential provider.
  • Add an unexported awsSigner interface to make the official AWS SDK migration easier in the future.

Background & Motivation

@mongodb-drivers-pr-bot
Copy link
Contributor

mongodb-drivers-pr-bot bot commented Oct 25, 2025

🧪 Performance Results

Commit SHA: a61c360

The following benchmark tests for version 69866eb3dc65520007412f7b had statistically significant changes (i.e., |z-score| > 1.96):

Benchmark Measurement % Change Patch Value Stable Region H-Score Z-Score
BenchmarkMultiFindMany ops_per_second_min -85.7369 4991.5393 Avg: 34996.0609
Med: 35294.1810
Stdev: 15227.4791
0.7116 -1.9704
BenchmarkBSONDeepDocumentDecoding ops_per_second_max -3.3854 16659.1700 Avg: 17242.9046
Med: 17186.8555
Stdev: 265.7632
0.7615 -2.1964
BenchmarkBSONDeepDocumentEncoding total_time_seconds -1.6571 1.1741 Avg: 1.1939
Med: 1.1944
Stdev: 0.0090
0.7723 -2.1889

For a comprehensive view of all microbenchmark results for this PR's commit, please check out the Evergreen perf task for this patch.

@mongodb-drivers-pr-bot
Copy link
Contributor

mongodb-drivers-pr-bot bot commented Oct 25, 2025

API Change Report

./v2/mongo/options

compatible changes

(*AutoEncryptionOptions).SetAWSCredentialsProvider: added
(*ClientEncryptionOptionsBuilder).SetAWSCredentialsProvider: added
AWSCredentials: added
AWSCredentialsProvider: added
AutoEncryptionOptions.AWSCredentialsProvider: added
ClientEncryptionOptions.AWSCredentialsProvider: added
Credential.AWSCredentialsProvider: added

./v2/x/mongo/driver

compatible changes

AWSCredentialsProvider: added
Cred.AWSCredentialsProvider: added

./v2/x/mongo/driver/mongocrypt/options

compatible changes

MongoCryptOptions.AWSCredentialsProvider: added

@qingyang-hu qingyang-hu force-pushed the godriver3615 branch 12 times, most recently from a1b8f64 to 795b157 Compare October 27, 2025 17:13
})
}

func TestCustomAwsCredentialsProse(t *testing.T) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Put this aside from TestClientSideEncryptionProse so Setenv() can be used without being influenced by t.Parallel().

@qingyang-hu qingyang-hu marked this pull request as ready for review October 27, 2025 18:30
@qingyang-hu qingyang-hu requested a review from a team as a code owner October 27, 2025 18:30
@qingyang-hu qingyang-hu requested review from Copilot, matthewdale and prestonvasquez and removed request for prestonvasquez October 27, 2025 18:30
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds support for custom AWS credential providers, enabling applications to supply their own AWS credential retrieval logic for both authentication and client-side encryption. The implementation allows users to provide custom credential providers as a callback function that returns AWS credentials on demand.

Key changes:

  • Added AwsCredentialsProvider callback field to credential options for connection authentication
  • Extended client-side encryption options to support custom credential providers via CredentialProviders map
  • Refactored internal credential provider interface to use context-aware Retrieve(context.Context) method consistently across all provider implementations

Reviewed Changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
mongo/options/clientoptions.go Added AwsCredentialsProvider field and Credentials type to support custom AWS credential callbacks
mongo/options/clientencryptionoptions.go Added CredentialProviders map and setter for custom provider configuration
mongo/options/autoencryptionoptions.go Added CredentialProviders field and setter for auto-encryption provider configuration
x/mongo/driver/topology/topology_options.go Converted user-provided credential provider to internal format during credential conversion
x/mongo/driver/driver.go Added AwsCredentialsProvider field to Cred struct for internal provider storage
x/mongo/driver/auth/mongodbaws.go Integrated custom credential provider into AWS authentication flow
x/mongo/driver/mongocrypt/options/mongocrypt_options.go Added CredentialProviders field to MongoCrypt options
x/mongo/driver/mongocrypt/mongocrypt.go Wired custom credential providers into MongoCrypt initialization
mongo/client.go Converted credential providers for auto-encryption setup
mongo/client_encryption.go Converted credential providers for client encryption setup
internal/credproviders/aws_provider.go New provider implementation that wraps custom AWS credential callbacks
internal/aws/types.go Added public Credentials type for custom provider return values
internal/aws/credentials/credentials.go Unified provider interface to use context-aware Retrieve(context.Context) method
internal/credproviders/*.go Updated all provider implementations to use context-aware retrieval
x/mongo/driver/auth/mongodbaws_test.go Added tests for custom credential provider behavior
internal/integration/client_side_encryption_prose_test.go Added integration tests for custom credential provider scenarios
mongo/client_examples_test.go Added example demonstrating custom credential provider usage

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 43 to 45
if a.credentials == nil || a.credentials.ExpirationCallback == nil {
return true
}
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

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

The IsExpired logic returns true when credentials are nil, which means the provider will always be considered expired before the first retrieval. This forces an immediate retrieval, but if ExpirationCallback is also nil after the first retrieval, it will always return true, causing credentials to be re-fetched on every call. Consider returning a.credentials == nil when ExpirationCallback is nil to avoid unnecessary re-fetching after initial retrieval.

Suggested change
if a.credentials == nil || a.credentials.ExpirationCallback == nil {
return true
}
if a.credentials == nil {
return true
}
if a.credentials.ExpirationCallback == nil {
return false
}

Copilot uses AI. Check for mistakes.
mongo/client.go Outdated
Comment on lines 604 to 617
providers[k] = &credproviders.AwsProvider{
Provider: func(ctx context.Context) (aws.Credentials, error) {
var creds aws.Credentials
c, err := fn(ctx)
if err != nil {
return creds, err
}
creds.AccessKeyID = c.AccessKeyID
creds.SecretAccessKey = c.SecretAccessKey
creds.SessionToken = c.SessionToken
creds.ExpirationCallback = c.ExpirationCallback
return creds, nil
},
}
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

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

The closure over fn in the loop may capture the wrong value if the loop variable is reused. While this loop only processes the 'aws' key specifically, consider using a function parameter or declaring fn within the block to ensure correct closure capture: provider := fn before the closure.

Copilot uses AI. Check for mistakes.
Comment on lines 62 to 75
providers[k] = &credproviders.AwsProvider{
Provider: func(ctx context.Context) (aws.Credentials, error) {
var creds aws.Credentials
c, err := fn(ctx)
if err != nil {
return creds, err
}
creds.AccessKeyID = c.AccessKeyID
creds.SecretAccessKey = c.SecretAccessKey
creds.SessionToken = c.SessionToken
creds.ExpirationCallback = c.ExpirationCallback
return creds, nil
},
}
Copy link

Copilot AI Oct 27, 2025

Choose a reason for hiding this comment

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

The closure over fn in the loop may capture the wrong value if the loop variable is reused. While this loop only processes the 'aws' key specifically, consider using a function parameter or declaring fn within the block to ensure correct closure capture: provider := fn before the closure.

Copilot uses AI. Check for mistakes.
@qingyang-hu qingyang-hu marked this pull request as draft October 28, 2025 21:37
@qingyang-hu qingyang-hu force-pushed the godriver3615 branch 2 times, most recently from 1a5bc81 to 4fea597 Compare October 30, 2025 20:03
@qingyang-hu qingyang-hu marked this pull request as ready for review October 30, 2025 21:45
}

// CredentialsProvider is the function type that returns AWS credentials.
type CredentialsProvider func(context.Context) (Credentials, error)
Copy link
Member

@prestonvasquez prestonvasquez Nov 13, 2025

Choose a reason for hiding this comment

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

Thanks for this work @qingyang-hu! Before committing to a detailed review, I'd like to align with the team on the overall approach for providing custom credentials by clarifying that there are (at least) two approaches:

  1. The callback approach suggested in this PR
  2. The submodule to use the official aws-sdk-go-v2 as prescribed in GODRIVER-3567 and POC’d in PR #2057

Here is how I anticipate folks would use the submodule approach (see here for required POC updates):

// Custom credentials provider using the aws-sdk-go-v2
myProvider := aws.CredentialsProviderFunc(func(ctx context.Context) (aws.Credentials, error) {
    return aws.Credentials{
        AccessKeyID:     "USER_ACCESS_KEY",
        SecretAccessKey: "USER_SECRET_KEY",
        SessionToken:    "USER_SESSION_TOKEN",
        Source:          "CustomProvider",
    }, nil
})

// Load AWS config with custom credentials provider using the aws-sdk-go-v2
awsCfg, _ := config.LoadDefaultConfig(
    ctx,
    config.WithCredentialsProvider(myProvider))

opts := options.Client().ApplyURI(uri)

// Extend options to include awsConfig using mongoaws
opts = mongoawsv2.WithAWSConfig(opts, awsConfig)

// Use with MongoDB
client, _ := mongo.Connect(opts)

The pros of the submodule solution:

  • Ideal for power users
  • Zero AWS dependencies in the Go Driver while still getting official AWS SDK support
  • The solution is future proof in that we can add mongoawsv3, mongoawsv4, etc. whenever needed
  • Much better user experience than having to define an abstract credential object that could apply to any provider
  • We can deprecate and no longer add to the AWS legacy code.

The Cons of the submodule solution:

  • We have to maintain the legacy behavior until v3
  • Requires an extra import when working with AWS

A potential concern is that the opaque any return type reduces compile-time type safety, but this is validated at connection time via mongo.Connect(), which is acceptable.


If we think custom credentials are rarely needed (power users only), then I suggest prioritizing GODRIVER-3567 (submodule approach) to avoid committing AWS-specific types to the stable API. Power users who need custom credentials likely already use AWS SDK v2 in their applications, so the dependency is acceptable. This prevents long-term technical debt from maintaining options.Credentials.

Otherwise, if credentials have broader appeal or users want to avoid AWS SDK v2 dependency, then this PR is acceptable imo. However, if we merge, I suggest we prioritize implementing GODRIVER-3567 afterward and clearly document it as the preferred approach for users already using AWS SDK v2 by deprecating options.Credentials.

IIUC, the SetCredentialProviders portion for CSFLE provides value regardless of which path we choose for connection authentication.

CC: @matthewdale

Copy link
Contributor

Choose a reason for hiding this comment

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

Addressing the pros:

Ideal for power users

The user experience for both the callback and submodule approach are very similar.

Zero AWS dependencies in the Go Driver while still getting official AWS SDK support

That is true for the callback approach as well. Dependency management for the callback approach is arguably simpler because you don't have to worry about what version of the AWS SDK the "mongoawsv2" submodule depends on.

The solution is future proof in that we can add mongoawsv3, mongoawsv4, etc. whenever needed

I think we can address future AWS SDK changes in either approach. The structure of AWS credentials and credentials providers has barely changed over the lifetime of both AWS SDK major versions.

Much better user experience than having to define an abstract credential object that could apply to any provider

I agree with this. We should make the credential and credential provider types in this PR specific to AWS.

We can deprecate and no longer add to the AWS legacy code.

There are two sections of AWS-specific code in the Go Driver:

  1. Code to support the server's "MONGODB-AWS" auth API.
  2. Code to fetch AWS credentials from different environments.

Section 1 is specific to the MongoDB server API and is something we will have to continue maintaining as long as the server supports it, whether in the main Go Driver module or in an AWS-specific submodule. Section 2 is what we want to delegate to the AWS SDK.

Addressing the cons:

We have to maintain the legacy behavior until v3

I think this is true for either approach.

Requires an extra import when working with AWS

I agree with this con.


E.g. user experience using the callback approach (with the Go Driver types renamed to be AWS-specific):

myProvider := aws.CredentialsProviderFunc(func(ctx context.Context) (aws.Credentials, error) {
	return aws.Credentials{
		AccessKeyID:     "USER_ACCESS_KEY",
		SecretAccessKey: "USER_SECRET_KEY",
		SessionToken:    "USER_SESSION_TOKEN",
		Source:          "CustomProvider",
	}, nil
})

// Load AWS config with custom credentials provider using the aws-sdk-go-v2
awsCfg, _ := config.LoadDefaultConfig(
	context.Background(),
	config.WithCredentialsProvider(myProvider))

opts := options.Client().
	ApplyURI("mongodb://localhost:27017").
	SetAuth(options.Credential{
		AWSCredentialsProvider: func(ctx context.Context) (options.AWSCredentials, error) {
			cfg, err := awsCfg.Credentials.Retrieve(ctx)
			if err != nil {
				return options.AWSCredentials{}, err
			}

			return options.AWSCredentials(cfg), nil
		},
	})
client, _ := mongo.Connect(opts)

Overall, I think the callback approach requires fewer moving pieces and has an almost identical user experience. Also, it seems to align a little better with the API in other drivers (e.g. mongodb/node-mongodb-native#4373) considering DRIVERS-3194 was closed as a duplicate of DRIVERS-2903. I think we should move forward with the callback approach, amending this PR to make the credentials and credentials provider types AWS-specific.

Copy link
Member

@prestonvasquez prestonvasquez Nov 18, 2025

Choose a reason for hiding this comment

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

@matthewdale

The user experience for both the callback and submodule approach are very similar.

I see it a bit differently. For example, if a caller needs to inject AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE to obtain credentials from the EKS Pod Identity Agent, the callback approach would require you the user to

  1. Read both the relative URI and token file from environment variables
  2. Make the HTTP request with the Authorization header set to the token value
  3. Parses the ECS response and returns credentials in the format the driver expects
  4. Pass this function to AwsCredentialsProvider (for auth) or SetCredentialProviders (for encryption)
// callback for to obtain credentials from the EKS Pod Identity Agent
func awsCredentialProvider(ctx context.Context) (options.Credentials, error) {
    awsCfg, err := config.LoadDefaultConfig(ctx)
    if err != nil {
        return options.Credentials{}, err
    }
    
    creds, err := awsCfg.Credentials.Retrieve(ctx)
    if err != nil {
        return options.Credentials{}, err
    }
    
    return options.Credentials{
        AccessKeyID:     creds.AccessKeyID,
        SecretAccessKey: creds.SecretAccessKey,
        SessionToken:    creds.SessionToken,
        Source:          creds.Source,
        CanExpire:       creds.CanExpire,
        Expires:         creds.Expires,
    }, nil
}

Shipping a standalone implementation upfront gives power users a supported hook, which is a much better UX:

// Load AWS config with default credential chain
// This automatically supports:
// - Environment variables
// - AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
// - AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE (automatically read and sent)
// - EC2 instance metadata
// - ECS container credentials
// - etc.
awsCfg, err := config.LoadDefaultConfig(ctx)
if err != nil {
    panic(err)
}

opts := options.Client().ApplyURI(uri)

// Extend options to include awsConfig using mongoawsv2
opts = mongoawsv2.WithAWSConfig(opts, awsCfg)

// Use with MongoDB - the AWS SDK will handle the token file automatically
client, err := mongo.Connect(opts)
if err != nil {
    panic(err)
}

That is true for the callback approach as well. Dependency management for the callback approach is arguably simpler because you don't have to worry about what version of the AWS SDK the "mongoawsv2" submodule depends on.

That's a fair point about avoiding a direct dependency. However, I'm concerned that copying SDK code (e.g., a signer) introduces security risks: if the SDK maintainers patch a CVE, we might not catch it in time and could continue shipping a vulnerable implementation. By relying directly on the SDK, we can benefit from their security updates automatically. Do you see a way to mitigate that risk with the current implementation?

We should make the credential and credential provider types in this PR specific to AWS.

From what I've seen, it seems like we'd be building and maintaining AWS-specific logic that essentially acts as a shim around the SDK. Is there a precedent or pattern you're pulling from that makes this the preferred approach? It seems like just relying on the SDK seems far more prudent.

Section 1 is specific to the MongoDB server API and is something we will have to continue maintaining as long as the server supports it

That's the longterm (v3) goal of GODRIVER-3567 and PR 2057, to containerize that logic into its own submodule, which would allow us to remove the AWS v1 legacy code from the driver. I realize this needs better documentation. I think this approach could give us a cleaner separation and make future maintenance easier. Does that address your concern?

Also, it seems to align a little better with the API in other drivers (e.g. mongodb/node-mongodb-native#4373) considering DRIVERS-3194 was closed as a duplicate of DRIVERS-2903

To clarify: DRIVERS-3194 was closed as a duplicate specifically because a submodule solution was expected to satisfy the requirements of DRIVERS-2903. The submodule approach was the intended path forward for addressing both tickets.


The callback solution is fair, but I think we should give strong consideration to the SDK solution as our end goal. If we implement the callback and then agree that the SDK solution is long-term better, we'd essentially need to turn around and deprecate it once we ship the submodule which feels like unnecessary churn.

To recap, my main concerns with the callback approach:

  • We miss upstream CVE patches if we copy SDK code (not huge, given users can update Go toolchain locally)
  • Power users still need workarounds for cases like EKS Pod Identity to avoid complex overhead
  • We're already removing v1 AWS code (GODRIVER-3570), so this adds another deprecation cycle

For other drivers:

  • Node uses an opt-in dependency for aws4 for signing, but plans to copy signing into the driver to reduce their overall dependency count as part of their serverless driver initiative, since the SigV4 signing algorithm is straightforward and spec-defined.
  • Python relies on the SDK for all aspects of AWS Auth, but it is an opt-in dependency
  • .NET also uses a standalone module that takes a dependency on the official AWS SDK, and will be fully integrated with this solution as of SHARP-4390

)

// Credentials represents AWS credentials.
type Credentials struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should rename this to AWSCredentials since it seems to be specific to the MONGODB-AWS auth mechanism.

}

// CredentialsProvider is the function type that returns AWS credentials.
type CredentialsProvider func(context.Context) (Credentials, error)
Copy link
Contributor

Choose a reason for hiding this comment

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

We should rename this to AWSCredentialsProvider since it seems to be specific to the MONGODB-AWS auth mechanism.

Props map[string]string
OIDCMachineCallback OIDCCallback
OIDCHumanCallback OIDCCallback
AwsCredentialsProvider func(context.Context) (aws.Credentials, error)
Copy link
Contributor

Choose a reason for hiding this comment

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

Initialisms in Go are conventionally all caps. This should be AWSCredentialsProvider.

@qingyang-hu qingyang-hu force-pushed the godriver3615 branch 4 times, most recently from 57dab25 to 61ee02f Compare January 16, 2026 20:21
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 43 out of 44 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

nonce := make([]byte, 64)
copy(nonce, n.Nonce.Data)

writeReplies(resps,
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

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

The function writeReplies is called but not defined in this file. This function should be added to write replies to the channel, similar to other test files in the auth package. The function should iterate over the provided documents and write them to the channel using drivertest.MakeReply.

Copilot uses AI. Check for mistakes.
// NewCredentialsProvider() adapts an AWS CredentialsProvider to be used in:
//
// ClientOptions
// ClinetEncryptionOptions
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

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

Typo in comment: "ClinetEncryptionOptions" should be "ClientEncryptionOptions".

Suggested change
// ClinetEncryptionOptions
// ClientEncryptionOptions

Copilot uses AI. Check for mistakes.
Comment on lines 140 to 159
type awsCredentialsProvider struct {
provider options.AWSCredentialsProvider
}

func (p awsCredentialsProvider) Retrieve(ctx context.Context) (credentials.Value, error) {
creds, err := p.provider.Retrieve(ctx)
if err != nil {
return credentials.Value{}, err
}
return credentials.Value{
AccessKeyID: creds.AccessKeyID,
SecretAccessKey: creds.SecretAccessKey,
SessionToken: creds.SessionToken,
Source: creds.Source,
CanExpire: creds.CanExpire,
Expires: creds.Expires,
AccountID: creds.AccountID,
ProviderName: "AwsProvider",
}, nil
}
Copy link

Copilot AI Jan 17, 2026

Choose a reason for hiding this comment

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

There is code duplication of the awsCredentialsProvider adapter type between this file and mongo/client_encryption.go. Both types have identical implementations that convert options.AWSCredentialsProvider to credentials.Provider. Consider extracting this to a shared internal package to avoid duplication and ensure consistency.

Copilot uses AI. Check for mistakes.
@qingyang-hu qingyang-hu force-pushed the godriver3615 branch 3 times, most recently from 46fb68a to 024f1ce Compare January 22, 2026 20:43
// This package allows credential providers and signers in v2 AWS SDK to be
// supplied to the MongoDB Go Driver for use with the MONGODB-AWS authentication
// mechanism.
//
Copy link
Member

Choose a reason for hiding this comment

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

[docs] We should add context about avoiding the AWS SDK depenency in the main driver:

Suggested change
//
//
// This package is a separate module to avoid adding the AWS SDK as a dependency
// of the main driver. Users who don't need AWS authentication won't need to
// import the AWS SDK.
//

)

func ExampleNewCredentialsProvider() {
awsCredentialProvider := NewCredentialsProvider(aws.NewConfig().Credentials)
Copy link
Member

Choose a reason for hiding this comment

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

[docs] Users would typically load credentials using LoadDefaultConfig, can we update examples to do that?

cfg, err := config.LoadDefaultConfig(context.TODO())
if err != nil {
    panic(err)
}
awsCredentialProvider := NewCredentialsProvider(cfg.Credentials)

// NewCredentialsProvider returns a CredentialsProvider that wraps AWS
// CredentialsProvider. Provider is expected to not be nil.
func NewCredentialsProvider(provider aws.CredentialsProvider) *CredentialsProvider {
return &CredentialsProvider{
Copy link
Member

Choose a reason for hiding this comment

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

[question] Does this need to return a pointer references? Whatever we decide, can we make NewSigner symmetric?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Making the NewSigner() align with the NewCredentialsProvider(). Will also return an error to indicate the nil interface.


var _ options.AWSCredentialsProvider = (*CredentialsProvider)(nil)

// CredentialsProvider is an implementation of options.AWSCredentialsProvider in
Copy link
Member

Choose a reason for hiding this comment

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

[nit] This documentation is kind of missleading. CredentialsProvider is just a thin wrapper around aws.CredentialsProvider and doesn't directly provide "caching and concurrency safe credentials retrieval".

// CredentialsProvider adapts an AWS SDK v2 CredentialsProvider to the
// options.AWSCredentialsProvider interface used by the MongoDB Go Driver.

hash := sha256.Sum256([]byte(payload))
payload = hex.EncodeToString(hash[:])
}
r.Header.Set("X-Amz-Security-Token", creds.SessionToken)
Copy link
Member

Choose a reason for hiding this comment

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

AFAICT we don't unset X-Amz-Security-Token in aws_conv.go. We just use it to set another header. I think we should avoid sending an empty value, in case that has some connotation we aren't aware of.

Comment on lines +58 to 59
v.CanExpire = true
}
Copy link
Member

Choose a reason for hiding this comment

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

[question] I'm not sure I understand the logic here, shouldn't we discard the credentials if invalid? Why mark it as expirable?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a failsafe mechanism to make the credentials expire if they are invalid. Positive CanExpire ensures the Expired() returns true with a zero Expires (see the logic here).
I'm adding a comment here.

if err != nil {
return v, err
}
v.CanExpire = true
Copy link
Member

Choose a reason for hiding this comment

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

[question] Shouldn't this be in the expiration check block? What happens if expiration is 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

These credentials are expirable. Positive CanExpire ensures the Expired() returns true with a zero Expires (see the logic here). So 0 expiration makes the credentials expire.

Comment on lines 45 to 52
func (s *StaticProvider) Retrieve(_ context.Context) (credentials.Value, error) {
if !s.verified {
s.err = verify(s.Value)
s.ProviderName = staticProviderName
s.verified = true
}
return s.Value, s.err
}
Copy link
Member

Choose a reason for hiding this comment

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

[question] Is error caching necessary here?

func (s *StaticProvider) Retrieve(_ context.Context) (credentials.Value, error) {
    s.ProviderName = staticProviderName
    return s.Value, verify(s.Value)
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Credentials might be retrieved more than once, and these values are static, so we cache the error as well as the value to avoid unnecessary computation.

type serverMessage struct {
Nonce bson.Binary `bson:"s"`
Host string `bson:"h"`
type awsSaslAdapter struct {
Copy link
Member

Choose a reason for hiding this comment

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

Should we rename this file to aws_sasl_adapter.go?


type awsSaslAdapter struct {
conversation *awsConversation
type builtInV4Signer struct {
Copy link
Member

@prestonvasquez prestonvasquez Jan 26, 2026

Choose a reason for hiding this comment

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

[question] Do we need a wrapper for v4 signing? v4.Signer.Sign is only called in 1 place, here. Can't we just have that method implement awsSigner?

@matthewdale matthewdale self-requested a review February 3, 2026 05:51
@@ -0,0 +1,33 @@
module go.mongodb.org/mongo-driver/v2/awsauth
Copy link
Contributor

Choose a reason for hiding this comment

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

We're planning to tag the initial module release with v0.1, so the module name should not include "v2", but should include the "ext" dir.

Suggested change
module go.mongodb.org/mongo-driver/v2/awsauth
module go.mongodb.org/mongo-driver/ext/awsauth

Copy link
Contributor

Choose a reason for hiding this comment

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

Optional: Rename file to awsauth.go.

Copy link
Contributor

Choose a reason for hiding this comment

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

Optional: Rename file to awsauth_example_test.go.

// not use this file except in compliance with the License. You may obtain
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0

package awsauth
Copy link
Contributor

Choose a reason for hiding this comment

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

Testable examples should always be in a *_test package. That has two benefits:

  1. The example code has to import the package under test (e.g. awstest here), so it looks more like the code a customer would write.
  2. Testable example functions are rendered as full code files, including imports and a main block, in the docs site.
Suggested change
package awsauth
package awsauth_test

credential := options.Credential{
AuthMechanism: "MONGODB-AWS",
AWSCredentialsProvider: awsCredentialProvider,
AWSSigner: awsSigner,
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm concerned asking customers to specify both AWSCredentialsProvider and AWSSigner is going to create confusion. There's currently no observable behavior difference and no guidance on why a customer should specify it. Also, having to initialize and specify both a cred provider and a signer separately requires customers to write more code that could be avoided if the interfaces were combined.

We should either:

  1. Combine the AWSCredentialsProvider and AWSSigner interfaces and update the docs to say that we're planning to require customers to use awsauth for signing in Go Driver v3.
  2. Remove the AWSSigner interface and the awsauth.Signer type.

I prefer option 2 because it seems to be a better overall customer experience. While it would be nice to eventually remove the AWS v4 signing logic from the Go Driver, it doesn't seem worth a more confusing API.

Comment on lines 38 to 41
func NewCredentialsProvider(credentialsProvider aws.CredentialsProvider) (*CredentialsProvider, error) {
if credentialsProvider == nil {
return nil, errors.New("nil provider")
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We shouldn't require users to handle errors here. We should either defer this error check to Retrieve or let it panic.

@alcaeus alcaeus marked this pull request as draft February 17, 2026 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature review-priority-normal Medium Priority PR for Review: within 1 business day

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants