Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

GODRIVER-2758: Add documentation examples #1759

Merged
merged 12 commits into from
Sep 11, 2024
250 changes: 250 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,252 @@ func ExampleConnect_bSONOptions() {
panic(err)
}
}

func ExampleConnect_oIDC() {
pmeredit marked this conversation as resolved.
Show resolved Hide resolved
// 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 c.Disconnect(context.TODO())
c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
}

// 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 c.Disconnect(context.TODO())
c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
}

// 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 c.Disconnect(context.TODO())
c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
}

// 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 c.Disconnect(context.TODO())
c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
}

// 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 Prodiver (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 persmissions to the IdP.
accessToken, err := negotiateWithIDP(ctx, idpInfo.Issuer)
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,
OIDCHumanCallback: humanCallback,
},
)
c, err := mongo.Connect(context.TODO(), opts)
if err != nil {
panic(err)
}
defer c.Disconnect(context.TODO())
c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
}

// * 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
}
Loading