1//go:build go1.18
2// +build go1.18
3
4// Copyright (c) Microsoft Corporation. All rights reserved.
5// Licensed under the MIT License.
6
7package azidentity
8
9import (
10 "bytes"
11 "encoding/json"
12 "errors"
13 "fmt"
14 "net/http"
15
16 "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
17 "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime"
18 "github.com/Azure/azure-sdk-for-go/sdk/internal/errorinfo"
19 msal "github.com/AzureAD/microsoft-authentication-library-for-go/apps/errors"
20)
21
22// getResponseFromError retrieves the response carried by
23// an AuthenticationFailedError or MSAL CallErr, if any
24func getResponseFromError(err error) *http.Response {
25 var a *AuthenticationFailedError
26 var c msal.CallErr
27 var res *http.Response
28 if errors.As(err, &c) {
29 res = c.Resp
30 } else if errors.As(err, &a) {
31 res = a.RawResponse
32 }
33 return res
34}
35
36// AuthenticationFailedError indicates an authentication request has failed.
37type AuthenticationFailedError struct {
38 // RawResponse is the HTTP response motivating the error, if available.
39 RawResponse *http.Response
40
41 credType string
42 message string
43 err error
44}
45
46func newAuthenticationFailedError(credType string, message string, resp *http.Response, err error) error {
47 return &AuthenticationFailedError{credType: credType, message: message, RawResponse: resp, err: err}
48}
49
50// Error implements the error interface. Note that the message contents are not contractual and can change over time.
51func (e *AuthenticationFailedError) Error() string {
52 if e.RawResponse == nil {
53 return e.credType + ": " + e.message
54 }
55 msg := &bytes.Buffer{}
56 fmt.Fprintf(msg, "%s authentication failed. %s\n", e.credType, e.message)
57 if e.RawResponse.Request != nil {
58 fmt.Fprintf(msg, "%s %s://%s%s\n", e.RawResponse.Request.Method, e.RawResponse.Request.URL.Scheme, e.RawResponse.Request.URL.Host, e.RawResponse.Request.URL.Path)
59 } else {
60 // this happens when the response is created from a custom HTTP transporter,
61 // which doesn't guarantee to bind the original request to the response
62 fmt.Fprintln(msg, "Request information not available")
63 }
64 fmt.Fprintln(msg, "--------------------------------------------------------------------------------")
65 fmt.Fprintf(msg, "RESPONSE %s\n", e.RawResponse.Status)
66 fmt.Fprintln(msg, "--------------------------------------------------------------------------------")
67 body, err := runtime.Payload(e.RawResponse)
68 switch {
69 case err != nil:
70 fmt.Fprintf(msg, "Error reading response body: %v", err)
71 case len(body) > 0:
72 if err := json.Indent(msg, body, "", " "); err != nil {
73 // failed to pretty-print so just dump it verbatim
74 fmt.Fprint(msg, string(body))
75 }
76 default:
77 fmt.Fprint(msg, "Response contained no body")
78 }
79 fmt.Fprintln(msg, "\n--------------------------------------------------------------------------------")
80 var anchor string
81 switch e.credType {
82 case credNameAzureCLI:
83 anchor = "azure-cli"
84 case credNameAzureDeveloperCLI:
85 anchor = "azd"
86 case credNameAzurePipelines:
87 anchor = "apc"
88 case credNameCert:
89 anchor = "client-cert"
90 case credNameSecret:
91 anchor = "client-secret"
92 case credNameManagedIdentity:
93 anchor = "managed-id"
94 case credNameUserPassword:
95 anchor = "username-password"
96 case credNameWorkloadIdentity:
97 anchor = "workload"
98 }
99 if anchor != "" {
100 fmt.Fprintf(msg, "To troubleshoot, visit https://aka.ms/azsdk/go/identity/troubleshoot#%s", anchor)
101 }
102 return msg.String()
103}
104
105// NonRetriable indicates the request which provoked this error shouldn't be retried.
106func (*AuthenticationFailedError) NonRetriable() {
107 // marker method
108}
109
110var _ errorinfo.NonRetriable = (*AuthenticationFailedError)(nil)
111
112// authenticationRequiredError indicates a credential's Authenticate method must be called to acquire a token
113// because the credential requires user interaction and is configured not to request it automatically.
114type authenticationRequiredError struct {
115 credentialUnavailableError
116
117 // TokenRequestOptions for the required token. Pass this to the credential's Authenticate method.
118 TokenRequestOptions policy.TokenRequestOptions
119}
120
121func newauthenticationRequiredError(credType string, tro policy.TokenRequestOptions) error {
122 return &authenticationRequiredError{
123 credentialUnavailableError: credentialUnavailableError{
124 credType + " can't acquire a token without user interaction. Call Authenticate to authenticate a user interactively",
125 },
126 TokenRequestOptions: tro,
127 }
128}
129
130var (
131 _ credentialUnavailable = (*authenticationRequiredError)(nil)
132 _ errorinfo.NonRetriable = (*authenticationRequiredError)(nil)
133)
134
135type credentialUnavailable interface {
136 error
137 credentialUnavailable()
138}
139
140type credentialUnavailableError struct {
141 message string
142}
143
144// newCredentialUnavailableError is an internal helper that ensures consistent error message formatting
145func newCredentialUnavailableError(credType, message string) error {
146 msg := fmt.Sprintf("%s: %s", credType, message)
147 return &credentialUnavailableError{msg}
148}
149
150// NewCredentialUnavailableError constructs an error indicating a credential can't attempt authentication
151// because it lacks required data or state. When [ChainedTokenCredential] receives this error it will try
152// its next credential, if any.
153func NewCredentialUnavailableError(message string) error {
154 return &credentialUnavailableError{message}
155}
156
157// Error implements the error interface. Note that the message contents are not contractual and can change over time.
158func (e *credentialUnavailableError) Error() string {
159 return e.message
160}
161
162// NonRetriable is a marker method indicating this error should not be retried. It has no implementation.
163func (*credentialUnavailableError) NonRetriable() {}
164
165func (*credentialUnavailableError) credentialUnavailable() {}
166
167var (
168 _ credentialUnavailable = (*credentialUnavailableError)(nil)
169 _ errorinfo.NonRetriable = (*credentialUnavailableError)(nil)
170)