diff --git a/sdk/azidentity/CHANGELOG.md b/sdk/azidentity/CHANGELOG.md index ce5be9f15710..6afba175220e 100644 --- a/sdk/azidentity/CHANGELOG.md +++ b/sdk/azidentity/CHANGELOG.md @@ -9,6 +9,10 @@ ### Bugs Fixed ### Other Changes +* `ManagedIdentityCredential` no longer probes IMDS before requesting a token + from it. Also, an error response from IMDS no longer disables a credential + instance. Following an error, a credential instance will continue to send + requests to IMDS as necessary. ## 0.12.0 (2021-11-02) ### Breaking Changes diff --git a/sdk/azidentity/default_azure_credential.go b/sdk/azidentity/default_azure_credential.go index 5283555222c1..0d4ac8d1a8ea 100644 --- a/sdk/azidentity/default_azure_credential.go +++ b/sdk/azidentity/default_azure_credential.go @@ -6,6 +6,7 @@ package azidentity import ( "context" "errors" + "time" "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" @@ -56,6 +57,7 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default msiCred, err := NewManagedIdentityCredential(&ManagedIdentityCredentialOptions{ClientOptions: options.ClientOptions}) if err == nil { creds = append(creds, msiCred) + msiCred.client.imdsTimeout = time.Second } else { errMsg += err.Error() } diff --git a/sdk/azidentity/logging.go b/sdk/azidentity/logging.go index 1a56f331c2b1..4689adc32bac 100644 --- a/sdk/azidentity/logging.go +++ b/sdk/azidentity/logging.go @@ -5,7 +5,6 @@ package azidentity import ( "fmt" - "os" "strings" "github.com/Azure/azure-sdk-for-go/sdk/azcore" @@ -19,30 +18,6 @@ import ( // used when obtaining credentials and the type of credential used. const EventAuthentication log.Event = "Authentication" -// log environment variables that can be used for credential types -func logEnvVars() { - if !log.Should(EventAuthentication) { - return - } - // Log available environment variables - envVars := []string{} - if envCheck := os.Getenv("AZURE_TENANT_ID"); len(envCheck) > 0 { - envVars = append(envVars, "AZURE_TENANT_ID") - } - if envCheck := os.Getenv("AZURE_CLIENT_ID"); len(envCheck) > 0 { - envVars = append(envVars, "AZURE_CLIENT_ID") - } - if envCheck := os.Getenv("AZURE_CLIENT_SECRET"); len(envCheck) > 0 { - envVars = append(envVars, "AZURE_CLIENT_SECRET") - } - if envCheck := os.Getenv(azureAuthorityHost); len(envCheck) > 0 { - envVars = append(envVars, azureAuthorityHost) - } - if len(envVars) > 0 { - log.Writef(EventAuthentication, "Azure Identity => Found the following environment variables:\n\t%s", strings.Join(envVars, ", ")) - } -} - func logGetTokenSuccess(cred azcore.TokenCredential, opts policy.TokenRequestOptions) { if !log.Should(EventAuthentication) { return @@ -56,24 +31,6 @@ func logCredentialError(credName string, err error) { log.Writef(EventAuthentication, "Azure Identity => ERROR in %s: %s", credName, err.Error()) } -func logMSIEnv(msi msiType) { - if !log.Should(EventAuthentication) { - return - } - var msg string - switch msi { - case msiTypeIMDS: - msg = "Azure Identity => Managed Identity environment: IMDS" - case msiTypeAppServiceV20170901, msiTypeCloudShell, msiTypeAppServiceV20190801: - msg = "Azure Identity => Managed Identity environment: MSI_ENDPOINT" - case msiTypeUnavailable: - msg = "Azure Identity => Managed Identity environment: Unavailable" - default: - msg = "Azure Identity => Managed Identity environment: Unknown" - } - log.Write(EventAuthentication, msg) -} - func addGetTokenFailureLogs(credName string, err error, includeStack bool) { if !log.Should(EventAuthentication) { return diff --git a/sdk/azidentity/managed_identity_client.go b/sdk/azidentity/managed_identity_client.go index fbc300b9782d..40b594de08f4 100644 --- a/sdk/azidentity/managed_identity_client.go +++ b/sdk/azidentity/managed_identity_client.go @@ -20,11 +20,7 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" "github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming" -) - -const ( - headerMetadata = "Metadata" - imdsEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token" + "github.com/Azure/azure-sdk-for-go/sdk/internal/log" ) const ( @@ -32,6 +28,8 @@ const ( identityEndpoint = "IDENTITY_ENDPOINT" identityHeader = "IDENTITY_HEADER" identityServerThumbprint = "IDENTITY_SERVER_THUMBPRINT" + headerMetadata = "Metadata" + imdsEndpoint = "http://169.254.169.254/metadata/identity/oauth2/token" msiEndpoint = "MSI_ENDPOINT" msiSecret = "MSI_SECRET" imdsAPIVersion = "2018-02-01" @@ -45,26 +43,22 @@ const ( type msiType int const ( - msiTypeUnknown msiType = 0 - msiTypeIMDS msiType = 1 - msiTypeAppServiceV20170901 msiType = 2 - msiTypeCloudShell msiType = 3 - msiTypeUnavailable msiType = 4 - msiTypeAppServiceV20190801 msiType = 5 - msiTypeAzureArc msiType = 6 - msiTypeServiceFabric msiType = 7 + msiTypeAppServiceV20170901 msiType = iota + msiTypeAppServiceV20190801 + msiTypeAzureArc + msiTypeCloudShell + msiTypeIMDS + msiTypeServiceFabric ) // managedIdentityClient provides the base for authenticating in managed identity environments // This type includes an runtime.Pipeline and TokenCredentialOptions. type managedIdentityClient struct { - pipeline runtime.Pipeline - imdsAPIVersion string - imdsAvailableTimeout time.Duration - msiType msiType - endpoint string - id ManagedIDKind - unavailableMessage string + pipeline runtime.Pipeline + msiType msiType + endpoint string + id ManagedIDKind + imdsTimeout time.Duration } type wrappedNumber json.Number @@ -77,8 +71,8 @@ func (n *wrappedNumber) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, (*json.Number)(n)) } -// setRetryOptionDefaults sets zero-valued fields to default values appropriate for IMDS -func setRetryOptionDefaults(o *policy.RetryOptions) { +// setIMDSRetryOptionDefaults sets zero-valued fields to default values appropriate for IMDS +func setIMDSRetryOptionDefaults(o *policy.RetryOptions) { if o.MaxRetries == 0 { o.MaxRetries = 5 } @@ -111,27 +105,49 @@ func setRetryOptionDefaults(o *policy.RetryOptions) { } } -// newDefaultMSIPipeline creates a pipeline using the specified pipeline options needed -// for a Managed Identity, such as a MSI specific retry policy. -func newDefaultMSIPipeline(o ManagedIdentityCredentialOptions) runtime.Pipeline { - cp := o.ClientOptions - setRetryOptionDefaults(&cp.Retry) - return runtime.NewPipeline(component, version, runtime.PipelineOptions{}, &cp) -} - // newManagedIdentityClient creates a new instance of the ManagedIdentityClient with the ManagedIdentityCredentialOptions // that are passed into it along with a default pipeline. // options: ManagedIdentityCredentialOptions configure policies for the pipeline and the authority host that // will be used to retrieve tokens and authenticate -func newManagedIdentityClient(options *ManagedIdentityCredentialOptions) *managedIdentityClient { - logEnvVars() - return &managedIdentityClient{ - id: options.ID, - pipeline: newDefaultMSIPipeline(*options), // a pipeline that includes the specific requirements for MSI authentication, such as custom retry policy options - imdsAPIVersion: imdsAPIVersion, // this field will be set to whatever value exists in the constant and is used when creating requests to IMDS - imdsAvailableTimeout: 500 * time.Millisecond, // we allow a timeout of 500 ms since the endpoint might be slow to respond - msiType: msiTypeUnknown, // when creating a new managedIdentityClient, the current MSI type is unknown and will be tested for and replaced once authenticate() is called from GetToken on the credential side +func newManagedIdentityClient(options *ManagedIdentityCredentialOptions) (*managedIdentityClient, error) { + if options == nil { + options = &ManagedIdentityCredentialOptions{} + } + cp := options.ClientOptions + c := managedIdentityClient{id: options.ID, endpoint: imdsEndpoint, msiType: msiTypeIMDS} + env := "IMDS" + if endpoint, ok := os.LookupEnv(msiEndpoint); ok { + if _, ok := os.LookupEnv(msiSecret); ok { + env = "App Service" + c.endpoint = endpoint + c.msiType = msiTypeAppServiceV20170901 + } else { + env = "Cloud Shell" + c.endpoint = endpoint + c.msiType = msiTypeCloudShell + } + } else if endpoint, ok := os.LookupEnv(identityEndpoint); ok { + if _, ok := os.LookupEnv(identityHeader); ok { + if _, ok := os.LookupEnv(identityServerThumbprint); ok { + env = "Service Fabric" + c.endpoint = endpoint + c.msiType = msiTypeServiceFabric + } + } else if _, ok := os.LookupEnv(arcIMDSEndpoint); ok { + env = "Azure Arc" + c.endpoint = endpoint + c.msiType = msiTypeAzureArc + } + } else { + setIMDSRetryOptionDefaults(&cp.Retry) + } + c.pipeline = runtime.NewPipeline(component, version, runtime.PipelineOptions{}, &cp) + + if log.Should(EventAuthentication) { + log.Writef(EventAuthentication, "Azure Identity => Managed Identity Credential will use %s managed identity", env) } + + return &c, nil } // authenticate creates an authentication request for a Managed Identity and returns the resulting Access Token if successful. @@ -139,8 +155,10 @@ func newManagedIdentityClient(options *ManagedIdentityCredentialOptions) *manage // clientID: The client (application) ID of the service principal. // scopes: The scopes required for the token. func (c *managedIdentityClient) authenticate(ctx context.Context, id ManagedIDKind, scopes []string) (*azcore.AccessToken, error) { - if len(c.unavailableMessage) > 0 { - return nil, newCredentialUnavailableError("Managed Identity Credential", c.unavailableMessage) + var cancel context.CancelFunc + if c.imdsTimeout > 0 && c.msiType == msiTypeIMDS { + ctx, cancel = context.WithTimeout(ctx, c.imdsTimeout) + defer cancel() } msg, err := c.createAuthRequest(ctx, id, scopes) @@ -150,9 +168,15 @@ func (c *managedIdentityClient) authenticate(ctx context.Context, id ManagedIDKi resp, err := c.pipeline.Do(msg) if err != nil { - return nil, err + if cancel != nil && errors.Is(err, context.DeadlineExceeded) { + return nil, newCredentialUnavailableError("Managed Identity Credential", "IMDS token request timed out") + } + return nil, newAuthenticationFailedError(err, nil) } + // got a response, remove the IMDS timeout so future requests use the transport's configuration + c.imdsTimeout = 0 + if runtime.HasStatusCode(resp, http.StatusOK, http.StatusCreated) { return c.createAccessToken(resp) } @@ -161,8 +185,7 @@ func (c *managedIdentityClient) authenticate(ctx context.Context, id ManagedIDKi if id != nil { return nil, newAuthenticationFailedError(errors.New("the requested identity isn't assigned to this resource"), resp) } - c.unavailableMessage = "No default identity is assigned to this resource." - return nil, newCredentialUnavailableError("Managed Identity Credential", c.unavailableMessage) + return nil, newCredentialUnavailableError("Managed Identity Credential", "no default identity is assigned to this resource") } return nil, newAuthenticationFailedError(errors.New("authentication failed"), resp) @@ -229,15 +252,7 @@ func (c *managedIdentityClient) createAuthRequest(ctx context.Context, id Manage case msiTypeCloudShell: return c.createCloudShellAuthRequest(ctx, id, scopes) default: - errorMsg := "" - switch c.msiType { - case msiTypeUnavailable: - errorMsg = "unavailable" - default: - errorMsg = "unknown" - } - c.unavailableMessage = "managed identity support is " + errorMsg - return nil, newCredentialUnavailableError("Managed Identity Credential", c.unavailableMessage) + return nil, newCredentialUnavailableError("Managed Identity Credential", "managed identity isn't supported in this environment") } } @@ -248,7 +263,7 @@ func (c *managedIdentityClient) createIMDSAuthRequest(ctx context.Context, id Ma } request.Raw().Header.Set(headerMetadata, "true") q := request.Raw().URL.Query() - q.Add("api-version", c.imdsAPIVersion) + q.Add("api-version", imdsAPIVersion) q.Add("resource", strings.Join(scopes, " ")) if id != nil { if id.idKind() == miResourceID { @@ -383,52 +398,3 @@ func (c *managedIdentityClient) createCloudShellAuthRequest(ctx context.Context, } return request, nil } - -func (c *managedIdentityClient) getMSIType() (msiType, error) { - if c.msiType == msiTypeUnknown { // if we haven't already determined the msiType - if endpointEnvVar := os.Getenv(msiEndpoint); endpointEnvVar != "" { // if the env var MSI_ENDPOINT is set - c.endpoint = endpointEnvVar - if secretEnvVar := os.Getenv(msiSecret); secretEnvVar != "" { // if BOTH the env vars MSI_ENDPOINT and MSI_SECRET are set the msiType is AppService - c.msiType = msiTypeAppServiceV20170901 - } else { // if ONLY the env var MSI_ENDPOINT is set the msiType is CloudShell - c.msiType = msiTypeCloudShell - } - } else if endpointEnvVar := os.Getenv(identityEndpoint); endpointEnvVar != "" { // check for IDENTITY_ENDPOINT - c.endpoint = endpointEnvVar - if header := os.Getenv(identityHeader); header != "" { // if BOTH the env vars IDENTITY_ENDPOINT and IDENTITY_HEADER are set the msiType is AppService - c.msiType = msiTypeAppServiceV20190801 - if thumbprint := os.Getenv(identityServerThumbprint); thumbprint != "" { // if IDENTITY_SERVER_THUMBPRINT is set the environment is Service Fabric - c.msiType = msiTypeServiceFabric - } - } else if arcIMDS := os.Getenv(arcIMDSEndpoint); arcIMDS != "" { - c.msiType = msiTypeAzureArc - } else { - c.msiType = msiTypeUnavailable - return c.msiType, newCredentialUnavailableError("Managed Identity Credential", "this environment is not supported") - } - } else if c.imdsAvailable() { // if MSI_ENDPOINT is NOT set AND the IMDS endpoint is available the msiType is IMDS. This will timeout after 500 milliseconds - c.endpoint = imdsEndpoint - c.msiType = msiTypeIMDS - } else { // if MSI_ENDPOINT is NOT set and IMDS endpoint is not available Managed Identity is not available - c.msiType = msiTypeUnavailable - return c.msiType, newCredentialUnavailableError("Managed Identity Credential", "no managed identity endpoint is available") - } - } - return c.msiType, nil -} - -// performs an I/O request that has a timeout of 500 milliseconds -func (c *managedIdentityClient) imdsAvailable() bool { - tempCtx, cancel := context.WithTimeout(context.Background(), c.imdsAvailableTimeout) - defer cancel() - // this should never fail - request, _ := runtime.NewRequest(tempCtx, http.MethodGet, imdsEndpoint) - q := request.Raw().URL.Query() - q.Add("api-version", c.imdsAPIVersion) - request.Raw().URL.RawQuery = q.Encode() - resp, err := c.pipeline.Do(request) - if err == nil { - runtime.Drain(resp) - } - return err == nil -} diff --git a/sdk/azidentity/managed_identity_client_test.go b/sdk/azidentity/managed_identity_client_test.go index 95a80a3697ed..24e5c9be863d 100644 --- a/sdk/azidentity/managed_identity_client_test.go +++ b/sdk/azidentity/managed_identity_client_test.go @@ -11,10 +11,26 @@ import ( "testing" "github.com/Azure/azure-sdk-for-go/sdk/azcore" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" "github.com/Azure/azure-sdk-for-go/sdk/internal/mock" ) +type userAgentValidatingPolicy struct { + t *testing.T + appID string +} + +func (p userAgentValidatingPolicy) Do(req *policy.Request) (*http.Response, error) { + expected := "azsdk-go-" + component + "/" + version + if p.appID != "" { + expected = p.appID + " " + expected + } + if ua := req.Raw().Header.Get("User-Agent"); !strings.HasPrefix(ua, expected) { + p.t.Fatalf("unexpected User-Agent %s", ua) + } + return req.Next() +} + func TestIMDSEndpointParse(t *testing.T) { _, err := url.Parse(imdsEndpoint) if err != nil { @@ -22,48 +38,50 @@ func TestIMDSEndpointParse(t *testing.T) { } } -func TestMSITelemetryDefaultUserAgent(t *testing.T) { +func TestManagedIdentityClient_UserAgent(t *testing.T) { srv, close := mock.NewServer() defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - options := ManagedIdentityCredentialOptions{ClientOptions: azcore.ClientOptions{Transport: srv}} - pipeline := newDefaultMSIPipeline(options) - req, err := runtime.NewRequest(context.Background(), http.MethodGet, srv.URL()) - if err != nil { - t.Fatalf("Unexpected error: %v", err) + setEnvironmentVariables(t, map[string]string{msiEndpoint: srv.URL(), msiSecret: "..."}) + options := ManagedIdentityCredentialOptions{ + ClientOptions: azcore.ClientOptions{ + Transport: srv, PerCallPolicies: []policy.Policy{userAgentValidatingPolicy{t: t}}, + }, } - resp, err := pipeline.Do(req) + client, err := newManagedIdentityClient(&options) if err != nil { - t.Fatalf("Unexpected error: %v", err) + t.Fatal(err) } - if resp.StatusCode != http.StatusOK { - t.Fatalf("unexpected status code: %d", resp.StatusCode) + _, err = client.authenticate(context.Background(), nil, []string{liveTestScope}) + if err != nil { + t.Fatal(err) } - if ua := resp.Request.Header.Get("User-Agent"); !strings.HasPrefix(ua, "azsdk-go-"+component+"/"+version) { - t.Fatalf("unexpected User-Agent %s", ua) + if count := srv.Requests(); count != 1 { + t.Fatalf("expected 1 token request, got %d", count) } } -func TestMSITelemetryCustom(t *testing.T) { - customTelemetry := "customvalue" +func TestManagedIdentityClient_ApplicationID(t *testing.T) { srv, close := mock.NewServer() defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - options := ManagedIdentityCredentialOptions{ClientOptions: azcore.ClientOptions{Transport: srv}} - options.Telemetry.ApplicationID = customTelemetry - pipeline := newDefaultMSIPipeline(options) - req, err := runtime.NewRequest(context.Background(), http.MethodGet, srv.URL()) - if err != nil { - t.Fatalf("Unexpected error: %v", err) + setEnvironmentVariables(t, map[string]string{msiEndpoint: srv.URL(), msiSecret: "..."}) + appID := "customvalue" + options := ManagedIdentityCredentialOptions{ + ClientOptions: azcore.ClientOptions{ + Transport: srv, PerCallPolicies: []policy.Policy{userAgentValidatingPolicy{t: t, appID: appID}}, + }, } - resp, err := pipeline.Do(req) + options.Telemetry.ApplicationID = appID + client, err := newManagedIdentityClient(&options) if err != nil { - t.Fatalf("Unexpected error: %v", err) + t.Fatal(err) } - if resp.StatusCode != http.StatusOK { - t.Fatalf("unexpected status code: %d", resp.StatusCode) + _, err = client.authenticate(context.Background(), nil, []string{liveTestScope}) + if err != nil { + t.Fatal(err) } - if ua := resp.Request.Header.Get("User-Agent"); !strings.HasPrefix(ua, customTelemetry+" "+"azsdk-go-"+component+"/"+version) { - t.Fatalf("unexpected User-Agent %s", ua) + if count := srv.Requests(); count != 1 { + t.Fatalf("expected 1 token request, got %d", count) } } diff --git a/sdk/azidentity/managed_identity_credential.go b/sdk/azidentity/managed_identity_credential.go index 70fe3b636d64..45de47b7cf7b 100644 --- a/sdk/azidentity/managed_identity_credential.go +++ b/sdk/azidentity/managed_identity_credential.go @@ -75,13 +75,11 @@ func NewManagedIdentityCredential(options *ManagedIdentityCredentialOptions) (*M if options == nil { options = &ManagedIdentityCredentialOptions{} } - client := newManagedIdentityClient(options) - msiType, err := client.getMSIType() + client, err := newManagedIdentityClient(options) if err != nil { logCredentialError("Managed Identity Credential", err) return nil, err } - client.msiType = msiType return &ManagedIdentityCredential{id: options.ID, client: client}, nil } @@ -89,13 +87,8 @@ func NewManagedIdentityCredential(options *ManagedIdentityCredentialOptions) (*M // ctx: Context used to control the request lifetime. // opts: Options for the token request, in particular the desired scope of the access token. func (c *ManagedIdentityCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (*azcore.AccessToken, error) { - if opts.Scopes == nil { - err := errors.New("must specify a resource in order to authenticate") - addGetTokenFailureLogs("Managed Identity Credential", err, true) - return nil, err - } if len(opts.Scopes) != 1 { - err := errors.New("can only specify one resource to authenticate with ManagedIdentityCredential") + err := errors.New("ManagedIdentityCredential.GetToken() requires exactly one scope") addGetTokenFailureLogs("Managed Identity Credential", err, true) return nil, err } @@ -107,7 +100,6 @@ func (c *ManagedIdentityCredential) GetToken(ctx context.Context, opts policy.To return nil, err } logGetTokenSuccess(c, opts) - logMSIEnv(c.client.msiType) return tk, err } diff --git a/sdk/azidentity/managed_identity_credential_test.go b/sdk/azidentity/managed_identity_credential_test.go index 6d0a85d2c33a..4bd2609167b7 100644 --- a/sdk/azidentity/managed_identity_credential_test.go +++ b/sdk/azidentity/managed_identity_credential_test.go @@ -23,16 +23,16 @@ import ( ) const ( - msiScope = "https://storage.azure.com" appServiceWindowsSuccessResp = `{"access_token": "new_token", "expires_on": "9/14/2017 00:00:00 PM +00:00", "resource": "https://vault.azure.net", "token_type": "Bearer"}` appServiceLinuxSuccessResp = `{"access_token": "new_token", "expires_on": "09/14/2017 00:00:00 +00:00", "resource": "https://vault.azure.net", "token_type": "Bearer"}` expiresOnIntResp = `{"access_token": "new_token", "refresh_token": "", "expires_in": "", "expires_on": "1560974028", "not_before": "1560970130", "resource": "https://vault.azure.net", "token_type": "Bearer"}` expiresOnNonStringIntResp = `{"access_token": "new_token", "refresh_token": "", "expires_in": "", "expires_on": 1560974028, "not_before": "1560970130", "resource": "https://vault.azure.net", "token_type": "Bearer"}` ) +// TODO: replace with 1.17's T.Setenv func clearEnvVars(envVars ...string) { for _, ev := range envVars { - _ = os.Setenv(ev, "") + _ = os.Unsetenv(ev) } } @@ -55,6 +55,16 @@ func (m *mockIMDS) Do(req *http.Request) (*http.Response, error) { panic("no more responses") } +// delayPolicy adds a delay to pipeline requests. Used to test timeout behavior. +type delayPolicy struct { + delay time.Duration +} + +func (p delayPolicy) Do(req *policy.Request) (resp *http.Response, err error) { + time.Sleep(p.delay) + return req.Next() +} + func TestManagedIdentityCredential_AzureArc(t *testing.T) { file, err := os.Create(filepath.Join(t.TempDir(), "arc.key")) if err != nil { @@ -173,7 +183,7 @@ func TestManagedIdentityCredential_GetTokenInAppServiceV20170901Mock_windows(t * if err != nil { t.Fatalf("unexpected error: %v", err) } - tk, err := msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{msiScope}}) + tk, err := msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) if err != nil { t.Fatalf("Received an error when attempting to retrieve a token") } @@ -196,7 +206,7 @@ func TestManagedIdentityCredential_GetTokenInAppServiceV20170901Mock_linux(t *te if err != nil { t.Fatalf("unexpected error: %v", err) } - tk, err := msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{msiScope}}) + tk, err := msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) if err != nil { t.Fatalf("Received an error when attempting to retrieve a token") } @@ -209,6 +219,7 @@ func TestManagedIdentityCredential_GetTokenInAppServiceV20170901Mock_linux(t *te } func TestManagedIdentityCredential_GetTokenInAppServiceV20190801Mock_windows(t *testing.T) { + t.Skip("App Service 2019-08-01 isn't supported because it's unavailable in some Functions apps.") srv, close := mock.NewServer() defer close() srv.AppendResponse(mock.WithBody([]byte(appServiceWindowsSuccessResp))) @@ -219,7 +230,7 @@ func TestManagedIdentityCredential_GetTokenInAppServiceV20190801Mock_windows(t * if err != nil { t.Fatalf("unexpected error: %v", err) } - tk, err := msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{msiScope}}) + tk, err := msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) if err != nil { t.Fatalf("Received an error when attempting to retrieve a token") } @@ -232,6 +243,7 @@ func TestManagedIdentityCredential_GetTokenInAppServiceV20190801Mock_windows(t * } func TestManagedIdentityCredential_GetTokenInAppServiceV20190801Mock_linux(t *testing.T) { + t.Skip("App Service 2019-08-01 isn't supported because it's unavailable in some Functions apps.") srv, close := mock.NewServer() defer close() srv.AppendResponse(mock.WithBody([]byte(appServiceLinuxSuccessResp))) @@ -242,7 +254,7 @@ func TestManagedIdentityCredential_GetTokenInAppServiceV20190801Mock_linux(t *te if err != nil { t.Fatalf("unexpected error: %v", err) } - tk, err := msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{msiScope}}) + tk, err := msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) if err != nil { t.Fatalf("Received an error when attempting to retrieve a token") } @@ -266,7 +278,7 @@ func TestManagedIdentityCredential_GetTokenInAzureFunctions_linux(t *testing.T) if err != nil { t.Fatalf("unexpected error: %v", err) } - tk, err := msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{msiScope}}) + tk, err := msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) if err != nil { t.Fatalf("Received an error when attempting to retrieve a token") } @@ -279,6 +291,7 @@ func TestManagedIdentityCredential_GetTokenInAzureFunctions_linux(t *testing.T) } func TestManagedIdentityCredential_CreateAppServiceAuthRequestV20190801(t *testing.T) { + t.Skip("App Service 2019-08-01 isn't supported because it's unavailable in some Functions apps.") // setting a dummy value for MSI_ENDPOINT in order to be able to get a ManagedIdentityCredential type in order // to test App Service authentication request creation. setEnvironmentVariables(t, map[string]string{identityEndpoint: "somevalue", identityHeader: "header"}) @@ -287,7 +300,7 @@ func TestManagedIdentityCredential_CreateAppServiceAuthRequestV20190801(t *testi t.Fatalf("unexpected error: %v", err) } cred.client.endpoint = imdsEndpoint - req, err := cred.client.createAuthRequest(context.Background(), ClientID(fakeClientID), []string{msiScope}) + req, err := cred.client.createAuthRequest(context.Background(), ClientID(fakeClientID), []string{liveTestScope}) if err != nil { t.Fatal(err) } @@ -301,7 +314,7 @@ func TestManagedIdentityCredential_CreateAppServiceAuthRequestV20190801(t *testi if reqQueryParams["api-version"][0] != "2019-08-01" { t.Fatalf("Unexpected App Service API version") } - if reqQueryParams["resource"][0] != msiScope { + if reqQueryParams["resource"][0] != liveTestScope { t.Fatalf("Unexpected resource in resource query param") } if reqQueryParams[qpClientID][0] != fakeClientID { @@ -318,7 +331,7 @@ func TestManagedIdentityCredential_CreateAppServiceAuthRequestV20170901(t *testi t.Fatalf("unexpected error: %v", err) } cred.client.endpoint = imdsEndpoint - req, err := cred.client.createAuthRequest(context.Background(), ClientID(fakeClientID), []string{msiScope}) + req, err := cred.client.createAuthRequest(context.Background(), ClientID(fakeClientID), []string{liveTestScope}) if err != nil { t.Fatal(err) } @@ -332,7 +345,7 @@ func TestManagedIdentityCredential_CreateAppServiceAuthRequestV20170901(t *testi if reqQueryParams["api-version"][0] != "2017-09-01" { t.Fatalf("Unexpected App Service API version") } - if reqQueryParams["resource"][0] != msiScope { + if reqQueryParams["resource"][0] != liveTestScope { t.Fatalf("Unexpected resource in resource query param") } if reqQueryParams["clientid"][0] != fakeClientID { @@ -351,7 +364,7 @@ func TestManagedIdentityCredential_CreateAccessTokenExpiresOnStringInt(t *testin if err != nil { t.Fatalf("unexpected error: %v", err) } - _, err = msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{msiScope}}) + _, err = msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) if err != nil { t.Fatalf("Received an error when attempting to retrieve a token") } @@ -368,32 +381,26 @@ func TestManagedIdentityCredential_GetTokenInAppServiceMockFail(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - _, err = msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{msiScope}}) + _, err = msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) if err == nil { t.Fatalf("Expected an error but did not receive one") } } func TestManagedIdentityCredential_GetTokenIMDS400(t *testing.T) { + res := http.Response{StatusCode: http.StatusBadRequest, Body: io.NopCloser(bytes.NewBufferString(""))} options := ManagedIdentityCredentialOptions{} - res1 := http.Response{ - StatusCode: http.StatusBadRequest, - Header: http.Header{}, - Body: io.NopCloser(bytes.NewBufferString("")), - } - res2 := res1 - options.Transport = newMockImds(res1, res2) + options.Transport = newMockImds(res, res, res) cred, err := NewManagedIdentityCredential(&options) if err != nil { - t.Fatalf("unexpected error: %v", err) + t.Fatal(err) } - // cred should return CredentialUnavailableError when IMDS responds 400 to a token request. - // Also, it shouldn't send another token request (mockIMDS will appropriately panic if it does). + // cred should return CredentialUnavailableError when IMDS responds 400 to a token request var expected CredentialUnavailableError for i := 0; i < 3; i++ { - _, err = cred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{msiScope}}) + _, err = cred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) if !errors.As(err, &expected) { - t.Fatalf("Expected %T, got %T", expected, err) + t.Fatalf(`expected CredentialUnavailableError, got %T: "%s"`, err, err.Error()) } } } @@ -426,7 +433,7 @@ func TestManagedIdentityCredential_GetTokenUnexpectedJSON(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - _, err = msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{msiScope}}) + _, err = msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) if err == nil { t.Fatalf("Expected a JSON marshal error but received nil") } @@ -441,7 +448,7 @@ func TestManagedIdentityCredential_CreateIMDSAuthRequest(t *testing.T) { t.Fatalf("unexpected error: %v", err) } cred.client.endpoint = imdsEndpoint - req, err := cred.client.createIMDSAuthRequest(context.Background(), ClientID(fakeClientID), []string{msiScope}) + req, err := cred.client.createIMDSAuthRequest(context.Background(), ClientID(fakeClientID), []string{liveTestScope}) if err != nil { t.Fatal(err) } @@ -455,7 +462,7 @@ func TestManagedIdentityCredential_CreateIMDSAuthRequest(t *testing.T) { if reqQueryParams["api-version"][0] != imdsAPIVersion { t.Fatalf("Unexpected IMDS API version") } - if reqQueryParams["resource"][0] != msiScope { + if reqQueryParams["resource"][0] != liveTestScope { t.Fatalf("Unexpected resource in resource query param") } if reqQueryParams["client_id"][0] != fakeClientID { @@ -469,43 +476,24 @@ func TestManagedIdentityCredential_CreateIMDSAuthRequest(t *testing.T) { } } -func TestManagedIdentityCredential_GetTokenEnvVar(t *testing.T) { - srv, close := mock.NewServer() - defer close() - srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - setEnvironmentVariables(t, map[string]string{"AZURE_CLIENT_ID": "test_client_id", msiEndpoint: srv.URL(), msiSecret: "secret"}) - options := ManagedIdentityCredentialOptions{} - options.Transport = srv - msiCred, err := NewManagedIdentityCredential(&options) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - at, err := msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{msiScope}}) - if err != nil { - t.Fatalf("Received an error when attempting to retrieve a token") - } - if at.Token != "new_token" { - t.Fatalf("Did not receive the correct access token") - } -} - -func TestManagedIdentityCredential_GetTokenNilResource(t *testing.T) { +func TestManagedIdentityCredential_GetTokenScopes(t *testing.T) { srv, close := mock.NewServer() defer close() srv.AppendResponse(mock.WithStatusCode(http.StatusUnauthorized)) - setEnvironmentVariables(t, map[string]string{msiEndpoint: srv.URL()}) options := ManagedIdentityCredentialOptions{} options.Transport = srv msiCred, err := NewManagedIdentityCredential(&options) if err != nil { - t.Fatalf("unexpected error: %v", err) - } - _, err = msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: nil}) - if err == nil { - t.Fatalf("Expected an error but did not receive one") + t.Fatal(err) } - if err.Error() != "must specify a resource in order to authenticate" { - t.Fatalf("unexpected error: %v", err) + for _, scopes := range [][]string{nil, {}, {"a", "b"}} { + _, err = msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: scopes}) + if err == nil { + t.Fatal("expected an error") + } + if !strings.Contains(err.Error(), "scope") { + t.Fatalf(`unexpected error "%s"`, err.Error()) + } } } @@ -530,26 +518,6 @@ func TestManagedIdentityCredential_ScopesImmutable(t *testing.T) { } } -func TestManagedIdentityCredential_GetTokenMultipleResources(t *testing.T) { - srv, close := mock.NewServer() - defer close() - srv.AppendResponse(mock.WithStatusCode(http.StatusUnauthorized)) - setEnvironmentVariables(t, map[string]string{msiEndpoint: srv.URL()}) - options := ManagedIdentityCredentialOptions{} - options.Transport = srv - msiCred, err := NewManagedIdentityCredential(&options) - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - _, err = msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{"resource1", "resource2"}}) - if err == nil { - t.Fatalf("Expected an error but did not receive one") - } - if err.Error() != "can only specify one resource to authenticate with ManagedIdentityCredential" { - t.Fatalf("unexpected error: %v", err) - } -} - func TestManagedIdentityCredential_UseResourceID(t *testing.T) { srv, close := mock.NewServer() defer close() @@ -562,7 +530,7 @@ func TestManagedIdentityCredential_UseResourceID(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - tk, err := cred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{msiScope}}) + tk, err := cred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) if err != nil { t.Fatal(err) } @@ -571,7 +539,8 @@ func TestManagedIdentityCredential_UseResourceID(t *testing.T) { } } -func TestManagedIdentityCredential_ResourceID_AppService(t *testing.T) { +func TestManagedIdentityCredential_ResourceID_AppServiceV20190801(t *testing.T) { + t.Skip("App Service 2019-08-01 isn't supported because it's unavailable in some Functions apps.") setEnvironmentVariables(t, map[string]string{identityEndpoint: "somevalue", identityHeader: "header"}) resID := "sample/resource/id" cred, err := NewManagedIdentityCredential(&ManagedIdentityCredentialOptions{ID: ResourceID(resID)}) @@ -579,7 +548,7 @@ func TestManagedIdentityCredential_ResourceID_AppService(t *testing.T) { t.Fatalf("unexpected error: %v", err) } cred.client.endpoint = imdsEndpoint - req, err := cred.client.createAuthRequest(context.Background(), cred.id, []string{msiScope}) + req, err := cred.client.createAuthRequest(context.Background(), cred.id, []string{liveTestScope}) if err != nil { t.Fatal(err) } @@ -593,7 +562,7 @@ func TestManagedIdentityCredential_ResourceID_AppService(t *testing.T) { if reqQueryParams["api-version"][0] != "2019-08-01" { t.Fatalf("Unexpected App Service API version") } - if reqQueryParams["resource"][0] != msiScope { + if reqQueryParams["resource"][0] != liveTestScope { t.Fatalf("Unexpected resource in resource query param") } if reqQueryParams[qpResID][0] != resID { @@ -603,7 +572,7 @@ func TestManagedIdentityCredential_ResourceID_AppService(t *testing.T) { func TestManagedIdentityCredential_ResourceID_IMDS(t *testing.T) { // setting a dummy value for MSI_ENDPOINT in order to avoid failure in the constructor - setEnvironmentVariables(t, map[string]string{msiEndpoint: "http://foo.com"}) + setEnvironmentVariables(t, map[string]string{msiEndpoint: "http://localhost"}) resID := "sample/resource/id" cred, err := NewManagedIdentityCredential(&ManagedIdentityCredentialOptions{ID: ResourceID(resID)}) if err != nil { @@ -611,7 +580,7 @@ func TestManagedIdentityCredential_ResourceID_IMDS(t *testing.T) { } cred.client.msiType = msiTypeIMDS cred.client.endpoint = imdsEndpoint - req, err := cred.client.createAuthRequest(context.Background(), cred.id, []string{msiScope}) + req, err := cred.client.createAuthRequest(context.Background(), cred.id, []string{liveTestScope}) if err != nil { t.Fatal(err) } @@ -622,7 +591,7 @@ func TestManagedIdentityCredential_ResourceID_IMDS(t *testing.T) { if reqQueryParams["api-version"][0] != "2018-02-01" { t.Fatalf("Unexpected App Service API version") } - if reqQueryParams["resource"][0] != msiScope { + if reqQueryParams["resource"][0] != liveTestScope { t.Fatalf("Unexpected resource in resource query param") } if reqQueryParams[qpResID][0] != resID { @@ -641,7 +610,7 @@ func TestManagedIdentityCredential_CreateAccessTokenExpiresOnInt(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %v", err) } - _, err = msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{msiScope}}) + _, err = msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) if err != nil { t.Fatalf("Received an error when attempting to retrieve a token") } @@ -659,7 +628,7 @@ func TestManagedIdentityCredential_CreateAccessTokenExpiresOnFail(t *testing.T) if err != nil { t.Fatalf("unexpected error: %v", err) } - _, err = msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{msiScope}}) + _, err = msiCred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) if err == nil { t.Fatalf("expected to receive an error but received none") } @@ -749,3 +718,85 @@ func TestManagedIdentityCredential_IMDSResourceIDLive(t *testing.T) { t.Fatal("GetToken returned an invalid expiration time") } } + +func TestManagedIdentityCredential_IMDSTimeoutExceeded(t *testing.T) { + resetEnvironmentVarsForTest() + cred, err := NewManagedIdentityCredential(&ManagedIdentityCredentialOptions{ + ClientOptions: policy.ClientOptions{ + PerCallPolicies: []policy.Policy{delayPolicy{delay: time.Microsecond}}, + }, + }) + if err != nil { + t.Fatal(err) + } + cred.client.imdsTimeout = time.Nanosecond + tk, err := cred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) + var expected CredentialUnavailableError + if !errors.As(err, &expected) { + t.Fatalf(`expected CredentialUnavailableError, got %T: "%v"`, err, err) + } + if tk != nil { + t.Fatal("GetToken returned a token and an error") + } +} + +func TestManagedIdentityCredential_IMDSTimeoutSuccess(t *testing.T) { + resetEnvironmentVarsForTest() + res := http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString(accessTokenRespSuccess))} + options := ManagedIdentityCredentialOptions{} + options.Transport = newMockImds(res, res) + cred, err := NewManagedIdentityCredential(&options) + if err != nil { + t.Fatal(err) + } + cred.client.imdsTimeout = time.Minute + tk, err := cred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) + if err != nil { + t.Fatal(err) + } + if tk.Token != tokenValue { + t.Fatalf(`got unexpected token "%s"`, tk.Token) + } + if !tk.ExpiresOn.After(time.Now().UTC()) { + t.Fatal("GetToken returned an invalid expiration time") + } + if cred.client.imdsTimeout > 0 { + t.Fatal("credential didn't remove IMDS timeout after receiving a response") + } +} + +func TestManagedIdentityCredential_ServiceFabric(t *testing.T) { + resetEnvironmentVarsForTest() + expectedSecret := "expected-secret" + pred := func(req *http.Request) bool { + if secret := req.Header.Get("Secret"); secret != expectedSecret { + t.Fatalf(`unexpected Secret header "%s"`, secret) + } + if p := req.URL.Query().Get("api-version"); p != serviceFabricAPIVersion { + t.Fatalf("unexpected api-version: %s", p) + } + if p := req.URL.Query().Get("resource"); p != strings.TrimSuffix(liveTestScope, defaultSuffix) { + t.Fatalf("unexpected resource: %s", p) + } + return true + } + srv, close := mock.NewServer() + defer close() + srv.AppendResponse(mock.WithPredicate(pred), mock.WithBody([]byte(accessTokenRespSuccess))) + srv.AppendResponse() + setEnvironmentVariables(t, map[string]string{identityEndpoint: srv.URL(), identityHeader: expectedSecret, identityServerThumbprint: "..."}) + cred, err := NewManagedIdentityCredential(nil) + if err != nil { + t.Fatal(err) + } + tk, err := cred.GetToken(context.Background(), policy.TokenRequestOptions{Scopes: []string{liveTestScope}}) + if err != nil { + t.Fatal(err) + } + if tk.Token != tokenValue { + t.Fatalf(`got unexpected token "%s"`, tk.Token) + } + if !tk.ExpiresOn.After(time.Now().UTC()) { + t.Fatal("GetToken returned an invalid expiration time") + } +} diff --git a/sdk/azidentity/testdata/recordings/TestManagedIdentityCredential_IMDSClientIDLive.json b/sdk/azidentity/testdata/recordings/TestManagedIdentityCredential_IMDSClientIDLive.json index 670121c3e9e6..9f02518054a0 100644 --- a/sdk/azidentity/testdata/recordings/TestManagedIdentityCredential_IMDSClientIDLive.json +++ b/sdk/azidentity/testdata/recordings/TestManagedIdentityCredential_IMDSClientIDLive.json @@ -1,29 +1,5 @@ { "Entries": [ - { - "RequestUri": "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01", - "RequestMethod": "GET", - "RequestHeaders": { - ":authority": "localhost:5001", - ":method": "GET", - ":path": "/metadata/identity/oauth2/token?api-version=2018-02-01", - ":scheme": "https", - "Accept-Encoding": "gzip", - "User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.20.0 (go1.17.3; linux)" - }, - "RequestBody": null, - "StatusCode": 400, - "ResponseHeaders": { - "Content-Length": "88", - "Content-Type": "application/json; charset=utf-8", - "Date": "Tue, 16 Nov 2021 17:37:54 GMT", - "Server": "IMDS/150.870.65.523" - }, - "ResponseBody": { - "error": "invalid_request", - "error_description": "Required metadata header not specified" - } - }, { "RequestUri": "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01\u0026client_id=fake-client-id\u0026resource=https%3A%2F%2Fmanagement.core.windows.net%2F", "RequestMethod": "GET", @@ -34,23 +10,23 @@ ":scheme": "https", "Accept-Encoding": "gzip", "metadata": "true", - "User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.20.0 (go1.17.3; linux)" + "User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.21.0 (go1.17.3; linux)" }, "RequestBody": null, "StatusCode": 200, "ResponseHeaders": { - "Content-Length": "248", + "Content-Length": "226", "Content-Type": "application/json; charset=utf-8", - "Date": "Tue, 16 Nov 2021 17:37:54 GMT", + "Date": "Fri, 10 Dec 2021 23:57:32 GMT", "Server": "IMDS/150.870.65.523" }, "ResponseBody": { "access_token": "redacted", - "client_id": "b48ae5dc-70d0-488a-8ef5-f30d905cd5ec", - "expires_in": "86329", - "expires_on": "1637170604", + "client_id": "fake-client-id", + "expires_in": "84474", + "expires_on": "1639265126", "ext_expires_in": "86399", - "not_before": "1637083904", + "not_before": "1639178426", "resource": "https://management.core.windows.net/", "token_type": "Bearer" } diff --git a/sdk/azidentity/testdata/recordings/TestManagedIdentityCredential_IMDSLive.json b/sdk/azidentity/testdata/recordings/TestManagedIdentityCredential_IMDSLive.json index cc07c735178f..50837885ec31 100644 --- a/sdk/azidentity/testdata/recordings/TestManagedIdentityCredential_IMDSLive.json +++ b/sdk/azidentity/testdata/recordings/TestManagedIdentityCredential_IMDSLive.json @@ -1,29 +1,5 @@ { "Entries": [ - { - "RequestUri": "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01", - "RequestMethod": "GET", - "RequestHeaders": { - ":authority": "localhost:5001", - ":method": "GET", - ":path": "/metadata/identity/oauth2/token?api-version=2018-02-01", - ":scheme": "https", - "Accept-Encoding": "gzip", - "User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.20.0 (go1.17.3; linux)" - }, - "RequestBody": null, - "StatusCode": 400, - "ResponseHeaders": { - "Content-Length": "88", - "Content-Type": "application/json; charset=utf-8", - "Date": "Tue, 16 Nov 2021 17:37:54 GMT", - "Server": "IMDS/150.870.65.523" - }, - "ResponseBody": { - "error": "invalid_request", - "error_description": "Required metadata header not specified" - } - }, { "RequestUri": "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01\u0026resource=https%3A%2F%2Fmanagement.core.windows.net%2F", "RequestMethod": "GET", @@ -34,23 +10,23 @@ ":scheme": "https", "Accept-Encoding": "gzip", "metadata": "true", - "User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.20.0 (go1.17.3; linux)" + "User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.21.0 (go1.17.3; linux)" }, "RequestBody": null, "StatusCode": 200, "ResponseHeaders": { "Content-Length": "248", "Content-Type": "application/json; charset=utf-8", - "Date": "Tue, 16 Nov 2021 17:37:54 GMT", + "Date": "Fri, 10 Dec 2021 23:57:31 GMT", "Server": "IMDS/150.870.65.523" }, "ResponseBody": { "access_token": "redacted", "client_id": "615e6319-bf4f-4279-937c-b9eb198e511f", - "expires_in": "84513", - "expires_on": "1637168788", + "expires_in": "84473", + "expires_on": "1639265125", "ext_expires_in": "86399", - "not_before": "1637082088", + "not_before": "1639178425", "resource": "https://management.core.windows.net/", "token_type": "Bearer" } diff --git a/sdk/azidentity/testdata/recordings/TestManagedIdentityCredential_IMDSResourceIDLive.json b/sdk/azidentity/testdata/recordings/TestManagedIdentityCredential_IMDSResourceIDLive.json index e0a74cc8d789..8636ceb125bb 100644 --- a/sdk/azidentity/testdata/recordings/TestManagedIdentityCredential_IMDSResourceIDLive.json +++ b/sdk/azidentity/testdata/recordings/TestManagedIdentityCredential_IMDSResourceIDLive.json @@ -1,29 +1,5 @@ { "Entries": [ - { - "RequestUri": "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01", - "RequestMethod": "GET", - "RequestHeaders": { - ":authority": "localhost:5001", - ":method": "GET", - ":path": "/metadata/identity/oauth2/token?api-version=2018-02-01", - ":scheme": "https", - "Accept-Encoding": "gzip", - "User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.20.0 (go1.17.3; linux)" - }, - "RequestBody": null, - "StatusCode": 400, - "ResponseHeaders": { - "Content-Length": "88", - "Content-Type": "application/json; charset=utf-8", - "Date": "Tue, 16 Nov 2021 17:37:54 GMT", - "Server": "IMDS/150.870.65.523" - }, - "ResponseBody": { - "error": "invalid_request", - "error_description": "Required metadata header not specified" - } - }, { "RequestUri": "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01\u0026mi_res_id=%2Ffake%2Fresource%2FID\u0026resource=https%3A%2F%2Fmanagement.core.windows.net%2F", "RequestMethod": "GET", @@ -34,23 +10,23 @@ ":scheme": "https", "Accept-Encoding": "gzip", "metadata": "true", - "User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.20.0 (go1.17.3; linux)" + "User-Agent": "azsdk-go-azidentity/v0.12.1 azsdk-go-azcore/v0.21.0 (go1.17.3; linux)" }, "RequestBody": null, "StatusCode": 200, "ResponseHeaders": { - "Content-Length": "248", + "Content-Length": "226", "Content-Type": "application/json; charset=utf-8", - "Date": "Tue, 16 Nov 2021 17:37:54 GMT", + "Date": "Fri, 10 Dec 2021 23:57:32 GMT", "Server": "IMDS/150.870.65.523" }, "ResponseBody": { "access_token": "redacted", - "client_id": "b48ae5dc-70d0-488a-8ef5-f30d905cd5ec", - "expires_in": "86329", - "expires_on": "1637170604", + "client_id": "fake-client-id", + "expires_in": "84474", + "expires_on": "1639265126", "ext_expires_in": "86399", - "not_before": "1637083904", + "not_before": "1639178426", "resource": "https://management.core.windows.net/", "token_type": "Bearer" }