Skip to content

Commit 3b97332

Browse files
authored
feat!: enhance credentials usage and update warning message for credentials requests (#915)
- User type become unexported - HTTP not secure warning message added for auth token flow too - Intialize the default auth scheme value `Bearer` during client creation
1 parent 0f4a16a commit 3b97332

File tree

10 files changed

+80
-84
lines changed

10 files changed

+80
-84
lines changed

client.go

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ var (
7676
jsonKey = "json"
7777
xmlKey = "xml"
7878

79+
defaultAuthScheme = "Bearer"
80+
7981
hdrUserAgentValue = "go-resty/" + Version + " (https://github.com/go-resty/resty)"
8082
bufPool = &sync.Pool{New: func() any { return &bytes.Buffer{} }}
8183
)
@@ -172,7 +174,7 @@ type Client struct {
172174
pathParams map[string]string
173175
rawPathParams map[string]string
174176
header http.Header
175-
userInfo *User
177+
credentials *credentials
176178
authToken string
177179
authScheme string
178180
cookies []*http.Cookie
@@ -221,25 +223,13 @@ type Client struct {
221223
certWatcherStopChan chan bool
222224
}
223225

224-
// User type is to hold an username and password information
225-
type User struct {
226-
Username, Password string
227-
}
228-
229226
// CertWatcherOptions allows configuring a watcher that reloads dynamically TLS certs.
230227
type CertWatcherOptions struct {
231228
// PoolInterval is the frequency at which resty will check if the PEM file needs to be reloaded.
232229
// Default is 24 hours.
233230
PoolInterval time.Duration
234231
}
235232

236-
// Clone method returns deep copy of u.
237-
func (u *User) Clone() *User {
238-
uu := new(User)
239-
*uu = *u
240-
return uu
241-
}
242-
243233
//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
244234
// Client methods
245235
//___________________________________
@@ -498,15 +488,6 @@ func (c *Client) SetFormData(data map[string]string) *Client {
498488
return c
499489
}
500490

501-
// UserInfo method returns the authorization username and password.
502-
//
503-
// userInfo := client.UserInfo()
504-
func (c *Client) UserInfo() *User {
505-
c.lock.RLock()
506-
defer c.lock.RUnlock()
507-
return c.userInfo
508-
}
509-
510491
// SetBasicAuth method sets the basic authentication header in the HTTP request. For Example:
511492
//
512493
// Authorization: Basic <base64-encoded-value>
@@ -522,7 +503,7 @@ func (c *Client) UserInfo() *User {
522503
func (c *Client) SetBasicAuth(username, password string) *Client {
523504
c.lock.Lock()
524505
defer c.lock.Unlock()
525-
c.userInfo = &User{Username: username, Password: password}
506+
c.credentials = &credentials{Username: username, Password: password}
526507
return c
527508
}
528509

@@ -611,8 +592,8 @@ func (c *Client) SetDigestAuth(username, password string) *Client {
611592
c.lock.Unlock()
612593
c.AddRequestMiddleware(func(c *Client, _ *Request) error {
613594
c.httpClient.Transport = &digestTransport{
614-
digestCredentials: digestCredentials{username, password},
615-
transport: oldTransport,
595+
credentials: credentials{username, password},
596+
transport: oldTransport,
616597
}
617598
return nil
618599
})
@@ -638,7 +619,6 @@ func (c *Client) R() *Request {
638619
IsTrace: c.isTrace,
639620
AuthScheme: c.authScheme,
640621
AuthToken: c.authToken,
641-
UserInfo: c.userInfo,
642622
RetryCount: c.retryCount,
643623
RetryWaitTime: c.retryWaitTime,
644624
RetryMaxWaitTime: c.retryMaxWaitTime,
@@ -660,6 +640,7 @@ func (c *Client) R() *Request {
660640
setContentLength: c.setContentLength,
661641
generateCurlOnDebug: c.generateCurlOnDebug,
662642
unescapeQueryParams: c.unescapeQueryParams,
643+
credentials: c.credentials,
663644
}
664645

665646
if c.ctx != nil {
@@ -2012,7 +1993,7 @@ func (c *Client) Clone(ctx context.Context) *Client {
20121993
cc.header = c.header.Clone()
20131994
cc.pathParams = maps.Clone(c.pathParams)
20141995
cc.rawPathParams = maps.Clone(c.rawPathParams)
2015-
cc.userInfo = c.userInfo.Clone()
1996+
cc.credentials = c.credentials.Clone()
20161997
cc.contentTypeEncoders = maps.Clone(c.contentTypeEncoders)
20171998
cc.contentTypeDecoders = maps.Clone(c.contentTypeDecoders)
20181999
cc.contentDecompressors = maps.Clone(c.contentDecompressors)

client_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,8 +1381,8 @@ func TestClientClone(t *testing.T) {
13811381
// assert non-interface type
13821382
assertEqual(t, "http://localhost", parent.BaseURL())
13831383
assertEqual(t, "https://local.host", clone.BaseURL())
1384-
assertEqual(t, "parent", parent.UserInfo().Username)
1385-
assertEqual(t, "clone", clone.UserInfo().Username)
1384+
assertEqual(t, "parent", parent.credentials.Username)
1385+
assertEqual(t, "clone", clone.credentials.Username)
13861386

13871387
// assert interface/pointer type
13881388
assertEqual(t, parent.Client(), clone.Client())

digest.go

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,8 @@ var hashFuncs = map[string]func() hash.Hash{
3838
"SHA-512-256-sess": sha512.New,
3939
}
4040

41-
type digestCredentials struct {
42-
username, password string
43-
}
44-
4541
type digestTransport struct {
46-
digestCredentials
42+
credentials
4743
transport http.RoundTripper
4844
}
4945

@@ -98,9 +94,9 @@ func (dt *digestTransport) RoundTrip(req *http.Request) (*http.Response, error)
9894
return dt.transport.RoundTrip(req2)
9995
}
10096

101-
func (dt *digestTransport) newCredentials(req *http.Request, c *challenge) *credentials {
102-
return &credentials{
103-
username: dt.username,
97+
func (dt *digestTransport) newCredentials(req *http.Request, c *challenge) *digestCredentials {
98+
return &digestCredentials{
99+
username: dt.Username,
104100
userhash: c.userhash,
105101
realm: c.realm,
106102
nonce: c.nonce,
@@ -111,7 +107,7 @@ func (dt *digestTransport) newCredentials(req *http.Request, c *challenge) *cred
111107
messageQop: c.qop,
112108
nc: 0,
113109
method: req.Method,
114-
password: dt.password,
110+
password: dt.Password,
115111
}
116112
}
117113

@@ -203,7 +199,7 @@ func parseChallenge(input string) (*challenge, error) {
203199
return c, nil
204200
}
205201

206-
type credentials struct {
202+
type digestCredentials struct {
207203
username string
208204
userhash string
209205
realm string
@@ -219,7 +215,7 @@ type credentials struct {
219215
password string
220216
}
221217

222-
func (c *credentials) authorize() (string, error) {
218+
func (c *digestCredentials) authorize() (string, error) {
223219
if _, ok := hashFuncs[c.algorithm]; !ok {
224220
return "", ErrDigestAlgNotSupported
225221
}
@@ -257,7 +253,7 @@ func (c *credentials) authorize() (string, error) {
257253
return fmt.Sprintf("Digest %s", strings.Join(sl, ", ")), nil
258254
}
259255

260-
func (c *credentials) validateQop() error {
256+
func (c *digestCredentials) validateQop() error {
261257
// Currently only supporting auth quality of protection. TODO: add auth-int support
262258
// NOTE: cURL support auth-int qop for requests other than POST and PUT (i.e. w/o body) by hashing an empty string
263259
// is this applicable for resty? see: https://github.com/curl/curl/blob/307b7543ea1e73ab04e062bdbe4b5bb409eaba3a/lib/vauth/digest.c#L774
@@ -282,14 +278,14 @@ func (c *credentials) validateQop() error {
282278
return nil
283279
}
284280

285-
func (c *credentials) h(data string) string {
281+
func (c *digestCredentials) h(data string) string {
286282
hfCtor := hashFuncs[c.algorithm]
287283
hf := hfCtor()
288284
_, _ = hf.Write([]byte(data)) // Hash.Write never returns an error
289285
return fmt.Sprintf("%x", hf.Sum(nil))
290286
}
291287

292-
func (c *credentials) resp() (string, error) {
288+
func (c *digestCredentials) resp() (string, error) {
293289
c.nc++
294290

295291
b := make([]byte, 16)
@@ -306,12 +302,12 @@ func (c *credentials) resp() (string, error) {
306302
c.nonce, c.nc, c.cNonce, c.messageQop, ha2)), nil
307303
}
308304

309-
func (c *credentials) kd(secret, data string) string {
305+
func (c *digestCredentials) kd(secret, data string) string {
310306
return c.h(fmt.Sprintf("%s:%s", secret, data))
311307
}
312308

313309
// RFC 7616 3.4.2
314-
func (c *credentials) ha1() string {
310+
func (c *digestCredentials) ha1() string {
315311
ret := c.h(fmt.Sprintf("%s:%s:%s", c.username, c.realm, c.password))
316312
if c.sessionAlg {
317313
return c.h(fmt.Sprintf("%s:%s:%s", ret, c.nonce, c.cNonce))
@@ -321,7 +317,7 @@ func (c *credentials) ha1() string {
321317
}
322318

323319
// RFC 7616 3.4.3
324-
func (c *credentials) ha2() string {
320+
func (c *digestCredentials) ha2() string {
325321
// currently no auth-int support
326322
return c.h(fmt.Sprintf("%s:%s", c.method, c.digestURI))
327323
}

example_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,12 @@ func Example_post() {
9999

100100
printOutput(resp1, err1)
101101

102+
type User struct {
103+
Username, Password string
104+
}
102105
// POST Struct, default is JSON content type. No need to set one
103106
resp2, err2 := client.R().
104-
SetBody(resty.User{Username: "testuser", Password: "testpass"}).
107+
SetBody(User{Username: "testuser", Password: "testpass"}).
105108
SetResult(&AuthSuccess{}). // or SetResult(AuthSuccess{}).
106109
SetError(&AuthError{}). // or SetError(AuthError{}).
107110
Post("https://myapp.com/login")

middleware.go

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -302,28 +302,23 @@ func createHTTPRequest(c *Client, r *Request) (err error) {
302302
}
303303

304304
func addCredentials(c *Client, r *Request) error {
305-
var isBasicAuth bool
305+
credentialsAdded := false
306306
// Basic Auth
307-
if r.UserInfo != nil {
308-
r.RawRequest.SetBasicAuth(r.UserInfo.Username, r.UserInfo.Password)
309-
isBasicAuth = true
310-
}
311-
312-
if !c.IsDisableWarn() {
313-
if isBasicAuth && !strings.HasPrefix(r.URL, "https") {
314-
r.log.Warnf("Using Basic Auth in HTTP mode is not secure, use HTTPS")
315-
}
307+
if r.credentials != nil {
308+
credentialsAdded = true
309+
r.RawRequest.SetBasicAuth(r.credentials.Username, r.credentials.Password)
316310
}
317311

318312
// Build the token Auth header
319313
if !isStringEmpty(r.AuthToken) {
320-
var authScheme string
321-
if isStringEmpty(r.AuthScheme) {
322-
authScheme = "Bearer"
323-
} else {
324-
authScheme = r.AuthScheme
314+
credentialsAdded = true
315+
r.RawRequest.Header.Set(c.HeaderAuthorizationKey(), r.AuthScheme+" "+r.AuthToken)
316+
}
317+
318+
if !c.IsDisableWarn() && credentialsAdded {
319+
if strings.HasPrefix(r.URL, "http") {
320+
r.log.Warnf("Using sensitive credentials in HTTP mode is not secure. Use HTTPS")
325321
}
326-
r.RawRequest.Header.Set(c.HeaderAuthorizationKey(), authScheme+" "+r.AuthToken)
327322
}
328323

329324
return nil

request.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ type Request struct {
4747
Result any
4848
Error any
4949
RawRequest *http.Request
50-
UserInfo *User
5150
Cookies []*http.Cookie
5251
Debug bool
5352
CloseConnection bool
@@ -77,6 +76,7 @@ type Request struct {
7776
// first attempt + retry count = total attempts
7877
Attempt int
7978

79+
credentials *credentials
8080
isMultiPart bool
8181
isFormData bool
8282
setContentLength bool
@@ -618,7 +618,7 @@ func (r *Request) SetContentLength(l bool) *Request {
618618
//
619619
// It overrides the credentials set by method [Client.SetBasicAuth].
620620
func (r *Request) SetBasicAuth(username, password string) *Request {
621-
r.UserInfo = &User{Username: username, Password: password}
621+
r.credentials = &credentials{Username: username, Password: password}
622622
return r
623623
}
624624

@@ -677,8 +677,8 @@ func (r *Request) SetDigestAuth(username, password string) *Request {
677677
oldTransport := r.client.httpClient.Transport
678678
r.client.AddRequestMiddleware(func(c *Client, _ *Request) error {
679679
c.httpClient.Transport = &digestTransport{
680-
digestCredentials: digestCredentials{username, password},
681-
transport: oldTransport,
680+
credentials: credentials{username, password},
681+
transport: oldTransport,
682682
}
683683
return nil
684684
})
@@ -1400,8 +1400,8 @@ func (r *Request) Clone(ctx context.Context) *Request {
14001400
rr.RawPathParams = maps.Clone(r.RawPathParams)
14011401

14021402
// clone basic auth
1403-
if r.UserInfo != nil {
1404-
rr.UserInfo = r.UserInfo.Clone()
1403+
if r.credentials != nil {
1404+
rr.credentials = r.credentials.Clone()
14051405
}
14061406

14071407
// clone cookies

0 commit comments

Comments
 (0)