Skip to content

Commit

Permalink
feat: implement custom authorizer for msal-go example (#74)
Browse files Browse the repository at this point in the history
Signed-off-by: Anish Ramasekar <[email protected]>
  • Loading branch information
aramase authored Jun 29, 2021
1 parent c6b92d5 commit 5a1b5d1
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 46 deletions.
8 changes: 7 additions & 1 deletion examples/msal-go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ module github.com/Azure/aad-pod-managed-identity/example/msal-go
go 1.16

require (
github.com/AzureAD/microsoft-authentication-library-for-go v0.2.0
github.com/Azure/azure-sdk-for-go v55.3.0+incompatible
github.com/Azure/go-autorest/autorest v0.11.19
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v0.3.0
github.com/pkg/errors v0.9.1
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
k8s.io/klog/v2 v2.8.0
)
45 changes: 43 additions & 2 deletions examples/msal-go/go.sum
Original file line number Diff line number Diff line change
@@ -1,14 +1,55 @@
github.com/AzureAD/microsoft-authentication-library-for-go v0.2.0 h1:MMGApBI45TLJ6mj6od6zT+1da/Gk+5qFGom2TtBSLLU=
github.com/AzureAD/microsoft-authentication-library-for-go v0.2.0/go.mod h1:5aEdWF4KzHyRg5i5ItqNcv12iMQr/kYTYaX+GpdEcj8=
github.com/Azure/azure-sdk-for-go v55.3.0+incompatible h1:rLKCdFMMCAXt/QZ96skZJUArYS3UDo9Qm1ZWzoDtC9E=
github.com/Azure/azure-sdk-for-go v55.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/Azure/go-autorest/autorest v0.11.19 h1:7/IqD2fEYVha1EPeaiytVKhzmPV223pfkRIQUGOK2IE=
github.com/Azure/go-autorest/autorest v0.11.19/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA=
github.com/Azure/go-autorest/autorest/adal v0.9.13 h1:Mp5hbtOePIzM8pJVRa3YLrWWmZtoxRXqUEzCfJt3+/Q=
github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk=
github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk=
github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE=
github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac=
github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E=
github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg=
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
github.com/AzureAD/microsoft-authentication-library-for-go v0.3.0 h1:2iUGNrLWpRaZ8EFbrU6LxwcJuPkBxus5z5BHaBfu0lo=
github.com/AzureAD/microsoft-authentication-library-for-go v0.3.0/go.mod h1:5aEdWF4KzHyRg5i5ItqNcv12iMQr/kYTYaX+GpdEcj8=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts=
k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
54 changes: 11 additions & 43 deletions examples/msal-go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,56 +5,24 @@ import (
"fmt"
"os"

"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault"
"github.com/Azure/go-autorest/autorest"
"k8s.io/klog/v2"
)

func main() {
ctx := context.Background()
keyvaultName := os.Getenv("KEYVAULT_NAME")
secretName := os.Getenv("SECRET_NAME")

// AAD Pod Identity webhook will inject the following env vars
// AZURE_CLIENT_ID with the clientID set in the service account annotation
// AZURE_TENANT_ID with the tenantID set in the service account annotation. If not defined, then
// the tenantID provided via aad-pi-webhook-config for the webhook will be used.
// TOKEN_FILE_PATH is the service account token path
keyvaultURL := fmt.Sprintf("https://%s.vault.azure.net/", keyvaultName)

tokenFilePath := os.Getenv("TOKEN_FILE_PATH")
tenantID := os.Getenv("AZURE_TENANT_ID")
// if the service account wasn't annotated with the clientID, then this will be empty
// ensure to set your clientID if not provided through annotation
clientID := os.Getenv("AZURE_CLIENT_ID")
// initialize keyvault client with custom authorizer
kvClient := keyvault.New()
kvClient.Authorizer = autorest.NewBearerAuthorizerCallback(nil, clientAssertionBearerAuthorizerCallback)

// read the service account token from the filesystem
signedAssertion, err := readJWTFromFS(tokenFilePath)
secretBundle, err := kvClient.GetSecret(context.Background(), keyvaultURL, secretName, "")
if err != nil {
klog.Fatalf("failed to read service account token: %v", err)
klog.Fatalf("failed to get secret from keyvault, err: %+v", err)
}

cred, err := confidential.NewCredFromAssertion(signedAssertion)
if err != nil {
klog.Fatalf("failed to create confidential creds: %v", err)
}

// create the confidential client to request an AAD token
confidentialClientApp, err := confidential.New(clientID, cred,
// TODO (aramase) remove query params after available in prod
confidential.WithAuthority(fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/token?dc=ESTS-PUB-WUS2-AZ1-FD000-TEST1&fiextoidc=true", tenantID)))
if err != nil {
klog.Fatalf("failed to create confidential client app: %v", err)
}

scopes := []string{"https://graph.microsoft.com/.default"}
result, err := confidentialClientApp.AcquireTokenByCredential(ctx, scopes)
if err != nil {
klog.Fatalf("failed to get token: %v", err)
}
klog.InfoS("successfully obtained the token", "token", result.AccessToken, "expiry", result.ExpiresOn)
}

func readJWTFromFS(tokenFilePath string) (string, error) {
token, err := os.ReadFile(tokenFilePath)
if err != nil {
return "", err
}
return string(token), nil
klog.InfoS("successfully got secret", "secret", *secretBundle.Value)
}
87 changes: 87 additions & 0 deletions examples/msal-go/token_credential.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package main

import (
"context"
"fmt"
"os"
"strings"
"time"

"github.com/Azure/go-autorest/autorest"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
"github.com/pkg/errors"
)

// authResult contains the subset of results from token acquisition operation in ConfidentialClientApplication
// For details see https://aka.ms/msal-net-authenticationresult
type authResult struct {
accessToken string
expiresOn time.Time
grantedScopes []string
declinedScopes []string
}

func clientAssertionBearerAuthorizerCallback(tenantID, resource string) (*autorest.BearerAuthorizer, error) {
// AAD Pod Identity webhook will inject the following env vars
// AZURE_CLIENT_ID with the clientID set in the service account annotation
// AZURE_TENANT_ID with the tenantID set in the service account annotation. If not defined, then
// the tenantID provided via aad-pi-webhook-config for the webhook will be used.
// TOKEN_FILE_PATH is the service account token path
clientID := os.Getenv("AZURE_CLIENT_ID")
tokenFilePath := os.Getenv("TOKEN_FILE_PATH")

// generate a token using the msal confidential client
// this will always generate a new token request to AAD
// TODO (aramase) consider using acquire token silent (https://github.com/Azure/aad-pod-managed-identity/issues/76)

// read the service account token from the filesystem
signedAssertion, err := readJWTFromFS(tokenFilePath)
if err != nil {
return nil, errors.Errorf("failed to read service account token: %v", err)
}
cred, err := confidential.NewCredFromAssertion(signedAssertion)
if err != nil {
return nil, errors.Errorf("failed to create confidential creds: %v", err)
}
// create the confidential client to request an AAD token
confidentialClientApp, err := confidential.New(
clientID,
cred,
confidential.WithAuthority(fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/token", tenantID)))
if err != nil {
return nil, errors.Errorf("failed to create confidential client app: %v", err)
}

// trim the suffix / if exists
resource = strings.TrimSuffix(resource, "/")
// .default needs to be added to the scope
if !strings.HasSuffix(resource, ".default") {
resource += "/.default"
}

result, err := confidentialClientApp.AcquireTokenByCredential(context.Background(), []string{resource})
if err != nil {
return nil, errors.Errorf("failed to get token: %v", err)
}

return autorest.NewBearerAuthorizer(authResult{
accessToken: result.AccessToken,
expiresOn: result.ExpiresOn,
grantedScopes: result.GrantedScopes,
declinedScopes: result.DeclinedScopes,
}), nil
}

// OAuthToken implements the OAuthTokenProvider interface. It returns the current access token.
func (ar authResult) OAuthToken() string {
return ar.accessToken
}

// readJWTFromFS reads the jwt from file system
func readJWTFromFS(tokenFilePath string) (string, error) {
token, err := os.ReadFile(tokenFilePath)
if err != nil {
return "", err
}
return string(token), nil
}

0 comments on commit 5a1b5d1

Please sign in to comment.