Skip to content

Commit

Permalink
GODRIVER-2758: Add documentation examples (mongodb#1759)
Browse files Browse the repository at this point in the history
Co-authored-by: Steven Silvester <[email protected]>
Co-authored-by: Matt Dale <[email protected]>
  • Loading branch information
3 people authored Sep 11, 2024
1 parent ac03e73 commit 485e74d
Show file tree
Hide file tree
Showing 2 changed files with 266 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ repos:
rev: "v2.2.6"
hooks:
- id: codespell
args: ["-L", "te,fo,fle,alo,nin,compres,wil,collone,asess,sav,ot,wll,dne,nulll,hellow"]
args: ["-L", "te,fo,fle,alo,nin,compres,wil,collone,asess,sav,ot,wll,dne,nulll,hellow,aks"]
exclude: ^(vendor/|benchmark/operation_test.go)
exclude_types: [json,yaml,pem]

Expand Down
265 changes: 265 additions & 0 deletions mongo/client_examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"context"
"fmt"
"log"
"os"

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
Expand Down Expand Up @@ -468,3 +469,267 @@ func ExampleConnect_bSONOptions() {
panic(err)
}
}

func ExampleConnect_oIDC() {
// The `MONGODB-OIDC authentication mechanism` is available in MongoDB 7.0+
// on Linux platforms.
//
// The MONGODB-OIDC mechanism authenticates using an OpenID Connect (OIDC)
// access token. The driver supports OIDC for workload identity, defined as
// an identity you assign to a software workload (such as an application,
// service, script, or container) to authenticate and access other services
// and resources.
//
// The driver also supports OIDC for workforce identity for a more secure
// flow with a human in the loop.

// Credentials can be configured through the MongoDB URI or as arguments in
// the options.ClientOptions struct that is passed into the mongo.Connect
// function.

// Built-in Support
// The driver has built-in support for Azure IMDS and GCP
// IMDS environments. Other environments are supported with `Custom
// Callbacks`.

// Azure IMDS
// For an application running on an Azure VM or otherwise using the `Azure
// Internal Metadata Service`, you can use the built-in support for Azure,
// where "<client_id>" below is the client id of the Azure managed identity,
// and ``<audience>`` is the url-encoded ``audience`` `configured on your
// MongoDB deployment`.
{
uri := os.Getenv("MONGODB_URI")
props := map[string]string{
"ENVIRONMENT": "azure",
"TOKEN_RESOURCE": "<audience>",
}
opts := options.Client().ApplyURI(uri)
opts.SetAuth(
options.Credential{
Username: "<client_id>",
AuthMechanism: "MONGODB-OIDC",
AuthMechanismProperties: props,
},
)
c, err := mongo.Connect(context.TODO(), opts)
if err != nil {
panic(err)
}
defer func() { _ = c.Disconnect(context.TODO()) }()
_, err = c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
if err != nil {
panic(err)
}
}

// If the application is running on an Azure VM and only one managed
// identity is associated with the VM, "username" can be omitted.

// GCP IMDS

// For an application running on an GCP VM or otherwise using the `GCP
// Internal Metadata Service`_, you can use the built-in support for GCP,
// where "<audience>" below is the url-encoded "audience" `configured on
// your MongoDB deployment`.
{
uri := os.Getenv("MONGODB_URI")
props := map[string]string{
"ENVIRONMENT": "gcp",
"TOKEN_RESOURCE": "<audience>",
}
opts := options.Client().ApplyURI(uri)
opts.SetAuth(
options.Credential{
AuthMechanism: "MONGODB-OIDC",
AuthMechanismProperties: props,
},
)
c, err := mongo.Connect(context.TODO(), opts)
if err != nil {
panic(err)
}
defer func() { _ = c.Disconnect(context.TODO()) }()
_, err = c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
if err != nil {
panic(err)
}
}

// Custom Callbacks

// For environments that are not directly supported by the driver, you can
// use options.OIDCCallback. Some examples are given below.

// AWS EKS

// For an EKS Cluster with a configured `IAM OIDC provider`, the token can
// be read from a path given by the "AWS_WEB_IDENTITY_TOKEN_FILE"
// environment variable.
{
eksCallback := func(_ context.Context,
_ *options.OIDCArgs) (*options.OIDCCredential, error) {
accessToken, err := os.ReadFile(
os.Getenv("AWS_WEB_IDENTITY_TOKEN_FILE"))
if err != nil {
return nil, err
}
return &options.OIDCCredential{
AccessToken: string(accessToken),
}, nil
}
uri := os.Getenv("MONGODB_URI")
props := map[string]string{
"ENVIRONMENT": "gcp",
"TOKEN_RESOURCE": "<audience>",
}
opts := options.Client().ApplyURI(uri)
opts.SetAuth(
options.Credential{
AuthMechanism: "MONGODB-OIDC",
AuthMechanismProperties: props,
OIDCMachineCallback: eksCallback,
},
)
c, err := mongo.Connect(context.TODO(), opts)
if err != nil {
panic(err)
}
defer func() { _ = c.Disconnect(context.TODO()) }()
_, err = c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
if err != nil {
panic(err)
}
}

// Other Azure Environments

// For applications running on Azure Functions, App Service Environment
// (ASE), or Azure Kubernetes Service (AKS), you can use the `azidentity
// package`
// (https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity) to
// fetch the credentials. In each case, the OIDCCallback function should
// return the AccessToken from the azidentity package.

// GCP GKE

// For a Google Kubernetes Engine cluster with a `configured service
// account`, the token can be read from the standard service account token
// file location.
{
gkeCallback := func(_ context.Context,
_ *options.OIDCArgs) (*options.OIDCCredential, error) {
accessToken, err := os.ReadFile(
"/var/run/secrets/kubernetes.io/serviceaccount/token")
if err != nil {
return nil, err
}
return &options.OIDCCredential{
AccessToken: string(accessToken),
}, nil
}
uri := os.Getenv("MONGODB_URI")
props := map[string]string{
"ENVIRONMENT": "gcp",
"TOKEN_RESOURCE": "<audience>",
}
opts := options.Client().ApplyURI(uri)
opts.SetAuth(
options.Credential{
AuthMechanism: "MONGODB-OIDC",
AuthMechanismProperties: props,
OIDCMachineCallback: gkeCallback,
},
)
c, err := mongo.Connect(context.TODO(), opts)
if err != nil {
panic(err)
}
defer func() { _ = c.Disconnect(context.TODO()) }()
_, err = c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
if err != nil {
panic(err)
}
}

// For workforce identity, the Client must be configured with the
// OIDCHumanCallback rather than the OIDCMachineCallback. The
// OIDCHumanCallback is used by the driver in a process that is two step. In
// the first step, the driver retrieves the Identity Provider (IDP)
// Information (IDPInfo) for the passed username. The OIDCHumanCallback then
// needs to negotiate with the IDP in order to obtain an AccessToken,
// possible RefreshToken, any timeouts, and return them, similar to the
// OIDCMachineCallbacks seen above. See
// https://docs.hidglobal.com/dev/auth-service/integration/openid-authentication-flows.html
// for more information on various OIDC authentication flows.
{
humanCallback := func(ctx context.Context,
opts *options.OIDCArgs) (*options.OIDCCredential, error) {
// idpInfo passed from the driver by asking the MongoDB server for
// the info configured for the username
idpInfo := opts.IDPInfo
// negotiateWithIDP must work with the IdP to obtain an access
// token. In many cases this will involve opening a webbrowser or
// providing a URL on the command line to a human-in-the-loop who
// can give permissions to the IdP.
accessToken, err := negotiateWithIDP(ctx, idpInfo.Issuer)
if err != nil {
return nil, err
}
return &options.OIDCCredential{
AccessToken: accessToken,
}, nil
}
uri := os.Getenv("MONGODB_URI")
props := map[string]string{
"ENVIRONMENT": "gcp",
"TOKEN_RESOURCE": "<audience>",
}
opts := options.Client().ApplyURI(uri)
opts.SetAuth(
options.Credential{
AuthMechanism: "MONGODB-OIDC",
AuthMechanismProperties: props,
OIDCHumanCallback: humanCallback,
},
)
c, err := mongo.Connect(context.TODO(), opts)
if err != nil {
panic(err)
}
defer func() { _ = c.Disconnect(context.TODO()) }()
_, err = c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
if err != nil {
panic(err)
}
}

// * MONGODB-OIDC authentication mechanism:
// https://www.mongodb.com/docs/manual/core/security-oidc/
// * OIDC Identity Provider Configuration:
// https://www.mongodb.com/docs/manual/reference/parameters/#mongodb-parameter-param.oidcIdentityProviders
// * Azure Internal Metadata Service:
// https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service
// * GCP Internal Metadata Service:
// https://cloud.google.com/compute/docs/metadata/querying-metadata
// * IAM OIDC provider:
// https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html
// * azure-identity package:
// https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity
// * configured service account:
// https://cloud.google.com/kubernetes-engine/docs/how-to/service-accounts
}

func negotiateWithIDP(_ context.Context, _ string) (string, error) {
return "", nil
}

0 comments on commit 485e74d

Please sign in to comment.