managed_identity_credential.go raw

   1  //go:build go1.18
   2  // +build go1.18
   3  
   4  // Copyright (c) Microsoft Corporation. All rights reserved.
   5  // Licensed under the MIT License.
   6  
   7  package azidentity
   8  
   9  import (
  10  	"context"
  11  	"fmt"
  12  	"strings"
  13  
  14  	"github.com/Azure/azure-sdk-for-go/sdk/azcore"
  15  	"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
  16  	"github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
  17  )
  18  
  19  const credNameManagedIdentity = "ManagedIdentityCredential"
  20  
  21  type managedIdentityIDKind int
  22  
  23  const (
  24  	miClientID managedIdentityIDKind = iota
  25  	miObjectID
  26  	miResourceID
  27  )
  28  
  29  // ManagedIDKind identifies the ID of a managed identity as either a client or resource ID
  30  type ManagedIDKind interface {
  31  	fmt.Stringer
  32  	idKind() managedIdentityIDKind
  33  }
  34  
  35  // ClientID is the client ID of a user-assigned managed identity. [NewManagedIdentityCredential]
  36  // returns an error when a ClientID is specified on the following platforms:
  37  //
  38  //   - Azure Arc
  39  //   - Cloud Shell
  40  //   - Service Fabric
  41  type ClientID string
  42  
  43  func (ClientID) idKind() managedIdentityIDKind {
  44  	return miClientID
  45  }
  46  
  47  // String returns the string value of the ID.
  48  func (c ClientID) String() string {
  49  	return string(c)
  50  }
  51  
  52  // ObjectID is the object ID of a user-assigned managed identity. [NewManagedIdentityCredential]
  53  // returns an error when an ObjectID is specified on the following platforms:
  54  //
  55  //   - Azure Arc
  56  //   - Azure ML
  57  //   - Cloud Shell
  58  //   - Service Fabric
  59  type ObjectID string
  60  
  61  func (ObjectID) idKind() managedIdentityIDKind {
  62  	return miObjectID
  63  }
  64  
  65  // String returns the string value of the ID.
  66  func (o ObjectID) String() string {
  67  	return string(o)
  68  }
  69  
  70  // ResourceID is the resource ID of a user-assigned managed identity. [NewManagedIdentityCredential]
  71  // returns an error when a ResourceID is specified on the following platforms:
  72  //
  73  //   - Azure Arc
  74  //   - Azure ML
  75  //   - Cloud Shell
  76  //   - Service Fabric
  77  type ResourceID string
  78  
  79  func (ResourceID) idKind() managedIdentityIDKind {
  80  	return miResourceID
  81  }
  82  
  83  // String returns the string value of the ID.
  84  func (r ResourceID) String() string {
  85  	return string(r)
  86  }
  87  
  88  // ManagedIdentityCredentialOptions contains optional parameters for ManagedIdentityCredential.
  89  type ManagedIdentityCredentialOptions struct {
  90  	azcore.ClientOptions
  91  
  92  	// ID of a managed identity the credential should authenticate. Set this field to use a specific identity instead of
  93  	// the hosting environment's default. The value may be the identity's client, object, or resource ID.
  94  	// NewManagedIdentityCredential returns an error when the hosting environment doesn't support user-assigned managed
  95  	// identities, or the specified kind of ID.
  96  	ID ManagedIDKind
  97  
  98  	// dac indicates whether the credential is part of DefaultAzureCredential. When true, and the environment doesn't have
  99  	// configuration for a specific managed identity API, the credential tries to determine whether IMDS is available before
 100  	// sending its first token request. It does this by sending a malformed request with a short timeout. Any response to that
 101  	// request is taken to mean IMDS is available, in which case the credential will send ordinary token requests thereafter
 102  	// with no special timeout. The purpose of this behavior is to prevent a very long timeout when IMDS isn't available.
 103  	dac bool
 104  }
 105  
 106  // ManagedIdentityCredential authenticates an [Azure managed identity] in any hosting environment supporting managed identities.
 107  // This credential authenticates a system-assigned identity by default. Use ManagedIdentityCredentialOptions.ID to specify a
 108  // user-assigned identity.
 109  //
 110  // [Azure managed identity]: https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/overview
 111  type ManagedIdentityCredential struct {
 112  	mic *managedIdentityClient
 113  }
 114  
 115  // NewManagedIdentityCredential creates a ManagedIdentityCredential. Pass nil to accept default options.
 116  func NewManagedIdentityCredential(options *ManagedIdentityCredentialOptions) (*ManagedIdentityCredential, error) {
 117  	if options == nil {
 118  		options = &ManagedIdentityCredentialOptions{}
 119  	}
 120  	mic, err := newManagedIdentityClient(options)
 121  	if err != nil {
 122  		return nil, err
 123  	}
 124  	return &ManagedIdentityCredential{mic: mic}, nil
 125  }
 126  
 127  // GetToken requests an access token from the hosting environment. This method is called automatically by Azure SDK clients.
 128  func (c *ManagedIdentityCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) {
 129  	var err error
 130  	ctx, endSpan := runtime.StartSpan(ctx, credNameManagedIdentity+"."+traceOpGetToken, c.mic.azClient.Tracer(), nil)
 131  	defer func() { endSpan(err) }()
 132  
 133  	if len(opts.Scopes) != 1 {
 134  		err = fmt.Errorf("%s.GetToken() requires exactly one scope", credNameManagedIdentity)
 135  		return azcore.AccessToken{}, err
 136  	}
 137  	// managed identity endpoints require a v1 resource (i.e. token audience), not a v2 scope, so we remove "/.default" here
 138  	opts.Scopes = []string{strings.TrimSuffix(opts.Scopes[0], defaultSuffix)}
 139  	return c.mic.GetToken(ctx, opts)
 140  }
 141  
 142  var _ azcore.TokenCredential = (*ManagedIdentityCredential)(nil)
 143