@@ -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