Skip to content

Commit afc7858

Browse files
authored
Merge pull request #184 from deploymenttheory/dev
Refactor of authentication manager
2 parents 8930ec9 + 9157462 commit afc7858

11 files changed

+158
-79
lines changed

authenticationhandler/auth_token_management.go

-58
This file was deleted.

authenticationhandler/auth_handler.go renamed to authenticationhandler/authenticationhandler.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// authenticationhandler/auth_handler.go
1+
// authenticationhandler/authenticationhandler.go
22

33
package authenticationhandler
44

authenticationhandler/auth_bearer_token.go renamed to authenticationhandler/basicauthentication.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// authenticationhandler/auth_bearer_token.go
1+
// authenticationhandler/basicauthentication.go
22
/* The http_client_auth package focuses on authentication mechanisms for an HTTP client.
33
It provides structures and methods for handling both basic and bearer token based authentication */
44

@@ -14,8 +14,8 @@ import (
1414
"go.uber.org/zap"
1515
)
1616

17-
// ObtainToken fetches and sets an authentication token using the stored basic authentication credentials.
18-
func (h *AuthTokenHandler) ObtainToken(apiHandler apihandler.APIHandler, httpClient *http.Client, username string, password string) error {
17+
// BasicAuthTokenAcquisition fetches and sets an authentication token using the stored basic authentication credentials.
18+
func (h *AuthTokenHandler) BasicAuthTokenAcquisition(apiHandler apihandler.APIHandler, httpClient *http.Client, username string, password string) error {
1919

2020
// Use the APIHandler's method to get the bearer token endpoint
2121
bearerTokenEndpoint := apiHandler.GetBearerTokenEndpoint()
@@ -60,8 +60,8 @@ func (h *AuthTokenHandler) ObtainToken(apiHandler apihandler.APIHandler, httpCli
6060
return nil
6161
}
6262

63-
// RefreshToken refreshes the current authentication token.
64-
func (h *AuthTokenHandler) RefreshToken(apiHandler apihandler.APIHandler, httpClient *http.Client) error {
63+
// RefreshBearerToken refreshes the current authentication token.
64+
func (h *AuthTokenHandler) RefreshBearerToken(apiHandler apihandler.APIHandler, httpClient *http.Client) error {
6565
h.tokenLock.Lock()
6666
defer h.tokenLock.Unlock()
6767

authenticationhandler/auth_oauth.go renamed to authenticationhandler/oauth2.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
// authenticationhandler/auth_oauth.go
1+
// authenticationhandler/oauth2.go
22

33
/* The http_client_auth package focuses on authentication mechanisms for an HTTP client.
4-
It provides structures and methods for handling OAuth-based authentication
5-
*/
4+
It provides structures and methods for handling OAuth-based authentication */
65

76
package authenticationhandler
87

@@ -30,9 +29,9 @@ type OAuthResponse struct {
3029
Error string `json:"error,omitempty"` // Error contains details if an error occurs during the token acquisition process.
3130
}
3231

33-
// ObtainOAuthToken fetches an OAuth access token using the provided client ID and client secret.
32+
// OAuth2TokenAcquisition fetches an OAuth access token using the provided client ID and client secret.
3433
// It updates the AuthTokenHandler's Token and Expires fields with the obtained values.
35-
func (h *AuthTokenHandler) ObtainOAuthToken(apiHandler apihandler.APIHandler, httpClient *http.Client, clientID, clientSecret string) error {
34+
func (h *AuthTokenHandler) OAuth2TokenAcquisition(apiHandler apihandler.APIHandler, httpClient *http.Client, clientID, clientSecret string) error {
3635
// Get the OAuth token endpoint from the APIHandler
3736
oauthTokenEndpoint := apiHandler.GetOAuthTokenEndpoint()
3837

authenticationhandler/tokenmanager.go

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// authenticationhandler/tokenmanager.go
2+
package authenticationhandler
3+
4+
import (
5+
"fmt"
6+
"net/http"
7+
"time"
8+
9+
"github.com/deploymenttheory/go-api-http-client/apiintegrations/apihandler"
10+
"go.uber.org/zap"
11+
)
12+
13+
// CheckAndRefreshAuthToken checks the token's validity and refreshes it if necessary.
14+
// It returns true if the token is valid post any required operations and false with an error otherwise.
15+
func (h *AuthTokenHandler) CheckAndRefreshAuthToken(apiHandler apihandler.APIHandler, httpClient *http.Client, clientCredentials ClientCredentials, tokenRefreshBufferPeriod time.Duration) (bool, error) {
16+
if !h.isTokenValid(tokenRefreshBufferPeriod) {
17+
h.Logger.Debug("Token found to be invalid or close to expiry, handling token acquisition or refresh.")
18+
if err := h.obtainNewToken(apiHandler, httpClient, clientCredentials); err != nil {
19+
h.Logger.Error("Failed to obtain new token", zap.Error(err))
20+
return false, err
21+
}
22+
}
23+
24+
if err := h.refreshTokenIfNeeded(apiHandler, httpClient, clientCredentials, tokenRefreshBufferPeriod); err != nil {
25+
h.Logger.Error("Failed to refresh token", zap.Error(err))
26+
return false, err
27+
}
28+
29+
isValid := h.isTokenValid(tokenRefreshBufferPeriod)
30+
h.Logger.Info("Token validation status post check", zap.Bool("IsValid", isValid))
31+
return isValid, nil
32+
}
33+
34+
// isTokenValid checks if the current token is non-empty and not about to expire.
35+
// It considers a token valid if it exists and the time until its expiration is greater than the provided buffer period.
36+
func (h *AuthTokenHandler) isTokenValid(tokenRefreshBufferPeriod time.Duration) bool {
37+
isValid := h.Token != "" && time.Until(h.Expires) >= tokenRefreshBufferPeriod
38+
h.Logger.Debug("Checking token validity", zap.Bool("IsValid", isValid), zap.Duration("TimeUntilExpiry", time.Until(h.Expires)))
39+
return isValid
40+
}
41+
42+
// obtainNewToken acquires a new token using the credentials provided.
43+
// It handles different authentication methods based on the AuthMethod setting.
44+
func (h *AuthTokenHandler) obtainNewToken(apiHandler apihandler.APIHandler, httpClient *http.Client, clientCredentials ClientCredentials) error {
45+
var err error
46+
if h.AuthMethod == "basicauth" {
47+
err = h.BasicAuthTokenAcquisition(apiHandler, httpClient, clientCredentials.Username, clientCredentials.Password)
48+
} else if h.AuthMethod == "oauth2" {
49+
err = h.OAuth2TokenAcquisition(apiHandler, httpClient, clientCredentials.ClientID, clientCredentials.ClientSecret)
50+
} else {
51+
err = fmt.Errorf("no valid credentials provided. Unable to obtain a token")
52+
h.Logger.Error("Authentication method not supported", zap.String("AuthMethod", h.AuthMethod))
53+
}
54+
55+
if err != nil {
56+
h.Logger.Error("Failed to obtain new token", zap.Error(err))
57+
}
58+
return err
59+
}
60+
61+
// refreshTokenIfNeeded refreshes the token if it's close to expiration.
62+
// This function decides on the method based on the credentials type available.
63+
func (h *AuthTokenHandler) refreshTokenIfNeeded(apiHandler apihandler.APIHandler, httpClient *http.Client, clientCredentials ClientCredentials, tokenRefreshBufferPeriod time.Duration) error {
64+
if time.Until(h.Expires) < tokenRefreshBufferPeriod {
65+
h.Logger.Info("Token is close to expiry and will be refreshed", zap.Duration("TimeUntilExpiry", time.Until(h.Expires)))
66+
var err error
67+
if clientCredentials.Username != "" && clientCredentials.Password != "" {
68+
err = h.RefreshBearerToken(apiHandler, httpClient)
69+
} else if clientCredentials.ClientID != "" && clientCredentials.ClientSecret != "" {
70+
err = h.OAuth2TokenAcquisition(apiHandler, httpClient, clientCredentials.ClientID, clientCredentials.ClientSecret)
71+
} else {
72+
err = fmt.Errorf("unknown auth method")
73+
h.Logger.Error("Failed to determine authentication method for token refresh", zap.String("AuthMethod", h.AuthMethod))
74+
}
75+
76+
if err != nil {
77+
h.Logger.Error("Failed to refresh token", zap.Error(err))
78+
return err
79+
}
80+
}
81+
return nil
82+
}

authenticationhandler/auth_validation.go renamed to authenticationhandler/validation.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// authenticationhandler/auth_validation.go
1+
// authenticationhandler/validation.go
22

33
package authenticationhandler
44

concurrency/metrics.go

+60-4
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,20 @@ func (ch *ConcurrencyHandler) MonitorServerResponseCodes(resp *http.Response) in
197197
var responseTimes []time.Duration
198198
var responseTimesLock sync.Mutex
199199

200-
// MonitorResponseTimeVariability monitors the response time variability and suggests a concurrency adjustment.
201-
// MonitorResponseTimeVariability monitors the response time variability and suggests a concurrency adjustment.
200+
// MonitorResponseTimeVariability assesses the response time variability from a series of HTTP requests and decides whether to adjust the concurrency level of outgoing requests. This function is integral to maintaining optimal system performance under varying load conditions.
201+
//
202+
// The function first appends the latest response time to a sliding window of the last 10 response times to maintain a recent history. It then calculates the standard deviation and the average of these times. The standard deviation helps determine the variability or consistency of response times, while the average gives a central tendency.
203+
//
204+
// Based on these calculated metrics, the function employs a multi-factor decision mechanism:
205+
// - If the standard deviation exceeds a pre-defined threshold and the average response time is greater than an acceptable maximum, a debounce counter is incremented. This counter must reach a predefined threshold (debounceScaleDownThreshold) before a decision to decrease concurrency is made, ensuring that only sustained negative trends lead to a scale down.
206+
// - If the standard deviation is below or equal to the threshold, suggesting stable response times, and the system is currently operating below its concurrency capacity, it may suggest an increase in concurrency to improve throughput.
207+
//
208+
// This approach aims to prevent transient spikes in response times from causing undue scaling actions, thus stabilizing the overall performance and responsiveness of the system.
209+
//
210+
// Returns:
211+
// - (-1) to suggest a decrease in concurrency,
212+
// - (1) to suggest an increase in concurrency,
213+
// - (0) to indicate no change needed.
202214
func (ch *ConcurrencyHandler) MonitorResponseTimeVariability(responseTime time.Duration) int {
203215
ch.Metrics.ResponseTimeVariability.Lock.Lock()
204216
defer ch.Metrics.ResponseTimeVariability.Lock.Unlock()
@@ -229,7 +241,30 @@ func (ch *ConcurrencyHandler) MonitorResponseTimeVariability(responseTime time.D
229241
return 0 // Default to no change
230242
}
231243

232-
// calculateAverage computes the average response time from a slice of response times.
244+
// calculateAverage computes the average response time from a slice of time.Duration values.
245+
// The average, or mean, is a measure of the central tendency of a set of values, providing a simple
246+
// summary of the 'typical' value in a set. In the context of response times, the average gives a
247+
// straightforward indication of the overall system response performance over a given set of requests.
248+
//
249+
// The function performs the following steps to calculate the average response time:
250+
// 1. Sum all the response times in the input slice.
251+
// 2. Divide the total sum by the number of response times to find the mean value.
252+
//
253+
// This method of averaging is vital for assessing the overall health and efficiency of the system under load.
254+
// Monitoring average response times can help in identifying trends in system performance, guiding capacity planning,
255+
// and optimizing resource allocation.
256+
//
257+
// Parameters:
258+
// - times: A slice of time.Duration values, each representing the response time for a single request.
259+
//
260+
// Returns:
261+
// - time.Duration: The average response time across all provided times. This is a time.Duration value that
262+
// can be directly compared to other durations or used to set thresholds for alerts or further analysis.
263+
//
264+
// Example Usage:
265+
// This function is typically used in performance analysis where the average response time is monitored over
266+
// specific intervals to ensure service level agreements (SLAs) are met or to trigger scaling actions when
267+
// average response times exceed acceptable levels.
233268
func calculateAverage(times []time.Duration) time.Duration {
234269
var total time.Duration
235270
for _, t := range times {
@@ -238,7 +273,28 @@ func calculateAverage(times []time.Duration) time.Duration {
238273
return total / time.Duration(len(times))
239274
}
240275

241-
// calculateStdDev computes the standard deviation of response times.
276+
// calculateStdDev computes the standard deviation of response times from a slice of time.Duration values.
277+
// Standard deviation is a measure of the amount of variation or dispersion in a set of values. A low standard
278+
// deviation indicates that the values tend to be close to the mean (average) of the set, while a high standard
279+
// deviation indicates that the values are spread out over a wider range.
280+
//
281+
// The function performs the following steps to calculate the standard deviation:
282+
// 1. Calculate the mean (average) response time of the input slice.
283+
// 2. Sum the squared differences from the mean for each response time. This measures the total variance from the mean.
284+
// 3. Divide the total variance by the number of response times to get the average variance.
285+
// 4. Take the square root of the average variance to obtain the standard deviation.
286+
//
287+
// This statistical approach is crucial for identifying how consistently the system responds under different loads
288+
// and can be instrumental in diagnosing performance fluctuations in real-time systems.
289+
//
290+
// Parameters:
291+
// - times: A slice of time.Duration values representing response times.
292+
//
293+
// Returns:
294+
// - float64: The calculated standard deviation of the response times, which represents the variability in response times.
295+
//
296+
// This function is typically used in performance monitoring to adjust system concurrency based on the stability
297+
// of response times, as part of a larger strategy to optimize application responsiveness and reliability.
242298
func calculateStdDev(times []time.Duration) float64 {
243299
var sum time.Duration
244300
for _, t := range times {

httpclient/auth_method.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func DetermineAuthMethod(authConfig AuthConfig) (string, error) {
3131
validClientSecret, clientSecretErrMsg = authenticationhandler.IsValidClientSecret(authConfig.ClientSecret)
3232
// If both ClientID and ClientSecret are valid, use OAuth
3333
if validClientID && validClientSecret {
34-
return "oauth", nil
34+
return "oauth2", nil
3535
}
3636
}
3737

@@ -41,7 +41,7 @@ func DetermineAuthMethod(authConfig AuthConfig) (string, error) {
4141
validPassword, passwordErrMsg = authenticationhandler.IsValidPassword(authConfig.Password)
4242
// If both Username and Password are valid, use Bearer
4343
if validUsername && validPassword {
44-
return "bearer", nil
44+
return "basicauth", nil
4545
}
4646
}
4747

httpclient/multipartrequest.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func (c *Client) DoMultipartRequest(method, endpoint string, fields map[string]s
4848
ClientSecret: c.clientConfig.Auth.ClientSecret,
4949
}
5050

51-
valid, err := c.AuthTokenHandler.ValidAuthTokenCheck(c.APIHandler, c.httpClient, clientCredentials, c.clientConfig.ClientOptions.TokenRefreshBufferPeriod)
51+
valid, err := c.AuthTokenHandler.CheckAndRefreshAuthToken(c.APIHandler, c.httpClient, clientCredentials, c.clientConfig.ClientOptions.TokenRefreshBufferPeriod)
5252
if err != nil || !valid {
5353
return nil, err
5454
}

httpclient/request.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func (c *Client) executeRequestWithRetries(method, endpoint string, body, out in
123123
ClientSecret: c.clientConfig.Auth.ClientSecret,
124124
}
125125

126-
valid, err := c.AuthTokenHandler.ValidAuthTokenCheck(c.APIHandler, c.httpClient, clientCredentials, c.clientConfig.ClientOptions.TokenRefreshBufferPeriod)
126+
valid, err := c.AuthTokenHandler.CheckAndRefreshAuthToken(c.APIHandler, c.httpClient, clientCredentials, c.clientConfig.ClientOptions.TokenRefreshBufferPeriod)
127127
if err != nil || !valid {
128128
return nil, err
129129
}
@@ -286,7 +286,7 @@ func (c *Client) executeRequest(method, endpoint string, body, out interface{})
286286
ClientSecret: c.clientConfig.Auth.ClientSecret,
287287
}
288288

289-
valid, err := c.AuthTokenHandler.ValidAuthTokenCheck(c.APIHandler, c.httpClient, clientCredentials, c.clientConfig.ClientOptions.TokenRefreshBufferPeriod)
289+
valid, err := c.AuthTokenHandler.CheckAndRefreshAuthToken(c.APIHandler, c.httpClient, clientCredentials, c.clientConfig.ClientOptions.TokenRefreshBufferPeriod)
290290
if err != nil || !valid {
291291
return nil, err
292292
}

0 commit comments

Comments
 (0)