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