@@ -10,6 +10,7 @@ import (
10
10
"context"
11
11
"fmt"
12
12
"log"
13
+ "os"
13
14
14
15
"go.mongodb.org/mongo-driver/bson"
15
16
"go.mongodb.org/mongo-driver/mongo"
@@ -468,3 +469,267 @@ func ExampleConnect_bSONOptions() {
468
469
panic (err )
469
470
}
470
471
}
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