diff --git a/sdk/azidentity/aad_identity_client.go b/sdk/azidentity/aad_identity_client.go index d5c0f39cb293..48229fabe57d 100644 --- a/sdk/azidentity/aad_identity_client.go +++ b/sdk/azidentity/aad_identity_client.go @@ -380,7 +380,7 @@ func (c *aadIdentityClient) createDeviceCodeNumberRequest(ctx context.Context, t // clientSecret: Gets the client secret that was generated for the App Registration used to authenticate the client. // redirectURI: The redirect URI that was used to request the authorization code. Must be the same URI that is configured for the App Registration. // scopes: The scopes required for the token -func (c *aadIdentityClient) authenticateInteractiveBrowser(ctx context.Context, tenantID string, clientID string, clientSecret *string, redirectURI *string, scopes []string) (*azcore.AccessToken, error) { +func (c *aadIdentityClient) authenticateInteractiveBrowser(ctx context.Context, tenantID string, clientID string, clientSecret string, redirectURI string, scopes []string) (*azcore.AccessToken, error) { cfg, err := authCodeReceiver(c.authorityHost, tenantID, clientID, redirectURI, scopes) if err != nil { return nil, err @@ -396,7 +396,7 @@ func (c *aadIdentityClient) authenticateInteractiveBrowser(ctx context.Context, // clientSecret: Gets the client secret that was generated for the App Registration used to authenticate the client. // redirectURI: The redirect URI that was used to request the authorization code. Must be the same URI that is configured for the App Registration. // scopes: The scopes required for the token -func (c *aadIdentityClient) authenticateAuthCode(ctx context.Context, tenantID string, clientID string, authCode string, clientSecret *string, redirectURI string, scopes []string) (*azcore.AccessToken, error) { +func (c *aadIdentityClient) authenticateAuthCode(ctx context.Context, tenantID string, clientID string, authCode string, clientSecret string, redirectURI string, scopes []string) (*azcore.AccessToken, error) { req, err := c.createAuthorizationCodeAuthRequest(ctx, tenantID, clientID, authCode, clientSecret, redirectURI, scopes) if err != nil { return nil, err @@ -415,12 +415,12 @@ func (c *aadIdentityClient) authenticateAuthCode(ctx context.Context, tenantID s } // createAuthorizationCodeAuthRequest creates a request for an Access Token for authorization_code grant types. -func (c *aadIdentityClient) createAuthorizationCodeAuthRequest(ctx context.Context, tenantID string, clientID string, authCode string, clientSecret *string, redirectURI string, scopes []string) (*azcore.Request, error) { +func (c *aadIdentityClient) createAuthorizationCodeAuthRequest(ctx context.Context, tenantID string, clientID string, authCode string, clientSecret string, redirectURI string, scopes []string) (*azcore.Request, error) { data := url.Values{} data.Set(qpGrantType, "authorization_code") data.Set(qpClientID, clientID) - if clientSecret != nil { - data.Set(qpClientSecret, *clientSecret) // only for web apps + if clientSecret != "" { + data.Set(qpClientSecret, clientSecret) // only for web apps } data.Set(qpRedirectURI, redirectURI) data.Set(qpScope, strings.Join(scopes, " ")) diff --git a/sdk/azidentity/authorization_code_credential.go b/sdk/azidentity/authorization_code_credential.go index f7f3d3e985c9..4f6c5036a468 100644 --- a/sdk/azidentity/authorization_code_credential.go +++ b/sdk/azidentity/authorization_code_credential.go @@ -10,46 +10,51 @@ import ( ) // AuthorizationCodeCredentialOptions contain optional parameters that can be used to configure the AuthorizationCodeCredential. +// Call DefaultAuthorizationCodeCredentialOptions() to create an instance populated with default values. type AuthorizationCodeCredentialOptions struct { // Gets the client secret that was generated for the App Registration used to authenticate the client. - ClientSecret *string - // The host of the Azure Active Directory authority. The default is https://login.microsoft.com + ClientSecret string + // The host of the Azure Active Directory authority. The default is AzurePublicCloud. + // Leave empty to allow overriding the value from the AZURE_AUTHORITY_HOST environment variable. AuthorityHost string // HTTPClient sets the transport for making HTTP requests // Leave this as nil to use the default HTTP transport HTTPClient azcore.Transport // Retry configures the built-in retry policy behavior - Retry *azcore.RetryOptions + Retry azcore.RetryOptions // Telemetry configures the built-in telemetry policy behavior Telemetry azcore.TelemetryOptions } +// DefaultAuthorizationCodeCredentialOptions returns an instance of AuthorizationCodeCredentialOptions initialized with default values. +func DefaultAuthorizationCodeCredentialOptions() AuthorizationCodeCredentialOptions { + return AuthorizationCodeCredentialOptions{ + Retry: azcore.DefaultRetryOptions(), + Telemetry: azcore.DefaultTelemetryOptions(), + } +} + // AuthorizationCodeCredential enables authentication to Azure Active Directory using an authorization code // that was obtained through the authorization code flow, described in more detail in the Azure Active Directory // documentation: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow. type AuthorizationCodeCredential struct { client *aadIdentityClient - tenantID string // Gets the Azure Active Directory tenant (directory) ID of the service principal - clientID string // Gets the client (application) ID of the service principal - authCode string // The authorization code received from the authorization code flow. The authorization code must not have been used to obtain another token. - clientSecret *string // Gets the client secret that was generated for the App Registration used to authenticate the client. - redirectURI string // The redirect URI that was used to request the authorization code. Must be the same URI that is configured for the App Registration. -} - -// DefaultAuthorizationCodeCredentialOptions returns an instance of AuthorizationCodeCredentialOptions initialized with default values. -func DefaultAuthorizationCodeCredentialOptions() AuthorizationCodeCredentialOptions { - return AuthorizationCodeCredentialOptions{} + tenantID string // Gets the Azure Active Directory tenant (directory) ID of the service principal + clientID string // Gets the client (application) ID of the service principal + authCode string // The authorization code received from the authorization code flow. The authorization code must not have been used to obtain another token. + clientSecret string // Gets the client secret that was generated for the App Registration used to authenticate the client. + redirectURI string // The redirect URI that was used to request the authorization code. Must be the same URI that is configured for the App Registration. } // NewAuthorizationCodeCredential constructs a new AuthorizationCodeCredential with the details needed to authenticate against Azure Active Directory with an authorization code. // tenantID: The Azure Active Directory tenant (directory) ID of the service principal. // clientID: The client (application) ID of the service principal. // authCode: The authorization code received from the authorization code flow. The authorization code must not have been used to obtain another token. -// redirectURI: The redirect URI that was used to request the authorization code. Must be the same URI that is configured for the App Registration. +// redirectURL: The redirect URL that was used to request the authorization code. Must be the same URL that is configured for the App Registration. // options: Manage the configuration of the requests sent to Azure Active Directory, they can also include a client secret for web app authentication. -func NewAuthorizationCodeCredential(tenantID string, clientID string, authCode string, redirectURI string, options *AuthorizationCodeCredentialOptions) (*AuthorizationCodeCredential, error) { +func NewAuthorizationCodeCredential(tenantID string, clientID string, authCode string, redirectURL string, options *AuthorizationCodeCredentialOptions) (*AuthorizationCodeCredential, error) { if !validTenantID(tenantID) { - return nil, &CredentialUnavailableError{CredentialType: "Authorization Code Credential", Message: tenantIDValidationErr} + return nil, &CredentialUnavailableError{credentialType: "Authorization Code Credential", message: tenantIDValidationErr} } if options == nil { temp := DefaultAuthorizationCodeCredentialOptions() @@ -63,7 +68,7 @@ func NewAuthorizationCodeCredential(tenantID string, clientID string, authCode s if err != nil { return nil, err } - return &AuthorizationCodeCredential{tenantID: tenantID, clientID: clientID, authCode: authCode, clientSecret: options.ClientSecret, redirectURI: redirectURI, client: c}, nil + return &AuthorizationCodeCredential{tenantID: tenantID, clientID: clientID, authCode: authCode, clientSecret: options.ClientSecret, redirectURI: redirectURL, client: c}, nil } // GetToken obtains a token from Azure Active Directory, using the specified authorization code to authenticate. diff --git a/sdk/azidentity/authorization_code_credential_test.go b/sdk/azidentity/authorization_code_credential_test.go index 1169b53219d9..f2af7290e9ed 100644 --- a/sdk/azidentity/authorization_code_credential_test.go +++ b/sdk/azidentity/authorization_code_credential_test.go @@ -80,8 +80,7 @@ func TestAuthorizationCodeCredential_GetTokenSuccess(t *testing.T) { defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) options := DefaultAuthorizationCodeCredentialOptions() - s := secret - options.ClientSecret = &s + options.ClientSecret = secret options.AuthorityHost = srv.URL() options.HTTPClient = srv cred, err := NewAuthorizationCodeCredential(tenantID, clientID, testAuthCode, testRedirectURI, &options) @@ -99,8 +98,7 @@ func TestAuthorizationCodeCredential_GetTokenInvalidCredentials(t *testing.T) { defer close() srv.SetResponse(mock.WithBody([]byte(accessTokenRespError)), mock.WithStatusCode(http.StatusUnauthorized)) options := DefaultAuthorizationCodeCredentialOptions() - s := secret - options.ClientSecret = &s + options.ClientSecret = secret options.AuthorityHost = srv.URL() options.HTTPClient = srv cred, err := NewAuthorizationCodeCredential(tenantID, clientID, testAuthCode, testRedirectURI, &options) @@ -134,8 +132,8 @@ func TestAuthorizationCodeCredential_GetTokenInvalidCredentials(t *testing.T) { if len(respError.CorrelationID) == 0 { t.Fatalf("Did not receive a CorrelationID") } - if len(respError.URI) == 0 { - t.Fatalf("Did not receive an error URI") + if len(respError.URL) == 0 { + t.Fatalf("Did not receive an error URL") } if respError.Response == nil { t.Fatalf("Did not receive an error response") @@ -149,8 +147,7 @@ func TestAuthorizationCodeCredential_GetTokenUnexpectedJSON(t *testing.T) { defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespMalformed))) options := DefaultAuthorizationCodeCredentialOptions() - s := secret - options.ClientSecret = &s + options.ClientSecret = secret options.AuthorityHost = srv.URL() options.HTTPClient = srv cred, err := NewAuthorizationCodeCredential(tenantID, clientID, testRedirectURI, testRedirectURI, &options) diff --git a/sdk/azidentity/azidentity.go b/sdk/azidentity/azidentity.go index 4198aea333f4..dadf4bcc5dbe 100644 --- a/sdk/azidentity/azidentity.go +++ b/sdk/azidentity/azidentity.go @@ -48,7 +48,7 @@ type AADAuthenticationFailedError struct { Timestamp string `json:"timestamp"` TraceID string `json:"trace_id"` CorrelationID string `json:"correlation_id"` - URI string `json:"error_uri"` + URL string `json:"error_uri"` Response *azcore.Response } @@ -101,13 +101,13 @@ func newAADAuthenticationFailedError(resp *azcore.Response) error { // create a credential do not exist or are unavailable. type CredentialUnavailableError struct { // CredentialType holds the name of the credential that is unavailable - CredentialType string + credentialType string // Message contains the reason why the credential is unavailable - Message string + message string } func (e *CredentialUnavailableError) Error() string { - return e.CredentialType + ": " + e.Message + return e.credentialType + ": " + e.message } // NonRetriable indicates that this error should not be retried. @@ -124,7 +124,7 @@ type pipelineOptions struct { HTTPClient azcore.Transport // Retry configures the built-in retry policy behavior - Retry *azcore.RetryOptions + Retry azcore.RetryOptions // Telemetry configures the built-in telemetry policy behavior Telemetry azcore.TelemetryOptions @@ -134,6 +134,7 @@ type pipelineOptions struct { func setAuthorityHost(authorityHost string) (string, error) { if authorityHost == "" { authorityHost = AzurePublicCloud + // NOTE: we only allow overriding the authority host if no host was specified if envAuthorityHost := os.Getenv("AZURE_AUTHORITY_HOST"); envAuthorityHost != "" { authorityHost = envAuthorityHost } @@ -153,7 +154,7 @@ func newDefaultPipeline(o pipelineOptions) azcore.Pipeline { return azcore.NewPipeline( o.HTTPClient, azcore.NewTelemetryPolicy(&o.Telemetry), - azcore.NewRetryPolicy(o.Retry), + azcore.NewRetryPolicy(&o.Retry), azcore.NewLogPolicy(nil)) } diff --git a/sdk/azidentity/azidentity_test.go b/sdk/azidentity/azidentity_test.go index 7398f61f8996..d0d7f67853c2 100644 --- a/sdk/azidentity/azidentity_test.go +++ b/sdk/azidentity/azidentity_test.go @@ -23,6 +23,13 @@ func Test_AuthorityHost_Parse(t *testing.T) { func Test_SetEnvAuthorityHost(t *testing.T) { err := os.Setenv("AZURE_AUTHORITY_HOST", envHostString) + defer func() { + // Unset that host environment variable to avoid other tests failed. + err = os.Unsetenv("AZURE_AUTHORITY_HOST") + if err != nil { + t.Fatalf("Unexpected error when unset environment variable: %v", err) + } + }() if err != nil { t.Fatalf("Unexpected error when initializing environment variables: %v", err) } @@ -31,34 +38,29 @@ func Test_SetEnvAuthorityHost(t *testing.T) { t.Fatal(err) } if authorityHost != envHostString { - t.Fatalf("Unexpected error when get host from environment vairable: %v", err) - } - - // Unset that host environment vairable to avoid other tests failed. - err = os.Unsetenv("AZURE_AUTHORITY_HOST") - if err != nil { - t.Fatalf("Unexpected error when unset environment vairable: %v", err) + t.Fatalf("Unexpected error when get host from environment variable: %v", err) } } func Test_CustomAuthorityHost(t *testing.T) { err := os.Setenv("AZURE_AUTHORITY_HOST", envHostString) + defer func() { + // Unset that host environment variable to avoid other tests failed. + err = os.Unsetenv("AZURE_AUTHORITY_HOST") + if err != nil { + t.Fatalf("Unexpected error when unset environment variable: %v", err) + } + }() if err != nil { t.Fatalf("Unexpected error when initializing environment variables: %v", err) } - authorityHost, err := setAuthorityHost(customHostString) if err != nil { t.Fatal(err) } + // ensure env var doesn't override explicit value if authorityHost != customHostString { - t.Fatalf("Unexpected error when get host from environment vairable: %v", err) - } - - // Unset that host environment vairable to avoid other tests failed. - err = os.Unsetenv("AZURE_AUTHORITY_HOST") - if err != nil { - t.Fatalf("Unexpected error when unset environment vairable: %v", err) + t.Fatalf("Unexpected host when get host from environment variable: %v", authorityHost) } } @@ -68,7 +70,7 @@ func Test_DefaultAuthorityHost(t *testing.T) { t.Fatal(err) } if authorityHost != AzurePublicCloud { - t.Fatalf("Unexpected error when set default AuthorityHost: %v", err) + t.Fatalf("Unexpected host when set default AuthorityHost: %v", authorityHost) } } diff --git a/sdk/azidentity/azure_cli_credential.go b/sdk/azidentity/azure_cli_credential.go index 2de9caff4a4c..15483cf46ab9 100644 --- a/sdk/azidentity/azure_cli_credential.go +++ b/sdk/azidentity/azure_cli_credential.go @@ -22,10 +22,16 @@ import ( type AzureCLITokenProvider func(ctx context.Context, resource string) ([]byte, error) // AzureCLICredentialOptions contains options used to configure the AzureCLICredential +// Call DefaultAzureCLICredentialOptions() to create an instance populated with default values. type AzureCLICredentialOptions struct { TokenProvider AzureCLITokenProvider } +// DefaultAzureCLICredentialOptions returns an instance of AzureCLICredentialOptions initialized with default values. +func DefaultAzureCLICredentialOptions() AzureCLICredentialOptions { + return AzureCLICredentialOptions{TokenProvider: defaultTokenProvider()} +} + // AzureCLICredential enables authentication to Azure Active Directory using the Azure CLI command "az account get-access-token". type AzureCLICredential struct { tokenProvider AzureCLITokenProvider @@ -35,7 +41,8 @@ type AzureCLICredential struct { // options: configure the management of the requests sent to Azure Active Directory. func NewAzureCLICredential(options *AzureCLICredentialOptions) (*AzureCLICredential, error) { if options == nil { - options = &AzureCLICredentialOptions{TokenProvider: defaultTokenProvider()} + def := DefaultAzureCLICredentialOptions() + options = &def } return &AzureCLICredential{ tokenProvider: options.TokenProvider, @@ -122,7 +129,7 @@ func defaultTokenProvider() func(ctx context.Context, resource string) ([]byte, output, err := cliCmd.Output() if err != nil { - return nil, &CredentialUnavailableError{CredentialType: "Azure CLI Credential", Message: stderr.String()} + return nil, &CredentialUnavailableError{credentialType: "Azure CLI Credential", message: stderr.String()} } return output, nil diff --git a/sdk/azidentity/azure_cli_credential_test.go b/sdk/azidentity/azure_cli_credential_test.go index 8138c65bfadb..89a305c7fda8 100644 --- a/sdk/azidentity/azure_cli_credential_test.go +++ b/sdk/azidentity/azure_cli_credential_test.go @@ -27,7 +27,9 @@ var ( ) func TestAzureCLICredential_GetTokenSuccess(t *testing.T) { - cred, err := NewAzureCLICredential(&AzureCLICredentialOptions{TokenProvider: mockCLITokenProviderSuccess}) + options := DefaultAzureCLICredentialOptions() + options.TokenProvider = mockCLITokenProviderSuccess + cred, err := NewAzureCLICredential(&options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -44,7 +46,9 @@ func TestAzureCLICredential_GetTokenSuccess(t *testing.T) { } func TestAzureCLICredential_GetTokenInvalidToken(t *testing.T) { - cred, err := NewAzureCLICredential(&AzureCLICredentialOptions{TokenProvider: mockCLITokenProviderFailure}) + options := DefaultAzureCLICredentialOptions() + options.TokenProvider = mockCLITokenProviderFailure + cred, err := NewAzureCLICredential(&options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -58,7 +62,9 @@ func TestBearerPolicy_AzureCLICredential(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithStatusCode(http.StatusOK)) - cred, err := NewAzureCLICredential(&AzureCLICredentialOptions{TokenProvider: mockCLITokenProviderSuccess}) + options := DefaultAzureCLICredentialOptions() + options.TokenProvider = mockCLITokenProviderSuccess + cred, err := NewAzureCLICredential(&options) if err != nil { t.Fatalf("Did not expect an error but received: %v", err) } diff --git a/sdk/azidentity/bearer_token_policy_test.go b/sdk/azidentity/bearer_token_policy_test.go index c2cba6576189..ea7afa3cf645 100644 --- a/sdk/azidentity/bearer_token_policy_test.go +++ b/sdk/azidentity/bearer_token_policy_test.go @@ -22,9 +22,12 @@ const ( ) func defaultTestPipeline(srv azcore.Transport, cred azcore.Credential, scope string) azcore.Pipeline { + retryOpts := azcore.DefaultRetryOptions() + retryOpts.MaxRetryDelay = 500 * time.Millisecond + retryOpts.RetryDelay = 50 * time.Millisecond return azcore.NewPipeline( srv, - azcore.NewRetryPolicy(nil), + azcore.NewRetryPolicy(&retryOpts), cred.AuthenticationPolicy(azcore.AuthenticationPolicyOptions{Options: azcore.TokenRequestOptions{Scopes: []string{scope}}}), azcore.NewLogPolicy(nil)) } @@ -34,7 +37,10 @@ func TestBearerPolicy_SuccessGetToken(t *testing.T) { defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) srv.AppendResponse(mock.WithStatusCode(http.StatusOK)) - cred, err := NewClientSecretCredential(tenantID, clientID, secret, &ClientSecretCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientSecretCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientSecretCredential(tenantID, clientID, secret, &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -58,7 +64,10 @@ func TestBearerPolicy_CredentialFailGetToken(t *testing.T) { defer close() srv.AppendResponse(mock.WithStatusCode(http.StatusUnauthorized)) srv.AppendResponse(mock.WithStatusCode(http.StatusOK)) - cred, err := NewClientSecretCredential(tenantID, clientID, wrongSecret, &ClientSecretCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientSecretCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientSecretCredential(tenantID, clientID, wrongSecret, &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -86,8 +95,10 @@ func TestBearerTokenPolicy_TokenExpired(t *testing.T) { srv.AppendResponse(mock.WithStatusCode(http.StatusOK)) srv.AppendResponse(mock.WithBody([]byte(accessTokenRespShortLived))) srv.AppendResponse(mock.WithStatusCode(http.StatusOK)) - - cred, err := NewClientSecretCredential(tenantID, clientID, secret, &ClientSecretCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientSecretCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientSecretCredential(tenantID, clientID, secret, &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -112,7 +123,10 @@ func TestRetryPolicy_NonRetriable(t *testing.T) { defer close() srv.AppendResponse(mock.WithStatusCode(http.StatusUnauthorized)) srv.AppendResponse(mock.WithStatusCode(http.StatusOK)) - cred, err := NewClientSecretCredential(tenantID, clientID, wrongSecret, &ClientSecretCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientSecretCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientSecretCredential(tenantID, clientID, wrongSecret, &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -132,7 +146,10 @@ func TestRetryPolicy_HTTPRequest(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithStatusCode(http.StatusUnauthorized)) - cred, err := NewClientSecretCredential(tenantID, clientID, wrongSecret, &ClientSecretCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientSecretCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientSecretCredential(tenantID, clientID, wrongSecret, &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -156,7 +173,7 @@ func TestBearerPolicy_GetTokenFailsNoDeadlock(t *testing.T) { cred, err := NewClientSecretCredential(tenantID, clientID, secret, &ClientSecretCredentialOptions{ HTTPClient: srv, AuthorityHost: srv.URL(), - Retry: &azcore.RetryOptions{ + Retry: azcore.RetryOptions{ // leaving TryTimeout at zero will trigger a deadline exceeded error causing GetToken() to fail RetryDelay: 50 * time.Millisecond, MaxRetryDelay: 500 * time.Millisecond, diff --git a/sdk/azidentity/chained_token_credential.go b/sdk/azidentity/chained_token_credential.go index 748a7af36aeb..af06decfd221 100644 --- a/sdk/azidentity/chained_token_credential.go +++ b/sdk/azidentity/chained_token_credential.go @@ -19,14 +19,14 @@ type ChainedTokenCredential struct { // NewChainedTokenCredential creates an instance of ChainedTokenCredential with the specified TokenCredential sources. func NewChainedTokenCredential(sources ...azcore.TokenCredential) (*ChainedTokenCredential, error) { if len(sources) == 0 { - credErr := &CredentialUnavailableError{CredentialType: "Chained Token Credential", Message: "Length of sources cannot be 0"} - logCredentialError(credErr.CredentialType, credErr) + credErr := &CredentialUnavailableError{credentialType: "Chained Token Credential", message: "Length of sources cannot be 0"} + logCredentialError(credErr.credentialType, credErr) return nil, credErr } for _, source := range sources { if source == nil { // cannot have a nil credential in the chain or else the application will panic when GetToken() is called on nil - credErr := &CredentialUnavailableError{CredentialType: "Chained Token Credential", Message: "Sources cannot contain a nil TokenCredential"} - logCredentialError(credErr.CredentialType, credErr) + credErr := &CredentialUnavailableError{credentialType: "Chained Token Credential", message: "Sources cannot contain a nil TokenCredential"} + logCredentialError(credErr.credentialType, credErr) return nil, credErr } } @@ -62,7 +62,7 @@ func (c *ChainedTokenCredential) GetToken(ctx context.Context, opts azcore.Token } } // if we reach this point it means that all of the credentials in the chain returned CredentialUnavailableErrors - credErr := &CredentialUnavailableError{CredentialType: "Chained Token Credential", Message: createChainedErrorMessage(errList)} + credErr := &CredentialUnavailableError{credentialType: "Chained Token Credential", message: createChainedErrorMessage(errList)} // skip adding the stack trace here as it was already logged by other calls to GetToken() addGetTokenFailureLogs("Chained Token Credential", credErr, false) return nil, credErr diff --git a/sdk/azidentity/chained_token_credential_test.go b/sdk/azidentity/chained_token_credential_test.go index ad426f9dbf4e..f2a744f5ee51 100644 --- a/sdk/azidentity/chained_token_credential_test.go +++ b/sdk/azidentity/chained_token_credential_test.go @@ -67,7 +67,10 @@ func TestChainedTokenCredential_GetTokenSuccess(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - secCred, err := NewClientSecretCredential(tenantID, clientID, secret, &ClientSecretCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientSecretCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + secCred, err := NewClientSecretCredential(tenantID, clientID, secret, &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -95,7 +98,10 @@ func TestChainedTokenCredential_GetTokenFail(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithStatusCode(http.StatusUnauthorized)) - secCred, err := NewClientSecretCredential(tenantID, clientID, wrongSecret, &ClientSecretCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientSecretCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + secCred, err := NewClientSecretCredential(tenantID, clientID, wrongSecret, &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -119,9 +125,12 @@ func TestChainedTokenCredential_GetTokenFail(t *testing.T) { func TestChainedTokenCredential_GetTokenWithUnavailableCredentialInChain(t *testing.T) { srv, close := mock.NewTLSServer() defer close() - srv.AppendError(&CredentialUnavailableError{CredentialType: "MockCredential", Message: "Mocking a credential unavailable error"}) + srv.AppendError(&CredentialUnavailableError{credentialType: "MockCredential", message: "Mocking a credential unavailable error"}) srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - secCred, err := NewClientSecretCredential(tenantID, clientID, wrongSecret, &ClientSecretCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientSecretCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + secCred, err := NewClientSecretCredential(tenantID, clientID, wrongSecret, &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -155,7 +164,10 @@ func TestBearerPolicy_ChainedTokenCredential(t *testing.T) { defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) srv.AppendResponse(mock.WithStatusCode(http.StatusOK)) - cred, err := NewClientSecretCredential(tenantID, clientID, secret, &ClientSecretCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientSecretCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientSecretCredential(tenantID, clientID, secret, &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } diff --git a/sdk/azidentity/client_certificate_credential.go b/sdk/azidentity/client_certificate_credential.go index b014bfd300ab..0a5c36d4b73e 100644 --- a/sdk/azidentity/client_certificate_credential.go +++ b/sdk/azidentity/client_certificate_credential.go @@ -17,36 +17,39 @@ import ( "golang.org/x/crypto/pkcs12" ) -// ClientCertificateCredential enables authentication of a service principal to Azure Active Directory using a certificate that is assigned to its App Registration. More information -// on how to configure certificate authentication can be found here: -// https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials#register-your-certificate-with-azure-ad -type ClientCertificateCredential struct { - client *aadIdentityClient - tenantID string // The Azure Active Directory tenant (directory) ID of the service principal - clientID string // The client (application) ID of the service principal - cert *certContents // The contents of the certificate file -} - // ClientCertificateCredentialOptions contain optional parameters that can be used when configuring a ClientCertificateCredential. -// password: The password required to decrypt the private key. Pass nil if there is no password. -// options: Manage the configuration of requests sent to Azure Active Directory. +// Call DefaultClientCertificateCredentialOptions() to create an instance populated with default values. type ClientCertificateCredentialOptions struct { - // The password required to decrypt the private key. Pass nil if there is no password. - Password *string - // The host of the Azure Active Directory authority. The default is https://login.microsoft.com + // The password required to decrypt the private key. Leave empty if there is no password. + Password string + // The host of the Azure Active Directory authority. The default is AzurePublicCloud. + // Leave empty to allow overriding the value from the AZURE_AUTHORITY_HOST environment variable. AuthorityHost string // HTTPClient sets the transport for making HTTP requests // Leave this as nil to use the default HTTP transport HTTPClient azcore.Transport // Retry configures the built-in retry policy behavior - Retry *azcore.RetryOptions + Retry azcore.RetryOptions // Telemetry configures the built-in telemetry policy behavior Telemetry azcore.TelemetryOptions } // DefaultClientCertificateCredentialOptions returns an instance of ClientCertificateCredentialOptions initialized with default values. func DefaultClientCertificateCredentialOptions() ClientCertificateCredentialOptions { - return ClientCertificateCredentialOptions{} + return ClientCertificateCredentialOptions{ + Retry: azcore.DefaultRetryOptions(), + Telemetry: azcore.DefaultTelemetryOptions(), + } +} + +// ClientCertificateCredential enables authentication of a service principal to Azure Active Directory using a certificate that is assigned to its App Registration. More information +// on how to configure certificate authentication can be found here: +// https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials#register-your-certificate-with-azure-ad +type ClientCertificateCredential struct { + client *aadIdentityClient + tenantID string // The Azure Active Directory tenant (directory) ID of the service principal + clientID string // The client (application) ID of the service principal + cert *certContents // The contents of the certificate file } // NewClientCertificateCredential creates an instance of ClientCertificateCredential with the details needed to authenticate against Azure Active Directory with the specified certificate. @@ -57,18 +60,18 @@ func DefaultClientCertificateCredentialOptions() ClientCertificateCredentialOpti // options: ClientCertificateCredentialOptions that can be used to provide additional configurations for the credential. func NewClientCertificateCredential(tenantID string, clientID string, certificatePath string, options *ClientCertificateCredentialOptions) (*ClientCertificateCredential, error) { if !validTenantID(tenantID) { - return nil, &CredentialUnavailableError{CredentialType: "Client Certificate Credential", Message: tenantIDValidationErr} + return nil, &CredentialUnavailableError{credentialType: "Client Certificate Credential", message: tenantIDValidationErr} } _, err := os.Stat(certificatePath) if err != nil { - credErr := &CredentialUnavailableError{CredentialType: "Client Certificate Credential", Message: "Certificate file not found in path: " + certificatePath} - logCredentialError(credErr.CredentialType, credErr) + credErr := &CredentialUnavailableError{credentialType: "Client Certificate Credential", message: "Certificate file not found in path: " + certificatePath} + logCredentialError(credErr.credentialType, credErr) return nil, credErr } certData, err := ioutil.ReadFile(certificatePath) if err != nil { - credErr := &CredentialUnavailableError{CredentialType: "Client Certificate Credential", Message: err.Error()} - logCredentialError(credErr.CredentialType, credErr) + credErr := &CredentialUnavailableError{credentialType: "Client Certificate Credential", message: err.Error()} + logCredentialError(credErr.credentialType, credErr) return nil, credErr } if options == nil { @@ -85,8 +88,8 @@ func NewClientCertificateCredential(tenantID string, clientID string, certificat err = errors.New("only PEM and PFX files are supported") } if err != nil { - credErr := &CredentialUnavailableError{CredentialType: "Client Certificate Credential", Message: err.Error()} - logCredentialError(credErr.CredentialType, credErr) + credErr := &CredentialUnavailableError{credentialType: "Client Certificate Credential", message: err.Error()} + logCredentialError(credErr.credentialType, credErr) return nil, credErr } authorityHost, err := setAuthorityHost(options.AuthorityHost) @@ -163,7 +166,7 @@ func newCertContents(blocks []*pem.Block, fromPEM bool) (*certContents, error) { return &cc, nil } -func extractFromPEMFile(certData []byte, password *string) (*certContents, error) { +func extractFromPEMFile(certData []byte, password string) (*certContents, error) { // TODO: wire up support for password blocks := []*pem.Block{} // read all of the PEM blocks @@ -181,13 +184,9 @@ func extractFromPEMFile(certData []byte, password *string) (*certContents, error return newCertContents(blocks, true) } -func extractFromPFXFile(certData []byte, password *string) (*certContents, error) { - if password == nil { - empty := "" - password = &empty - } +func extractFromPFXFile(certData []byte, password string) (*certContents, error) { // convert PFX binary data to PEM blocks - blocks, err := pkcs12.ToPEM(certData, *password) + blocks, err := pkcs12.ToPEM(certData, password) if err != nil { return nil, err } diff --git a/sdk/azidentity/client_certificate_credential_test.go b/sdk/azidentity/client_certificate_credential_test.go index 43180beb85a1..f49ed9c0b072 100644 --- a/sdk/azidentity/client_certificate_credential_test.go +++ b/sdk/azidentity/client_certificate_credential_test.go @@ -82,7 +82,10 @@ func TestClientCertificateCredential_GetTokenSuccess(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - cred, err := NewClientCertificateCredential(tenantID, clientID, certificatePath, &ClientCertificateCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientCertificateCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientCertificateCredential(tenantID, clientID, certificatePath, &options) if err != nil { t.Fatalf("Expected an empty error but received: %s", err.Error()) } @@ -96,7 +99,10 @@ func TestClientCertificateCredential_GetTokenInvalidCredentials(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.SetResponse(mock.WithStatusCode(http.StatusUnauthorized)) - cred, err := NewClientCertificateCredential(tenantID, clientID, certificatePath, &ClientCertificateCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientCertificateCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientCertificateCredential(tenantID, clientID, certificatePath, &options) if err != nil { t.Fatalf("Did not expect an error but received one: %v", err) } @@ -114,7 +120,10 @@ func TestClientCertificateCredential_WrongCertificatePath(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.SetResponse(mock.WithStatusCode(http.StatusUnauthorized)) - _, err := NewClientCertificateCredential(tenantID, clientID, wrongCertificatePath, &ClientCertificateCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientCertificateCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + _, err := NewClientCertificateCredential(tenantID, clientID, wrongCertificatePath, &options) if err == nil { t.Fatalf("Expected an error but did not receive one") } @@ -124,7 +133,10 @@ func TestClientCertificateCredential_GetTokenCheckPrivateKeyBlocks(t *testing.T) srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - cred, err := NewClientCertificateCredential(tenantID, clientID, "testdata/certificate_formatB.pem", &ClientCertificateCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientCertificateCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientCertificateCredential(tenantID, clientID, "testdata/certificate_formatB.pem", &options) if err != nil { t.Fatalf("Expected an empty error but received: %s", err.Error()) } @@ -138,7 +150,10 @@ func TestClientCertificateCredential_GetTokenCheckCertificateBlocks(t *testing.T srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - cred, err := NewClientCertificateCredential(tenantID, clientID, "testdata/certificate_formatA.pem", &ClientCertificateCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientCertificateCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientCertificateCredential(tenantID, clientID, "testdata/certificate_formatA.pem", &options) if err != nil { t.Fatalf("Expected an empty error but received: %s", err.Error()) } @@ -152,7 +167,10 @@ func TestClientCertificateCredential_GetTokenEmptyCertificate(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - _, err := NewClientCertificateCredential(tenantID, clientID, "testdata/certificate_empty.pem", &ClientCertificateCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientCertificateCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + _, err := NewClientCertificateCredential(tenantID, clientID, "testdata/certificate_empty.pem", &options) if err == nil { t.Fatalf("Expected an error but received nil") } @@ -162,7 +180,10 @@ func TestClientCertificateCredential_GetTokenNoPrivateKey(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - _, err := NewClientCertificateCredential(tenantID, clientID, "testdata/certificate_nokey.pem", &ClientCertificateCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientCertificateCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + _, err := NewClientCertificateCredential(tenantID, clientID, "testdata/certificate_nokey.pem", &options) if err == nil { t.Fatalf("Expected an error but received nil") } @@ -173,7 +194,10 @@ func TestBearerPolicy_ClientCertificateCredential(t *testing.T) { defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) srv.AppendResponse(mock.WithStatusCode(http.StatusOK)) - cred, err := NewClientCertificateCredential(tenantID, clientID, certificatePath, &ClientCertificateCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientCertificateCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientCertificateCredential(tenantID, clientID, certificatePath, &options) if err != nil { t.Fatalf("Did not expect an error but received: %v", err) } diff --git a/sdk/azidentity/client_secret_credential.go b/sdk/azidentity/client_secret_credential.go index 0c02b5953a37..4ec3497beceb 100644 --- a/sdk/azidentity/client_secret_credential.go +++ b/sdk/azidentity/client_secret_credential.go @@ -9,32 +9,37 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" ) -// ClientSecretCredential enables authentication to Azure Active Directory using a client secret that was generated for an App Registration. More information on how -// to configure a client secret can be found here: -// https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-access-web-apis#add-credentials-to-your-web-application -type ClientSecretCredential struct { - client *aadIdentityClient - tenantID string // Gets the Azure Active Directory tenant (directory) ID of the service principal - clientID string // Gets the client (application) ID of the service principal - clientSecret string // Gets the client secret that was generated for the App Registration used to authenticate the client. -} - // ClientSecretCredentialOptions configures the ClientSecretCredential with optional parameters. +// Call DefaultClientSecretCredentialOptions() to create an instance populated with default values. type ClientSecretCredentialOptions struct { - // The host of the Azure Active Directory authority. The default is https://login.microsoft.com + // The host of the Azure Active Directory authority. The default is AzurePublicCloud. + // Leave empty to allow overriding the value from the AZURE_AUTHORITY_HOST environment variable. AuthorityHost string // HTTPClient sets the transport for making HTTP requests // Leave this as nil to use the default HTTP transport HTTPClient azcore.Transport // Retry configures the built-in retry policy behavior - Retry *azcore.RetryOptions + Retry azcore.RetryOptions // Telemetry configures the built-in telemetry policy behavior Telemetry azcore.TelemetryOptions } // DefaultClientSecretCredentialOptions returns an instance of ClientSecretCredentialOptions initialized with default values. func DefaultClientSecretCredentialOptions() ClientSecretCredentialOptions { - return ClientSecretCredentialOptions{} + return ClientSecretCredentialOptions{ + Retry: azcore.DefaultRetryOptions(), + Telemetry: azcore.DefaultTelemetryOptions(), + } +} + +// ClientSecretCredential enables authentication to Azure Active Directory using a client secret that was generated for an App Registration. More information on how +// to configure a client secret can be found here: +// https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-access-web-apis#add-credentials-to-your-web-application +type ClientSecretCredential struct { + client *aadIdentityClient + tenantID string // Gets the Azure Active Directory tenant (directory) ID of the service principal + clientID string // Gets the client (application) ID of the service principal + clientSecret string // Gets the client secret that was generated for the App Registration used to authenticate the client. } // NewClientSecretCredential constructs a new ClientSecretCredential with the details needed to authenticate against Azure Active Directory with a client secret. @@ -44,7 +49,7 @@ func DefaultClientSecretCredentialOptions() ClientSecretCredentialOptions { // options: allow to configure the management of the requests sent to Azure Active Directory. func NewClientSecretCredential(tenantID string, clientID string, clientSecret string, options *ClientSecretCredentialOptions) (*ClientSecretCredential, error) { if !validTenantID(tenantID) { - return nil, &CredentialUnavailableError{CredentialType: "Client Secret Credential", Message: tenantIDValidationErr} + return nil, &CredentialUnavailableError{credentialType: "Client Secret Credential", message: tenantIDValidationErr} } if options == nil { temp := DefaultClientSecretCredentialOptions() diff --git a/sdk/azidentity/client_secret_credential_test.go b/sdk/azidentity/client_secret_credential_test.go index c1db0cbe2e6b..5b36b48e0401 100644 --- a/sdk/azidentity/client_secret_credential_test.go +++ b/sdk/azidentity/client_secret_credential_test.go @@ -82,7 +82,10 @@ func TestClientSecretCredential_GetTokenSuccess(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - cred, err := NewClientSecretCredential(tenantID, clientID, secret, &ClientSecretCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientSecretCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientSecretCredential(tenantID, clientID, secret, &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -96,7 +99,10 @@ func TestClientSecretCredential_GetTokenInvalidCredentials(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.SetResponse(mock.WithBody([]byte(accessTokenRespError)), mock.WithStatusCode(http.StatusUnauthorized)) - cred, err := NewClientSecretCredential(tenantID, clientID, wrongSecret, &ClientSecretCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientSecretCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientSecretCredential(tenantID, clientID, wrongSecret, &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -127,8 +133,8 @@ func TestClientSecretCredential_GetTokenInvalidCredentials(t *testing.T) { if len(respError.CorrelationID) == 0 { t.Fatalf("Did not receive a CorrelationID") } - if len(respError.URI) == 0 { - t.Fatalf("Did not receive an error URI") + if len(respError.URL) == 0 { + t.Fatalf("Did not receive an error URL") } if respError.Response == nil { t.Fatalf("Did not receive an error response") @@ -141,7 +147,10 @@ func TestClientSecretCredential_GetTokenUnexpectedJSON(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespMalformed))) - cred, err := NewClientSecretCredential(tenantID, clientID, secret, &ClientSecretCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultClientSecretCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewClientSecretCredential(tenantID, clientID, secret, &options) if err != nil { t.Fatalf("Failed to create the credential") } diff --git a/sdk/azidentity/default_azure_credential.go b/sdk/azidentity/default_azure_credential.go index 8ef6b1d6ed88..155911786bc9 100644 --- a/sdk/azidentity/default_azure_credential.go +++ b/sdk/azidentity/default_azure_credential.go @@ -68,8 +68,8 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Chained // if no credentials are added to the slice of TokenCredentials then return a CredentialUnavailableError if len(creds) == 0 { - err := &CredentialUnavailableError{CredentialType: "Default Azure Credential", Message: errMsg} - logCredentialError(err.CredentialType, err) + err := &CredentialUnavailableError{credentialType: "Default Azure Credential", message: errMsg} + logCredentialError(err.credentialType, err) return nil, err } azcore.Log().Write(LogCredential, "Azure Identity => NewDefaultAzureCredential() invoking NewChainedTokenCredential()") diff --git a/sdk/azidentity/default_azure_credential_test.go b/sdk/azidentity/default_azure_credential_test.go index 4c3952063cf9..635aa4df444d 100644 --- a/sdk/azidentity/default_azure_credential_test.go +++ b/sdk/azidentity/default_azure_credential_test.go @@ -87,7 +87,8 @@ func TestDefaultAzureCredential_NilOptions(t *testing.T) { if err != nil { t.Fatalf("Did not expect to receive an error in creating the credential") } - c := newManagedIdentityClient(nil) + options := DefaultManagedIdentityCredentialOptions() + c := newManagedIdentityClient(&options) // if the test is running in a MSI environment then the length of sources would be two since it will include environment credential and managed identity credential if msiType, err := c.getMSIType(); !(msiType == msiTypeUnavailable || msiType == msiTypeUnknown) { if len(cred.sources) != lengthOfChainFull { diff --git a/sdk/azidentity/device_code_credential.go b/sdk/azidentity/device_code_credential.go index 2aff28d7b9b3..efb98f3689a3 100644 --- a/sdk/azidentity/device_code_credential.go +++ b/sdk/azidentity/device_code_credential.go @@ -17,6 +17,7 @@ const ( ) // DeviceCodeCredentialOptions provide options that can configure DeviceCodeCredential instead of using the default values. +// Call DefaultDeviceCodeCredentialOptions() to create an instance populated with default values. type DeviceCodeCredentialOptions struct { // Gets the Azure Active Directory tenant (directory) ID of the service principal TenantID string @@ -24,38 +25,18 @@ type DeviceCodeCredentialOptions struct { ClientID string // The callback function used to send the login message back to the user UserPrompt func(DeviceCodeMessage) - // The host of the Azure Active Directory authority. The default is https://login.microsoft.com + // The host of the Azure Active Directory authority. The default is AzurePublicCloud. + // Leave empty to allow overriding the value from the AZURE_AUTHORITY_HOST environment variable. AuthorityHost string // HTTPClient sets the transport for making HTTP requests // Leave this as nil to use the default HTTP transport HTTPClient azcore.Transport // Retry configures the built-in retry policy behavior - Retry *azcore.RetryOptions + Retry azcore.RetryOptions // Telemetry configures the built-in telemetry policy behavior Telemetry azcore.TelemetryOptions } -// DeviceCodeCredential authenticates a user using the device code flow, and provides access tokens for that user account. -// For more information on the device code authentication flow see: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code. -type DeviceCodeCredential struct { - client *aadIdentityClient - tenantID string // Gets the Azure Active Directory tenant (directory) ID of the service principal - clientID string // Gets the client (application) ID of the service principal - userPrompt func(DeviceCodeMessage) // Sends the user a message with a verification URL and device code to sign in to the login server - refreshToken string // Gets the refresh token sent from the service and will be used to retreive new access tokens after the initial request for a token. Thread safety for updates is handled in the AuthenticationPolicy since only one goroutine will be updating at a time -} - -// DeviceCodeMessage is used to store device code related information to help the user login and allow the device code flow to continue -// to request a token to authenticate a user. -type DeviceCodeMessage struct { - // User code returned by the service. - UserCode string `json:"user_code"` - // Verification URL where the user must navigate to authenticate using the device code and credentials. - VerificationURL string `json:"verification_uri"` - // User friendly text response that can be used for display purposes. - Message string `json:"message"` -} - // DefaultDeviceCodeCredentialOptions provides the default settings for DeviceCodeCredential. // It will set the following default values: // TenantID set to "organizations". @@ -68,9 +49,32 @@ func DefaultDeviceCodeCredentialOptions() DeviceCodeCredentialOptions { UserPrompt: func(dc DeviceCodeMessage) { fmt.Println(dc.Message) }, + Retry: azcore.DefaultRetryOptions(), + Telemetry: azcore.DefaultTelemetryOptions(), } } +// DeviceCodeMessage is used to store device code related information to help the user login and allow the device code flow to continue +// to request a token to authenticate a user. +type DeviceCodeMessage struct { + // User code returned by the service. + UserCode string `json:"user_code"` + // Verification URL where the user must navigate to authenticate using the device code and credentials. + VerificationURL string `json:"verification_uri"` + // User friendly text response that can be used for display purposes. + Message string `json:"message"` +} + +// DeviceCodeCredential authenticates a user using the device code flow, and provides access tokens for that user account. +// For more information on the device code authentication flow see: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code. +type DeviceCodeCredential struct { + client *aadIdentityClient + tenantID string // Gets the Azure Active Directory tenant (directory) ID of the service principal + clientID string // Gets the client (application) ID of the service principal + userPrompt func(DeviceCodeMessage) // Sends the user a message with a verification URL and device code to sign in to the login server + refreshToken string // Gets the refresh token sent from the service and will be used to retreive new access tokens after the initial request for a token. Thread safety for updates is handled in the AuthenticationPolicy since only one goroutine will be updating at a time +} + // NewDeviceCodeCredential constructs a new DeviceCodeCredential used to authenticate against Azure Active Directory with a device code. // options: Options used to configure the management of the requests sent to Azure Active Directory, please see DeviceCodeCredentialOptions for a description of each field. func NewDeviceCodeCredential(options *DeviceCodeCredentialOptions) (*DeviceCodeCredential, error) { @@ -79,7 +83,7 @@ func NewDeviceCodeCredential(options *DeviceCodeCredentialOptions) (*DeviceCodeC options = &temp } if !validTenantID(options.TenantID) { - return nil, &CredentialUnavailableError{CredentialType: "Device Code Credential", Message: tenantIDValidationErr} + return nil, &CredentialUnavailableError{credentialType: "Device Code Credential", message: tenantIDValidationErr} } authorityHost, err := setAuthorityHost(options.AuthorityHost) if err != nil { diff --git a/sdk/azidentity/device_code_credential_test.go b/sdk/azidentity/device_code_credential_test.go index 4aa59639c8fa..857ecd286dca 100644 --- a/sdk/azidentity/device_code_credential_test.go +++ b/sdk/azidentity/device_code_credential_test.go @@ -24,7 +24,9 @@ const ( ) func TestDeviceCodeCredential_InvalidTenantID(t *testing.T) { - cred, err := NewDeviceCodeCredential(&DeviceCodeCredentialOptions{TenantID: badTenantID}) + options := DefaultDeviceCodeCredentialOptions() + options.TenantID = badTenantID + cred, err := NewDeviceCodeCredential(&options) if err == nil { t.Fatal("Expected an error but received none") } @@ -125,7 +127,10 @@ func TestDeviceCodeCredential_CreateAuthRequestCustomClientID(t *testing.T) { } func TestDeviceCodeCredential_RequestNewDeviceCodeCustomTenantIDClientID(t *testing.T) { - cred, err := NewDeviceCodeCredential(&DeviceCodeCredentialOptions{TenantID: tenantID, ClientID: clientID}) + options := DefaultDeviceCodeCredentialOptions() + options.ClientID = clientID + options.TenantID = tenantID + cred, err := NewDeviceCodeCredential(&options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -188,7 +193,12 @@ func TestDeviceCodeCredential_GetTokenInvalidCredentials(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.SetResponse(mock.WithStatusCode(http.StatusUnauthorized)) - cred, err := NewDeviceCodeCredential(&DeviceCodeCredentialOptions{TenantID: tenantID, ClientID: clientID, HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultDeviceCodeCredentialOptions() + options.ClientID = clientID + options.TenantID = tenantID + options.HTTPClient = srv + options.AuthorityHost = srv.URL() + cred, err := NewDeviceCodeCredential(&options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -205,8 +215,13 @@ func TestDeviceCodeCredential_GetTokenAuthorizationPending(t *testing.T) { srv.AppendResponse(mock.WithBody([]byte(authorizationPendingResponse)), mock.WithStatusCode(http.StatusUnauthorized)) srv.AppendResponse(mock.WithBody([]byte(authorizationPendingResponse)), mock.WithStatusCode(http.StatusUnauthorized)) srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - handler := func(dc DeviceCodeMessage) {} - cred, err := NewDeviceCodeCredential(&DeviceCodeCredentialOptions{TenantID: tenantID, ClientID: clientID, UserPrompt: handler, HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultDeviceCodeCredentialOptions() + options.ClientID = clientID + options.TenantID = tenantID + options.HTTPClient = srv + options.AuthorityHost = srv.URL() + options.UserPrompt = func(DeviceCodeMessage) {} + cred, err := NewDeviceCodeCredential(&options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -222,8 +237,13 @@ func TestDeviceCodeCredential_GetTokenExpiredToken(t *testing.T) { srv.AppendResponse(mock.WithBody([]byte(deviceCodeResponse))) srv.AppendResponse(mock.WithBody([]byte(authorizationPendingResponse)), mock.WithStatusCode(http.StatusUnauthorized)) srv.AppendResponse(mock.WithBody([]byte(expiredTokenResponse)), mock.WithStatusCode(http.StatusUnauthorized)) - handler := func(dc DeviceCodeMessage) {} - cred, err := NewDeviceCodeCredential(&DeviceCodeCredentialOptions{TenantID: tenantID, ClientID: clientID, UserPrompt: handler, HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultDeviceCodeCredentialOptions() + options.ClientID = clientID + options.TenantID = tenantID + options.HTTPClient = srv + options.AuthorityHost = srv.URL() + options.UserPrompt = func(DeviceCodeMessage) {} + cred, err := NewDeviceCodeCredential(&options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -237,7 +257,12 @@ func TestDeviceCodeCredential_GetTokenWithRefreshTokenFailure(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespError)), mock.WithStatusCode(http.StatusUnauthorized)) - cred, err := NewDeviceCodeCredential(&DeviceCodeCredentialOptions{TenantID: tenantID, ClientID: clientID, HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultDeviceCodeCredentialOptions() + options.ClientID = clientID + options.TenantID = tenantID + options.HTTPClient = srv + options.AuthorityHost = srv.URL() + cred, err := NewDeviceCodeCredential(&options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -256,8 +281,13 @@ func TestDeviceCodeCredential_GetTokenWithRefreshTokenSuccess(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - handler := func(DeviceCodeMessage) {} - cred, err := NewDeviceCodeCredential(&DeviceCodeCredentialOptions{TenantID: tenantID, ClientID: clientID, UserPrompt: handler, HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultDeviceCodeCredentialOptions() + options.ClientID = clientID + options.TenantID = tenantID + options.HTTPClient = srv + options.AuthorityHost = srv.URL() + options.UserPrompt = func(DeviceCodeMessage) {} + cred, err := NewDeviceCodeCredential(&options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -277,8 +307,13 @@ func TestBearerPolicy_DeviceCodeCredential(t *testing.T) { srv.AppendResponse(mock.WithBody([]byte(deviceCodeResponse))) srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) srv.AppendResponse(mock.WithStatusCode(http.StatusOK)) - handler := func(DeviceCodeMessage) {} - cred, err := NewDeviceCodeCredential(&DeviceCodeCredentialOptions{TenantID: tenantID, ClientID: clientID, UserPrompt: handler, HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultDeviceCodeCredentialOptions() + options.ClientID = clientID + options.TenantID = tenantID + options.HTTPClient = srv + options.AuthorityHost = srv.URL() + options.UserPrompt = func(DeviceCodeMessage) {} + cred, err := NewDeviceCodeCredential(&options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } diff --git a/sdk/azidentity/environment_credential.go b/sdk/azidentity/environment_credential.go index 810766ad9a2c..47e4715911b9 100644 --- a/sdk/azidentity/environment_credential.go +++ b/sdk/azidentity/environment_credential.go @@ -10,36 +10,41 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" ) -// EnvironmentCredential enables authentication to Azure Active Directory using either ClientSecretCredential, ClientCertificateCredential or UsernamePasswordCredential. -// This credential type will check for the following environment variables in the same order as listed: -// - AZURE_TENANT_ID -// - AZURE_CLIENT_ID -// - AZURE_CLIENT_SECRET -// - AZURE_CLIENT_CERTIFICATE_PATH -// - AZURE_USERNAME -// - AZURE_PASSWORD -// NOTE: EnvironmentCredential will stop checking environment variables as soon as it finds enough environment variables to -// create a credential type. -type EnvironmentCredential struct { - cred azcore.TokenCredential -} - // EnvironmentCredentialOptions configures the EnvironmentCredential with optional parameters. +// Call DefaultEnvironmentCredentialOptions() to create an instance populated with default values. type EnvironmentCredentialOptions struct { - // The host of the Azure Active Directory authority. The default is https://login.microsoft.com + // The host of the Azure Active Directory authority. The default is AzurePublicCloud. + // Leave empty to allow overriding the value from the AZURE_AUTHORITY_HOST environment variable. AuthorityHost string // HTTPClient sets the transport for making HTTP requests // Leave this as nil to use the default HTTP transport HTTPClient azcore.Transport // Retry configures the built-in retry policy behavior - Retry *azcore.RetryOptions + Retry azcore.RetryOptions // Telemetry configures the built-in telemetry policy behavior Telemetry azcore.TelemetryOptions } // DefaultEnvironmentCredentialOptions returns an instance of EnvironmentCredentialOptions initialized with default values. func DefaultEnvironmentCredentialOptions() EnvironmentCredentialOptions { - return EnvironmentCredentialOptions{} + return EnvironmentCredentialOptions{ + Retry: azcore.DefaultRetryOptions(), + Telemetry: azcore.DefaultTelemetryOptions(), + } +} + +// EnvironmentCredential enables authentication to Azure Active Directory using either ClientSecretCredential, ClientCertificateCredential or UsernamePasswordCredential. +// This credential type will check for the following environment variables in the same order as listed: +// - AZURE_TENANT_ID +// - AZURE_CLIENT_ID +// - AZURE_CLIENT_SECRET +// - AZURE_CLIENT_CERTIFICATE_PATH +// - AZURE_USERNAME +// - AZURE_PASSWORD +// NOTE: EnvironmentCredential will stop checking environment variables as soon as it finds enough environment variables to +// create a credential type. +type EnvironmentCredential struct { + cred azcore.TokenCredential } // NewEnvironmentCredential creates an instance that implements the azcore.TokenCredential interface and reads credential details from environment variables. @@ -52,14 +57,14 @@ func NewEnvironmentCredential(options *EnvironmentCredentialOptions) (*Environme } tenantID := os.Getenv("AZURE_TENANT_ID") if tenantID == "" { - err := &CredentialUnavailableError{CredentialType: "Environment Credential", Message: "Missing environment variable AZURE_TENANT_ID"} - logCredentialError(err.CredentialType, err) + err := &CredentialUnavailableError{credentialType: "Environment Credential", message: "Missing environment variable AZURE_TENANT_ID"} + logCredentialError(err.credentialType, err) return nil, err } clientID := os.Getenv("AZURE_CLIENT_ID") if clientID == "" { - err := &CredentialUnavailableError{CredentialType: "Environment Credential", Message: "Missing environment variable AZURE_CLIENT_ID"} - logCredentialError(err.CredentialType, err) + err := &CredentialUnavailableError{credentialType: "Environment Credential", message: "Missing environment variable AZURE_CLIENT_ID"} + logCredentialError(err.credentialType, err) return nil, err } if clientSecret := os.Getenv("AZURE_CLIENT_SECRET"); clientSecret != "" { @@ -88,8 +93,8 @@ func NewEnvironmentCredential(options *EnvironmentCredentialOptions) (*Environme return &EnvironmentCredential{cred: cred}, nil } } - err := &CredentialUnavailableError{CredentialType: "Environment Credential", Message: "Missing environment variable AZURE_CLIENT_SECRET or AZURE_CLIENT_CERTIFICATE_PATH or AZURE_USERNAME and AZURE_PASSWORD"} - logCredentialError(err.CredentialType, err) + err := &CredentialUnavailableError{credentialType: "Environment Credential", message: "Missing environment variable AZURE_CLIENT_SECRET or AZURE_CLIENT_CERTIFICATE_PATH or AZURE_USERNAME and AZURE_PASSWORD"} + logCredentialError(err.credentialType, err) return nil, err } diff --git a/sdk/azidentity/go.mod b/sdk/azidentity/go.mod index c157459e6581..f3548090abe8 100644 --- a/sdk/azidentity/go.mod +++ b/sdk/azidentity/go.mod @@ -3,10 +3,9 @@ module github.com/Azure/azure-sdk-for-go/sdk/azidentity go 1.14 require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.0 + github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.1 github.com/Azure/azure-sdk-for-go/sdk/internal v0.5.0 - github.com/Azure/azure-sdk-for-go/sdk/to v0.1.2 github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 - golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee - golang.org/x/net v0.0.0-20201010224723-4f7140c49acb + golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 + golang.org/x/net v0.0.0-20201110031124-69a78807bb2b ) diff --git a/sdk/azidentity/go.sum b/sdk/azidentity/go.sum index 99394639f880..66f64be138be 100644 --- a/sdk/azidentity/go.sum +++ b/sdk/azidentity/go.sum @@ -1,18 +1,18 @@ -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.0 h1:JN0lbs2OdikXprbmj4c2xq6/yyQER/xCNQGast73hTM= -github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.0/go.mod h1:pElNP+u99BvCZD+0jOlhI9OC/NB2IDTOTGZOZH0Qhq8= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.1 h1:YLBIWApuKB/RS0dK/mk/2g6x2+kjI9djMKqViZDTD88= +github.com/Azure/azure-sdk-for-go/sdk/azcore v0.13.1/go.mod h1:pElNP+u99BvCZD+0jOlhI9OC/NB2IDTOTGZOZH0Qhq8= github.com/Azure/azure-sdk-for-go/sdk/internal v0.5.0 h1:HG1ggl8L3ZkV/Ydanf7lKr5kkhhPGCpWdnr1J6v7cO4= github.com/Azure/azure-sdk-for-go/sdk/internal v0.5.0/go.mod h1:k4KbFSunV/+0hOHL1vyFaPsiYQ1Vmvy1TBpmtvCDLZM= -github.com/Azure/azure-sdk-for-go/sdk/to v0.1.2 h1:TZTVOb/ce7nCmOZYga9+ELtPPVVFG2Px4s/w5OycYS0= -github.com/Azure/azure-sdk-for-go/sdk/to v0.1.2/go.mod h1:UL/d4lvWAzSJUuX+19uKdN0ktyjoOyQhgY+HWNgtIYI= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4 h1:49lOXmGaUpV9Fz3gd7TFZY106KVlPVa5jcYD1gaQf98= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI= -golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= +golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb h1:mUVeFHoDKis5nxCAzoAi7E8Ghb86EXh/RK6wtvJIqRY= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/sdk/azidentity/interactive_browser_credential.go b/sdk/azidentity/interactive_browser_credential.go index 917443a8b035..951e67f7116c 100644 --- a/sdk/azidentity/interactive_browser_credential.go +++ b/sdk/azidentity/interactive_browser_credential.go @@ -16,26 +16,38 @@ import ( // InteractiveBrowserCredentialOptions can be used when providing additional credential information, such as a client secret. // Also use these options to modify the default pipeline behavior through the TokenCredentialOptions. +// Call DefaultInteractiveBrowserCredentialOptions() to create an instance populated with default values. type InteractiveBrowserCredentialOptions struct { // The Azure Active Directory tenant (directory) ID of the service principal. TenantID string // The client (application) ID of the service principal. ClientID string // The client secret that was generated for the App Registration used to authenticate the client. Only applies for web apps. - ClientSecret *string - // The redirect URI used to request the authorization code. Must be the same URI that is configured for the App Registration. - RedirectURI *string - // The host of the Azure Active Directory authority. The default is https://login.microsoft.com + ClientSecret string + // The redirect URL used to request the authorization code. Must be the same URL that is configured for the App Registration. + RedirectURL string + // The host of the Azure Active Directory authority. The default is AzurePublicCloud. + // Leave empty to allow overriding the value from the AZURE_AUTHORITY_HOST environment variable. AuthorityHost string // HTTPClient sets the transport for making HTTP requests // Leave this as nil to use the default HTTP transport HTTPClient azcore.Transport // Retry configures the built-in retry policy behavior - Retry *azcore.RetryOptions + Retry azcore.RetryOptions // Telemetry configures the built-in telemetry policy behavior Telemetry azcore.TelemetryOptions } +// DefaultInteractiveBrowserCredentialOptions returns an instance of InteractiveBrowserCredentialOptions initialized with default values. +func DefaultInteractiveBrowserCredentialOptions() InteractiveBrowserCredentialOptions { + return InteractiveBrowserCredentialOptions{ + TenantID: organizationsTenantID, + ClientID: developerSignOnClientID, + Retry: azcore.DefaultRetryOptions(), + Telemetry: azcore.DefaultTelemetryOptions(), + } +} + // InteractiveBrowserCredential enables authentication to Azure Active Directory using an interactive browser to log in. type InteractiveBrowserCredential struct { client *aadIdentityClient @@ -43,16 +55,6 @@ type InteractiveBrowserCredential struct { options InteractiveBrowserCredentialOptions } -// DefaultInteractiveBrowserCredentialOptions returns an instance of InteractiveBrowserCredentialOptions initialized with default values. -func DefaultInteractiveBrowserCredentialOptions() InteractiveBrowserCredentialOptions { - return InteractiveBrowserCredentialOptions{ - TenantID: organizationsTenantID, - ClientID: developerSignOnClientID, - ClientSecret: nil, - RedirectURI: nil, - } -} - // NewInteractiveBrowserCredential constructs a new InteractiveBrowserCredential with the details needed to authenticate against Azure Active Directory through an interactive browser window. // options: allow to configure the management of the requests sent to Azure Active Directory, pass in nil for default behavior. func NewInteractiveBrowserCredential(options *InteractiveBrowserCredentialOptions) (*InteractiveBrowserCredential, error) { @@ -61,7 +63,7 @@ func NewInteractiveBrowserCredential(options *InteractiveBrowserCredentialOption options = &temp } if !validTenantID(options.TenantID) { - return nil, &CredentialUnavailableError{CredentialType: "Interactive Browser Credential", Message: tenantIDValidationErr} + return nil, &CredentialUnavailableError{credentialType: "Interactive Browser Credential", message: tenantIDValidationErr} } authorityHost, err := setAuthorityHost(options.AuthorityHost) if err != nil { @@ -79,7 +81,7 @@ func NewInteractiveBrowserCredential(options *InteractiveBrowserCredentialOption // opts: TokenRequestOptions contains the list of scopes for which the token will have access. // Returns an AccessToken which can be used to authenticate service client calls. func (c *InteractiveBrowserCredential) GetToken(ctx context.Context, opts azcore.TokenRequestOptions) (*azcore.AccessToken, error) { - tk, err := c.client.authenticateInteractiveBrowser(ctx, c.options.TenantID, c.options.ClientID, c.options.ClientSecret, c.options.RedirectURI, opts.Scopes) + tk, err := c.client.authenticateInteractiveBrowser(ctx, c.options.TenantID, c.options.ClientID, c.options.ClientSecret, c.options.RedirectURL, opts.Scopes) if err != nil { addGetTokenFailureLogs("Interactive Browser Credential", err, true) return nil, err @@ -97,13 +99,13 @@ func (c *InteractiveBrowserCredential) AuthenticationPolicy(options azcore.Authe var _ azcore.TokenCredential = (*InteractiveBrowserCredential)(nil) // authCodeReceiver is used to allow for testing without opening an interactive browser window. Allows mocking a response authorization code and redirect URI. -var authCodeReceiver = func(authorityHost string, tenantID string, clientID string, redirectURI *string, scopes []string) (*interactiveConfig, error) { +var authCodeReceiver = func(authorityHost string, tenantID string, clientID string, redirectURI string, scopes []string) (*interactiveConfig, error) { return interactiveBrowserLogin(authorityHost, tenantID, clientID, redirectURI, scopes) } // interactiveBrowserLogin opens an interactive browser with the specified tenant and client IDs provided then returns the authorization code // received or an error. -func interactiveBrowserLogin(authorityHost string, tenantID string, clientID string, redirectURL *string, scopes []string) (*interactiveConfig, error) { +func interactiveBrowserLogin(authorityHost string, tenantID string, clientID string, redirectURL string, scopes []string) (*interactiveConfig, error) { const authURLFormat = "%s/%s/oauth2/v2.0/authorize?response_type=code&response_mode=query&client_id=%s&redirect_uri=%s&state=%s&scope=%s&prompt=select_account" state := func() string { rand.Seed(time.Now().Unix()) @@ -117,12 +119,11 @@ func interactiveBrowserLogin(authorityHost string, tenantID string, clientID str }() // start local redirect server so login can call us back rs := newServer() - if redirectURL == nil { - temp := rs.Start(state) - redirectURL = &temp + if redirectURL == "" { + redirectURL = rs.Start(state) } defer rs.Stop() - authURL := fmt.Sprintf(authURLFormat, authorityHost, tenantID, clientID, *redirectURL, state, strings.Join(scopes, " ")) + authURL := fmt.Sprintf(authURLFormat, authorityHost, tenantID, clientID, redirectURL, state, strings.Join(scopes, " ")) // open browser window so user can select credentials err := browser.OpenURL(authURL) if err != nil { @@ -137,6 +138,6 @@ func interactiveBrowserLogin(authorityHost string, tenantID string, clientID str } return &interactiveConfig{ authCode: authCode, - redirectURI: *redirectURL, + redirectURI: redirectURL, }, nil } diff --git a/sdk/azidentity/interactive_browser_credential_test.go b/sdk/azidentity/interactive_browser_credential_test.go index bb9cf3450e27..f3163915ca96 100644 --- a/sdk/azidentity/interactive_browser_credential_test.go +++ b/sdk/azidentity/interactive_browser_credential_test.go @@ -11,12 +11,13 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/internal/mock" - "github.com/Azure/azure-sdk-for-go/sdk/to" "golang.org/x/net/http2" ) func TestInteractiveBrowserCredential_InvalidTenantID(t *testing.T) { - cred, err := NewInteractiveBrowserCredential(&InteractiveBrowserCredentialOptions{TenantID: badTenantID}) + options := DefaultInteractiveBrowserCredentialOptions() + options.TenantID = badTenantID + cred, err := NewInteractiveBrowserCredential(&options) if err == nil { t.Fatal("Expected an error but received none") } @@ -62,7 +63,7 @@ func TestInteractiveBrowserCredential_GetTokenSuccess(t *testing.T) { if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } - authCodeReceiver = func(authorityHost string, tenantID string, clientID string, redirectURI *string, scopes []string) (*interactiveConfig, error) { + authCodeReceiver = func(authorityHost string, tenantID string, clientID string, redirectURI string, scopes []string) (*interactiveConfig, error) { return &interactiveConfig{ authCode: "12345", redirectURI: srv.URL(), @@ -88,14 +89,14 @@ func TestInteractiveBrowserCredential_GetTokenInvalidCredentials(t *testing.T) { client := &http.Client{Transport: tr} srv.SetResponse(mock.WithBody([]byte(accessTokenRespError)), mock.WithStatusCode(http.StatusUnauthorized)) options := DefaultInteractiveBrowserCredentialOptions() - options.ClientSecret = to.StringPtr(wrongSecret) + options.ClientSecret = wrongSecret options.AuthorityHost = srv.URL() options.HTTPClient = client cred, err := NewInteractiveBrowserCredential(&options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } - authCodeReceiver = func(authorityHost string, tenantID string, clientID string, redirectURI *string, scopes []string) (*interactiveConfig, error) { + authCodeReceiver = func(authorityHost string, tenantID string, clientID string, redirectURI string, scopes []string) (*interactiveConfig, error) { return &interactiveConfig{ authCode: "12345", redirectURI: srv.URL(), @@ -128,8 +129,8 @@ func TestInteractiveBrowserCredential_GetTokenInvalidCredentials(t *testing.T) { if len(respError.CorrelationID) == 0 { t.Fatalf("Did not receive a CorrelationID") } - if len(respError.URI) == 0 { - t.Fatalf("Did not receive an error URI") + if len(respError.URL) == 0 { + t.Fatalf("Did not receive an error URL") } if respError.Response == nil { t.Fatalf("Did not receive an error response") diff --git a/sdk/azidentity/managed_identity_client.go b/sdk/azidentity/managed_identity_client.go index 6c2e89cb46f8..0438b77fd9c7 100644 --- a/sdk/azidentity/managed_identity_client.go +++ b/sdk/azidentity/managed_identity_client.go @@ -65,21 +65,12 @@ func (n *wrappedNumber) UnmarshalJSON(b []byte) error { return json.Unmarshal(b, (*json.Number)(n)) } -var ( - defaultMSIOpts = newDefaultManagedIdentityOptions() -) - -func newDefaultManagedIdentityOptions() *ManagedIdentityCredentialOptions { - return &ManagedIdentityCredentialOptions{} -} - // 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() - options = options.setDefaultValues() return &managedIdentityClient{ 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 @@ -164,7 +155,7 @@ func (c *managedIdentityClient) createAuthRequest(ctx context.Context, clientID default: errorMsg = "unknown" } - return nil, &CredentialUnavailableError{CredentialType: "Managed Identity Credential", Message: "Make sure you are running in a valid Managed Identity Environment. Status: " + errorMsg} + return nil, &CredentialUnavailableError{credentialType: "Managed Identity Credential", message: "Make sure you are running in a valid Managed Identity Environment. Status: " + errorMsg} } } @@ -283,7 +274,7 @@ func (c *managedIdentityClient) getMSIType() (msiType, error) { c.msiType = msiTypeAzureArc } else { // if ONLY the env var IDENTITY_ENDPOINT is set the msiType is Azure Functions c.msiType = msiTypeUnavailable - return c.msiType, &CredentialUnavailableError{CredentialType: "Managed Identity Credential", Message: "This Managed Identity Environment is not supported yet"} + return c.msiType, &CredentialUnavailableError{credentialType: "Managed Identity Credential", message: "This Managed Identity Environment is not supported yet"} } } else if endpointEnvVar := os.Getenv(msiEndpoint); endpointEnvVar != "" { // if the env var MSI_ENDPOINT is set c.endpoint = endpointEnvVar @@ -297,7 +288,7 @@ func (c *managedIdentityClient) getMSIType() (msiType, error) { 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, &CredentialUnavailableError{CredentialType: "Managed Identity Credential", Message: "Make sure you are running in a valid Managed Identity Environment"} + return c.msiType, &CredentialUnavailableError{credentialType: "Managed Identity Credential", message: "Make sure you are running in a valid Managed Identity Environment"} } } return c.msiType, nil diff --git a/sdk/azidentity/managed_identity_client_test.go b/sdk/azidentity/managed_identity_client_test.go index f38461c495ad..6d6f615c66da 100644 --- a/sdk/azidentity/managed_identity_client_test.go +++ b/sdk/azidentity/managed_identity_client_test.go @@ -14,7 +14,3 @@ func TestIMDSEndpointParse(t *testing.T) { t.Fatalf("Failed to parse the IMDS endpoint: %v", err) } } - -// func TestNewDefaultMSIPipeline(t *testing.T) { -// p := newDefaultMSIPipeline(ManagedIdentityCredentialOptions{}) -// } diff --git a/sdk/azidentity/managed_identity_credential.go b/sdk/azidentity/managed_identity_credential.go index 6e02094151cf..5520782f4d6c 100644 --- a/sdk/azidentity/managed_identity_credential.go +++ b/sdk/azidentity/managed_identity_credential.go @@ -12,6 +12,7 @@ import ( ) // ManagedIdentityCredentialOptions contains parameters that can be used to configure the pipeline used with Managed Identity Credential. +// Call DefaultManagedIdentityCredentialOptions() to create an instance populated with default values. type ManagedIdentityCredentialOptions struct { // HTTPClient sets the transport for making HTTP requests. // Leave this as nil to use the default HTTP transport. @@ -21,11 +22,11 @@ type ManagedIdentityCredentialOptions struct { Telemetry azcore.TelemetryOptions } -func (m *ManagedIdentityCredentialOptions) setDefaultValues() *ManagedIdentityCredentialOptions { - if m == nil { - m = defaultMSIOpts +// DefaultManagedIdentityCredentialOptions returns an instance of ManagedIdentityCredentialOptions initialized with default values. +func DefaultManagedIdentityCredentialOptions() ManagedIdentityCredentialOptions { + return ManagedIdentityCredentialOptions{ + Telemetry: azcore.DefaultTelemetryOptions(), } - return m } // ManagedIdentityCredential attempts authentication using a managed identity that has been assigned to the deployment environment. This authentication type works in several @@ -43,12 +44,16 @@ type ManagedIdentityCredential struct { // https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/overview#how-a-user-assigned-managed-identity-works-with-an-azure-vm func NewManagedIdentityCredential(clientID string, options *ManagedIdentityCredentialOptions) (*ManagedIdentityCredential, error) { // Create a new Managed Identity Client with default options + if options == nil { + def := DefaultManagedIdentityCredentialOptions() + options = &def + } client := newManagedIdentityClient(options) msiType, err := client.getMSIType() // If there is an error that means that the code is not running in a Managed Identity environment if err != nil { - credErr := &CredentialUnavailableError{CredentialType: "Managed Identity Credential", Message: "Please make sure you are running in a managed identity environment, such as a VM, Azure Functions, Cloud Shell, etc..."} - logCredentialError(credErr.CredentialType, credErr) + credErr := &CredentialUnavailableError{credentialType: "Managed Identity Credential", message: "Please make sure you are running in a managed identity environment, such as a VM, Azure Functions, Cloud Shell, etc..."} + logCredentialError(credErr.credentialType, credErr) return nil, credErr } // Assign the msiType discovered onto the client diff --git a/sdk/azidentity/managed_identity_credential_test.go b/sdk/azidentity/managed_identity_credential_test.go index 599a78fb6206..0472e8bf2775 100644 --- a/sdk/azidentity/managed_identity_credential_test.go +++ b/sdk/azidentity/managed_identity_credential_test.go @@ -61,7 +61,9 @@ func TestManagedIdentityCredential_GetTokenInCloudShellMock(t *testing.T) { srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) _ = os.Setenv("MSI_ENDPOINT", srv.URL()) defer clearEnvVars("MSI_ENDPOINT") - msiCred, err := NewManagedIdentityCredential(clientID, &ManagedIdentityCredentialOptions{HTTPClient: srv}) + options := DefaultManagedIdentityCredentialOptions() + options.HTTPClient = srv + msiCred, err := NewManagedIdentityCredential(clientID, &options) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -78,7 +80,9 @@ func TestManagedIdentityCredential_GetTokenInCloudShellMockFail(t *testing.T) { srv.AppendResponse(mock.WithStatusCode(http.StatusUnauthorized)) _ = os.Setenv("MSI_ENDPOINT", srv.URL()) defer clearEnvVars("MSI_ENDPOINT") - msiCred, err := NewManagedIdentityCredential("", &ManagedIdentityCredentialOptions{HTTPClient: srv}) + options := DefaultManagedIdentityCredentialOptions() + options.HTTPClient = srv + msiCred, err := NewManagedIdentityCredential("", &options) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -96,7 +100,9 @@ func TestManagedIdentityCredential_GetTokenInAppServiceV20170901Mock(t *testing. _ = os.Setenv("MSI_ENDPOINT", srv.URL()) _ = os.Setenv("MSI_SECRET", "secret") defer clearEnvVars("MSI_ENDPOINT", "MSI_SECRET") - msiCred, err := NewManagedIdentityCredential(clientID, &ManagedIdentityCredentialOptions{HTTPClient: srv}) + options := DefaultManagedIdentityCredentialOptions() + options.HTTPClient = srv + msiCred, err := NewManagedIdentityCredential(clientID, &options) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -120,7 +126,9 @@ func TestManagedIdentityCredential_GetTokenInAppServiceV20190801Mock(t *testing. _ = os.Setenv("IDENTITY_ENDPOINT", srv.URL()) _ = os.Setenv("IDENTITY_HEADER", "header") defer clearEnvVars("IDENTITY_ENDPOINT", "IDENTITY_HEADER") - msiCred, err := NewManagedIdentityCredential("", &ManagedIdentityCredentialOptions{HTTPClient: srv}) + options := DefaultManagedIdentityCredentialOptions() + options.HTTPClient = srv + msiCred, err := NewManagedIdentityCredential("", &options) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -204,7 +212,9 @@ func TestManagedIdentityCredential_CreateAccessTokenExpiresOnInt(t *testing.T) { _ = os.Setenv("MSI_ENDPOINT", srv.URL()) _ = os.Setenv("MSI_SECRET", "secret") defer clearEnvVars("MSI_ENDPOINT", "MSI_SECRET") - msiCred, err := NewManagedIdentityCredential(clientID, &ManagedIdentityCredentialOptions{HTTPClient: srv}) + options := DefaultManagedIdentityCredentialOptions() + options.HTTPClient = srv + msiCred, err := NewManagedIdentityCredential(clientID, &options) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -222,7 +232,9 @@ func TestManagedIdentityCredential_GetTokenInAppServiceMockFail(t *testing.T) { _ = os.Setenv("MSI_ENDPOINT", srv.URL()) _ = os.Setenv("MSI_SECRET", "secret") defer clearEnvVars("MSI_ENDPOINT", "MSI_SECRET") - msiCred, err := NewManagedIdentityCredential("", &ManagedIdentityCredentialOptions{HTTPClient: srv}) + options := DefaultManagedIdentityCredentialOptions() + options.HTTPClient = srv + msiCred, err := NewManagedIdentityCredential("", &options) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -243,7 +255,9 @@ func TestManagedIdentityCredential_GetTokenInAppServiceMockFail(t *testing.T) { // srv, close := mock.NewServer() // defer close() // srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) -// msiCred := NewManagedIdentityCredential("", &ManagedIdentityCredentialOptions{HTTPClient: srv}) +// options := DefaultManagedIdentityCredentialOptions() +// options.HTTPClient = srv +// msiCred := NewManagedIdentityCredential("", &options) // _, err = msiCred.GetToken(context.Background(), azcore.TokenRequestOptions{Scopes: []string{msiScope}}) // if err == nil { // t.Fatalf("Cannot run IMDS test in this environment") @@ -266,7 +280,9 @@ func TestManagedIdentityCredential_NewManagedIdentityCredentialFail(t *testing.T srv.AppendResponse(mock.WithStatusCode(http.StatusUnauthorized)) _ = os.Setenv("MSI_ENDPOINT", "https://t .com") defer clearEnvVars("MSI_ENDPOINT") - cred, err := NewManagedIdentityCredential("", &ManagedIdentityCredentialOptions{HTTPClient: srv}) + options := DefaultManagedIdentityCredentialOptions() + options.HTTPClient = srv + cred, err := NewManagedIdentityCredential("", &options) if err != nil { t.Fatal(err) } @@ -283,7 +299,9 @@ func TestBearerPolicy_ManagedIdentityCredential(t *testing.T) { srv.AppendResponse(mock.WithStatusCode(http.StatusOK)) _ = os.Setenv("MSI_ENDPOINT", srv.URL()) defer clearEnvVars("MSI_ENDPOINT") - cred, err := NewManagedIdentityCredential(clientID, &ManagedIdentityCredentialOptions{HTTPClient: srv}) + options := DefaultManagedIdentityCredentialOptions() + options.HTTPClient = srv + cred, err := NewManagedIdentityCredential(clientID, &options) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -305,7 +323,9 @@ func TestManagedIdentityCredential_GetTokenUnexpectedJSON(t *testing.T) { srv.AppendResponse(mock.WithBody([]byte(accessTokenRespMalformed))) _ = os.Setenv("MSI_ENDPOINT", srv.URL()) defer clearEnvVars("MSI_ENDPOINT") - msiCred, err := NewManagedIdentityCredential(clientID, &ManagedIdentityCredentialOptions{HTTPClient: srv}) + options := DefaultManagedIdentityCredentialOptions() + options.HTTPClient = srv + msiCred, err := NewManagedIdentityCredential(clientID, &options) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -361,7 +381,9 @@ func TestManagedIdentityCredential_GetTokenEnvVar(t *testing.T) { srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) _ = os.Setenv("MSI_ENDPOINT", srv.URL()) defer clearEnvVars("MSI_ENDPOINT") - msiCred, err := NewManagedIdentityCredential("", &ManagedIdentityCredentialOptions{HTTPClient: srv}) + options := DefaultManagedIdentityCredentialOptions() + options.HTTPClient = srv + msiCred, err := NewManagedIdentityCredential("", &options) if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/sdk/azidentity/username_password_credential.go b/sdk/azidentity/username_password_credential.go index 01731f0d3f37..4559db44983c 100644 --- a/sdk/azidentity/username_password_credential.go +++ b/sdk/azidentity/username_password_credential.go @@ -9,34 +9,39 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" ) -// UsernamePasswordCredential enables authentication to Azure Active Directory using a user's username and password. If the user has MFA enabled this -// credential will fail to get a token returning an AuthenticationFailureError. Also, this credential requires a high degree of trust and is not -// recommended outside of prototyping when more secure credentials can be used. -type UsernamePasswordCredential struct { - client *aadIdentityClient - tenantID string // Gets the Azure Active Directory tenant (directory) ID of the service principal - clientID string // Gets the client (application) ID of the service principal - username string // Gets the user account's user name - password string // Gets the user account's password -} - // UsernamePasswordCredentialOptions can be used to provide additional information to configure the UsernamePasswordCredential. // Use these options to modify the default pipeline behavior through the TokenCredentialOptions. +// Call DefaultUsernamePasswordCredentialOptions() to create an instance populated with default values. type UsernamePasswordCredentialOptions struct { - // The host of the Azure Active Directory authority. The default is https://login.microsoft.com + // The host of the Azure Active Directory authority. The default is AzurePublicCloud. + // Leave empty to allow overriding the value from the AZURE_AUTHORITY_HOST environment variable. AuthorityHost string // HTTPClient sets the transport for making HTTP requests // Leave this as nil to use the default HTTP transport HTTPClient azcore.Transport // Retry configures the built-in retry policy behavior - Retry *azcore.RetryOptions + Retry azcore.RetryOptions // Telemetry configures the built-in telemetry policy behavior Telemetry azcore.TelemetryOptions } // DefaultUsernamePasswordCredentialOptions returns an instance of UsernamePasswordCredentialOptions initialized with default values. func DefaultUsernamePasswordCredentialOptions() UsernamePasswordCredentialOptions { - return UsernamePasswordCredentialOptions{} + return UsernamePasswordCredentialOptions{ + Retry: azcore.DefaultRetryOptions(), + Telemetry: azcore.DefaultTelemetryOptions(), + } +} + +// UsernamePasswordCredential enables authentication to Azure Active Directory using a user's username and password. If the user has MFA enabled this +// credential will fail to get a token returning an AuthenticationFailureError. Also, this credential requires a high degree of trust and is not +// recommended outside of prototyping when more secure credentials can be used. +type UsernamePasswordCredential struct { + client *aadIdentityClient + tenantID string // Gets the Azure Active Directory tenant (directory) ID of the service principal + clientID string // Gets the client (application) ID of the service principal + username string // Gets the user account's user name + password string // Gets the user account's password } // NewUsernamePasswordCredential constructs a new UsernamePasswordCredential with the details needed to authenticate against Azure Active Directory with @@ -48,7 +53,7 @@ func DefaultUsernamePasswordCredentialOptions() UsernamePasswordCredentialOption // options: UsernamePasswordCredentialOptions used to configure the pipeline for the requests sent to Azure Active Directory. func NewUsernamePasswordCredential(tenantID string, clientID string, username string, password string, options *UsernamePasswordCredentialOptions) (*UsernamePasswordCredential, error) { if !validTenantID(tenantID) { - return nil, &CredentialUnavailableError{CredentialType: "Username Password Credential", Message: tenantIDValidationErr} + return nil, &CredentialUnavailableError{credentialType: "Username Password Credential", message: tenantIDValidationErr} } if options == nil { temp := DefaultUsernamePasswordCredentialOptions() diff --git a/sdk/azidentity/username_password_credential_test.go b/sdk/azidentity/username_password_credential_test.go index 456c3ee8a167..aa74b2aca29c 100644 --- a/sdk/azidentity/username_password_credential_test.go +++ b/sdk/azidentity/username_password_credential_test.go @@ -80,7 +80,10 @@ func TestUsernamePasswordCredential_GetTokenSuccess(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) - cred, err := NewUsernamePasswordCredential(tenantID, clientID, "username", "password", &UsernamePasswordCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultUsernamePasswordCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewUsernamePasswordCredential(tenantID, clientID, "username", "password", &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -94,7 +97,10 @@ func TestUsernamePasswordCredential_GetTokenInvalidCredentials(t *testing.T) { srv, close := mock.NewTLSServer() defer close() srv.SetResponse(mock.WithStatusCode(http.StatusUnauthorized)) - cred, err := NewUsernamePasswordCredential(tenantID, clientID, "username", "wrong_password", &UsernamePasswordCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultUsernamePasswordCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewUsernamePasswordCredential(tenantID, clientID, "username", "wrong_password", &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) } @@ -109,7 +115,10 @@ func TestBearerPolicy_UsernamePasswordCredential(t *testing.T) { defer close() srv.AppendResponse(mock.WithBody([]byte(accessTokenRespSuccess))) srv.AppendResponse(mock.WithStatusCode(http.StatusOK)) - cred, err := NewUsernamePasswordCredential(tenantID, clientID, "username", "password", &UsernamePasswordCredentialOptions{HTTPClient: srv, AuthorityHost: srv.URL()}) + options := DefaultUsernamePasswordCredentialOptions() + options.AuthorityHost = srv.URL() + options.HTTPClient = srv + cred, err := NewUsernamePasswordCredential(tenantID, clientID, "username", "password", &options) if err != nil { t.Fatalf("Unable to create credential. Received: %v", err) }