@rivascva/dt-idl 1.1.96 → 1.1.98
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/go/request/auth_transport.go +101 -0
- package/package.json +1 -1
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
package request
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"net/http"
|
|
6
|
+
"sync"
|
|
7
|
+
"time"
|
|
8
|
+
|
|
9
|
+
"github.com/RivasCVA/dt-idl/go/auth"
|
|
10
|
+
"github.com/golang-jwt/jwt/v5"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
type AuthTransport struct {
|
|
14
|
+
mu sync.RWMutex
|
|
15
|
+
transport http.RoundTripper
|
|
16
|
+
service string
|
|
17
|
+
secret string
|
|
18
|
+
token *jwt.Token
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
func NewAuthTransport(transport http.RoundTripper, service string, secret string) *AuthTransport {
|
|
22
|
+
return &AuthTransport{
|
|
23
|
+
mu: sync.RWMutex{},
|
|
24
|
+
transport: transport,
|
|
25
|
+
service: service,
|
|
26
|
+
secret: secret,
|
|
27
|
+
token: nil,
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
func (t *AuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
32
|
+
// get the token
|
|
33
|
+
t.mu.RLock()
|
|
34
|
+
token := t.token
|
|
35
|
+
t.mu.RUnlock()
|
|
36
|
+
|
|
37
|
+
// get the token expiration time
|
|
38
|
+
expirationTime := time.Time{}
|
|
39
|
+
if token != nil {
|
|
40
|
+
e, err := token.Claims.GetExpirationTime()
|
|
41
|
+
if err != nil {
|
|
42
|
+
return nil, fmt.Errorf("failed to get the expiration time from the token: %w", err)
|
|
43
|
+
}
|
|
44
|
+
expirationTime = e.Time
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// check if the token is expired or close to expiring
|
|
48
|
+
if expirationTime.Before(time.Now().Add(time.Minute)) {
|
|
49
|
+
err := t.setNewToken()
|
|
50
|
+
if err != nil {
|
|
51
|
+
return nil, fmt.Errorf("failed to set a new token: %w", err)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// perform the request
|
|
56
|
+
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", t.token.Raw))
|
|
57
|
+
resp, err := t.transport.RoundTrip(req)
|
|
58
|
+
if err != nil {
|
|
59
|
+
return nil, err
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// check if the request failed due to an authorization error.
|
|
63
|
+
// this should ideally not happen since we are setting a new token if the current one is expired or close to expiring,
|
|
64
|
+
// but it is still possible for the token to be revoked.
|
|
65
|
+
if resp != nil && resp.StatusCode == http.StatusUnauthorized {
|
|
66
|
+
// set a new token
|
|
67
|
+
err := t.setNewToken()
|
|
68
|
+
if err != nil {
|
|
69
|
+
return nil, fmt.Errorf("failed to set a new token: %w", err)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// retry the request
|
|
73
|
+
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", t.token.Raw))
|
|
74
|
+
return t.transport.RoundTrip(req)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// return the original response
|
|
78
|
+
return resp, err
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
func (t *AuthTransport) setNewToken() error {
|
|
82
|
+
t.mu.Lock()
|
|
83
|
+
defer t.mu.Unlock()
|
|
84
|
+
|
|
85
|
+
// create a new service token
|
|
86
|
+
rawToken, err := auth.NewServiceToken(t.secret, t.service, t.service, auth.DefaultServiceTokenDuration)
|
|
87
|
+
if err != nil {
|
|
88
|
+
return fmt.Errorf("failed to create a new service token: %w", err)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// validate the token
|
|
92
|
+
token, err := auth.ValidateToken(rawToken, t.secret)
|
|
93
|
+
if err != nil {
|
|
94
|
+
return fmt.Errorf("failed to validate the new token: %w", err)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// set the new token
|
|
98
|
+
t.token = token
|
|
99
|
+
|
|
100
|
+
return nil
|
|
101
|
+
}
|