Skip to content

Commit 485e74d

Browse files
pmereditblink1073matthewdale
authored
GODRIVER-2758: Add documentation examples (#1759)
Co-authored-by: Steven Silvester <[email protected]> Co-authored-by: Matt Dale <[email protected]>
1 parent ac03e73 commit 485e74d

File tree

2 files changed

+266
-1
lines changed

2 files changed

+266
-1
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ repos:
4040
rev: "v2.2.6"
4141
hooks:
4242
- id: codespell
43-
args: ["-L", "te,fo,fle,alo,nin,compres,wil,collone,asess,sav,ot,wll,dne,nulll,hellow"]
43+
args: ["-L", "te,fo,fle,alo,nin,compres,wil,collone,asess,sav,ot,wll,dne,nulll,hellow,aks"]
4444
exclude: ^(vendor/|benchmark/operation_test.go)
4545
exclude_types: [json,yaml,pem]
4646

mongo/client_examples_test.go

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"context"
1111
"fmt"
1212
"log"
13+
"os"
1314

1415
"go.mongodb.org/mongo-driver/bson"
1516
"go.mongodb.org/mongo-driver/mongo"
@@ -468,3 +469,267 @@ func ExampleConnect_bSONOptions() {
468469
panic(err)
469470
}
470471
}
472+
473+
func ExampleConnect_oIDC() {
474+
// The `MONGODB-OIDC authentication mechanism` is available in MongoDB 7.0+
475+
// on Linux platforms.
476+
//
477+
// The MONGODB-OIDC mechanism authenticates using an OpenID Connect (OIDC)
478+
// access token. The driver supports OIDC for workload identity, defined as
479+
// an identity you assign to a software workload (such as an application,
480+
// service, script, or container) to authenticate and access other services
481+
// and resources.
482+
//
483+
// The driver also supports OIDC for workforce identity for a more secure
484+
// flow with a human in the loop.
485+
486+
// Credentials can be configured through the MongoDB URI or as arguments in
487+
// the options.ClientOptions struct that is passed into the mongo.Connect
488+
// function.
489+
490+
// Built-in Support
491+
// The driver has built-in support for Azure IMDS and GCP
492+
// IMDS environments. Other environments are supported with `Custom
493+
// Callbacks`.
494+
495+
// Azure IMDS
496+
// For an application running on an Azure VM or otherwise using the `Azure
497+
// Internal Metadata Service`, you can use the built-in support for Azure,
498+
// where "<client_id>" below is the client id of the Azure managed identity,
499+
// and ``<audience>`` is the url-encoded ``audience`` `configured on your
500+
// MongoDB deployment`.
501+
{
502+
uri := os.Getenv("MONGODB_URI")
503+
props := map[string]string{
504+
"ENVIRONMENT": "azure",
505+
"TOKEN_RESOURCE": "<audience>",
506+
}
507+
opts := options.Client().ApplyURI(uri)
508+
opts.SetAuth(
509+
options.Credential{
510+
Username: "<client_id>",
511+
AuthMechanism: "MONGODB-OIDC",
512+
AuthMechanismProperties: props,
513+
},
514+
)
515+
c, err := mongo.Connect(context.TODO(), opts)
516+
if err != nil {
517+
panic(err)
518+
}
519+
defer func() { _ = c.Disconnect(context.TODO()) }()
520+
_, err = c.Database("test").
521+
Collection("test").
522+
InsertOne(context.TODO(), bson.D{})
523+
if err != nil {
524+
panic(err)
525+
}
526+
}
527+
528+
// If the application is running on an Azure VM and only one managed
529+
// identity is associated with the VM, "username" can be omitted.
530+
531+
// GCP IMDS
532+
533+
// For an application running on an GCP VM or otherwise using the `GCP
534+
// Internal Metadata Service`_, you can use the built-in support for GCP,
535+
// where "<audience>" below is the url-encoded "audience" `configured on
536+
// your MongoDB deployment`.
537+
{
538+
uri := os.Getenv("MONGODB_URI")
539+
props := map[string]string{
540+
"ENVIRONMENT": "gcp",
541+
"TOKEN_RESOURCE": "<audience>",
542+
}
543+
opts := options.Client().ApplyURI(uri)
544+
opts.SetAuth(
545+
options.Credential{
546+
AuthMechanism: "MONGODB-OIDC",
547+
AuthMechanismProperties: props,
548+
},
549+
)
550+
c, err := mongo.Connect(context.TODO(), opts)
551+
if err != nil {
552+
panic(err)
553+
}
554+
defer func() { _ = c.Disconnect(context.TODO()) }()
555+
_, err = c.Database("test").
556+
Collection("test").
557+
InsertOne(context.TODO(), bson.D{})
558+
if err != nil {
559+
panic(err)
560+
}
561+
}
562+
563+
// Custom Callbacks
564+
565+
// For environments that are not directly supported by the driver, you can
566+
// use options.OIDCCallback. Some examples are given below.
567+
568+
// AWS EKS
569+
570+
// For an EKS Cluster with a configured `IAM OIDC provider`, the token can
571+
// be read from a path given by the "AWS_WEB_IDENTITY_TOKEN_FILE"
572+
// environment variable.
573+
{
574+
eksCallback := func(_ context.Context,
575+
_ *options.OIDCArgs) (*options.OIDCCredential, error) {
576+
accessToken, err := os.ReadFile(
577+
os.Getenv("AWS_WEB_IDENTITY_TOKEN_FILE"))
578+
if err != nil {
579+
return nil, err
580+
}
581+
return &options.OIDCCredential{
582+
AccessToken: string(accessToken),
583+
}, nil
584+
}
585+
uri := os.Getenv("MONGODB_URI")
586+
props := map[string]string{
587+
"ENVIRONMENT": "gcp",
588+
"TOKEN_RESOURCE": "<audience>",
589+
}
590+
opts := options.Client().ApplyURI(uri)
591+
opts.SetAuth(
592+
options.Credential{
593+
AuthMechanism: "MONGODB-OIDC",
594+
AuthMechanismProperties: props,
595+
OIDCMachineCallback: eksCallback,
596+
},
597+
)
598+
c, err := mongo.Connect(context.TODO(), opts)
599+
if err != nil {
600+
panic(err)
601+
}
602+
defer func() { _ = c.Disconnect(context.TODO()) }()
603+
_, err = c.Database("test").
604+
Collection("test").
605+
InsertOne(context.TODO(), bson.D{})
606+
if err != nil {
607+
panic(err)
608+
}
609+
}
610+
611+
// Other Azure Environments
612+
613+
// For applications running on Azure Functions, App Service Environment
614+
// (ASE), or Azure Kubernetes Service (AKS), you can use the `azidentity
615+
// package`
616+
// (https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity) to
617+
// fetch the credentials. In each case, the OIDCCallback function should
618+
// return the AccessToken from the azidentity package.
619+
620+
// GCP GKE
621+
622+
// For a Google Kubernetes Engine cluster with a `configured service
623+
// account`, the token can be read from the standard service account token
624+
// file location.
625+
{
626+
gkeCallback := func(_ context.Context,
627+
_ *options.OIDCArgs) (*options.OIDCCredential, error) {
628+
accessToken, err := os.ReadFile(
629+
"/var/run/secrets/kubernetes.io/serviceaccount/token")
630+
if err != nil {
631+
return nil, err
632+
}
633+
return &options.OIDCCredential{
634+
AccessToken: string(accessToken),
635+
}, nil
636+
}
637+
uri := os.Getenv("MONGODB_URI")
638+
props := map[string]string{
639+
"ENVIRONMENT": "gcp",
640+
"TOKEN_RESOURCE": "<audience>",
641+
}
642+
opts := options.Client().ApplyURI(uri)
643+
opts.SetAuth(
644+
options.Credential{
645+
AuthMechanism: "MONGODB-OIDC",
646+
AuthMechanismProperties: props,
647+
OIDCMachineCallback: gkeCallback,
648+
},
649+
)
650+
c, err := mongo.Connect(context.TODO(), opts)
651+
if err != nil {
652+
panic(err)
653+
}
654+
defer func() { _ = c.Disconnect(context.TODO()) }()
655+
_, err = c.Database("test").
656+
Collection("test").
657+
InsertOne(context.TODO(), bson.D{})
658+
if err != nil {
659+
panic(err)
660+
}
661+
}
662+
663+
// For workforce identity, the Client must be configured with the
664+
// OIDCHumanCallback rather than the OIDCMachineCallback. The
665+
// OIDCHumanCallback is used by the driver in a process that is two step. In
666+
// the first step, the driver retrieves the Identity Provider (IDP)
667+
// Information (IDPInfo) for the passed username. The OIDCHumanCallback then
668+
// needs to negotiate with the IDP in order to obtain an AccessToken,
669+
// possible RefreshToken, any timeouts, and return them, similar to the
670+
// OIDCMachineCallbacks seen above. See
671+
// https://docs.hidglobal.com/dev/auth-service/integration/openid-authentication-flows.html
672+
// for more information on various OIDC authentication flows.
673+
{
674+
humanCallback := func(ctx context.Context,
675+
opts *options.OIDCArgs) (*options.OIDCCredential, error) {
676+
// idpInfo passed from the driver by asking the MongoDB server for
677+
// the info configured for the username
678+
idpInfo := opts.IDPInfo
679+
// negotiateWithIDP must work with the IdP to obtain an access
680+
// token. In many cases this will involve opening a webbrowser or
681+
// providing a URL on the command line to a human-in-the-loop who
682+
// can give permissions to the IdP.
683+
accessToken, err := negotiateWithIDP(ctx, idpInfo.Issuer)
684+
if err != nil {
685+
return nil, err
686+
}
687+
return &options.OIDCCredential{
688+
AccessToken: accessToken,
689+
}, nil
690+
}
691+
uri := os.Getenv("MONGODB_URI")
692+
props := map[string]string{
693+
"ENVIRONMENT": "gcp",
694+
"TOKEN_RESOURCE": "<audience>",
695+
}
696+
opts := options.Client().ApplyURI(uri)
697+
opts.SetAuth(
698+
options.Credential{
699+
AuthMechanism: "MONGODB-OIDC",
700+
AuthMechanismProperties: props,
701+
OIDCHumanCallback: humanCallback,
702+
},
703+
)
704+
c, err := mongo.Connect(context.TODO(), opts)
705+
if err != nil {
706+
panic(err)
707+
}
708+
defer func() { _ = c.Disconnect(context.TODO()) }()
709+
_, err = c.Database("test").
710+
Collection("test").
711+
InsertOne(context.TODO(), bson.D{})
712+
if err != nil {
713+
panic(err)
714+
}
715+
}
716+
717+
// * MONGODB-OIDC authentication mechanism:
718+
// https://www.mongodb.com/docs/manual/core/security-oidc/
719+
// * OIDC Identity Provider Configuration:
720+
// https://www.mongodb.com/docs/manual/reference/parameters/#mongodb-parameter-param.oidcIdentityProviders
721+
// * Azure Internal Metadata Service:
722+
// https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service
723+
// * GCP Internal Metadata Service:
724+
// https://cloud.google.com/compute/docs/metadata/querying-metadata
725+
// * IAM OIDC provider:
726+
// https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html
727+
// * azure-identity package:
728+
// https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity
729+
// * configured service account:
730+
// https://cloud.google.com/kubernetes-engine/docs/how-to/service-accounts
731+
}
732+
733+
func negotiateWithIDP(_ context.Context, _ string) (string, error) {
734+
return "", nil
735+
}

0 commit comments

Comments
 (0)