@rivascva/dt-idl 1.1.102 → 1.1.103
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/dist/index.d.ts +3 -1
- package/go/auth/jwt.go +17 -17
- package/go/auth/models.go +4 -4
- package/go/middlewares/middlewares.go +7 -7
- package/go/models/constants.go +7 -3
- package/go/request/auth_transport.go +38 -38
- package/go/request/http.go +2 -2
- package/go/utils/context.go +22 -22
- package/package.json +1 -1
- package/services/dt-user-service.yaml +6 -2
- package/ts/services/dt-user-service.ts +3 -1
package/dist/index.d.ts
CHANGED
package/go/auth/jwt.go
CHANGED
|
@@ -9,39 +9,39 @@ import (
|
|
|
9
9
|
|
|
10
10
|
// ValidateToken validates the given JWT token using the provided secret.
|
|
11
11
|
func ValidateToken(token string, secret string) (*jwt.Token, error) {
|
|
12
|
-
// create a
|
|
12
|
+
// create a parser with the HS256 signing method
|
|
13
13
|
parser := jwt.NewParser(jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}))
|
|
14
14
|
|
|
15
|
-
// validate the
|
|
15
|
+
// validate the token
|
|
16
16
|
parsedToken, err := parser.Parse(token, func(t *jwt.Token) (any, error) { return []byte(secret), nil })
|
|
17
17
|
if err != nil {
|
|
18
|
-
return nil, fmt.Errorf("unable to validate the
|
|
18
|
+
return nil, fmt.Errorf("unable to validate the token: %w", err)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
// validate the token expiration
|
|
22
|
-
|
|
21
|
+
// validate the token expiration time
|
|
22
|
+
expirationTime, err := parsedToken.Claims.GetExpirationTime()
|
|
23
23
|
if err != nil {
|
|
24
24
|
return nil, fmt.Errorf("failed to get the expiration time from the token: %w", err)
|
|
25
25
|
}
|
|
26
|
-
if time.Now()
|
|
27
|
-
return nil, fmt.Errorf("the
|
|
26
|
+
if expirationTime.Before(time.Now()) {
|
|
27
|
+
return nil, fmt.Errorf("the token has expired")
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
return parsedToken, nil
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
//
|
|
34
|
-
func
|
|
35
|
-
return
|
|
33
|
+
// NewUserAccessToken creates a new JWT access token for the given user id.
|
|
34
|
+
func NewUserAccessToken(accessTokenSecret string, issuer string, userId string, duration time.Duration) (string, error) {
|
|
35
|
+
return newAccessToken(accessTokenSecret, "user", issuer, userId, duration)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
//
|
|
39
|
-
func
|
|
40
|
-
return
|
|
38
|
+
// NewServiceAccessToken creates a new JWT access token for the given service.
|
|
39
|
+
func NewServiceAccessToken(accessTokenSecret string, issuer string, service string, duration time.Duration) (string, error) {
|
|
40
|
+
return newAccessToken(accessTokenSecret, "service", issuer, service, duration)
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
//
|
|
44
|
-
func
|
|
43
|
+
// newAccessToken creates a new JWT access token.
|
|
44
|
+
func newAccessToken(accessTokenSecret string, ttype string, issuer string, subject string, duration time.Duration) (string, error) {
|
|
45
45
|
t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
|
46
46
|
"type": ttype,
|
|
47
47
|
"iss": issuer,
|
|
@@ -50,9 +50,9 @@ func newToken(secret string, ttype string, issuer string, subject string, durati
|
|
|
50
50
|
"exp": time.Now().Add(duration).UnixMilli(),
|
|
51
51
|
})
|
|
52
52
|
|
|
53
|
-
s, err := t.SignedString([]byte(
|
|
53
|
+
s, err := t.SignedString([]byte(accessTokenSecret))
|
|
54
54
|
if err != nil {
|
|
55
|
-
return "", fmt.Errorf("unable to sign the
|
|
55
|
+
return "", fmt.Errorf("unable to sign the access token: %w", err)
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
return s, nil
|
package/go/auth/models.go
CHANGED
|
@@ -2,11 +2,11 @@ package auth
|
|
|
2
2
|
|
|
3
3
|
import "time"
|
|
4
4
|
|
|
5
|
-
//
|
|
6
|
-
const
|
|
5
|
+
// DefaultServiceAccessTokenDuration is the default duration for service access tokens.
|
|
6
|
+
const DefaultServiceAccessTokenDuration = 60 * time.Minute
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
const
|
|
8
|
+
// DefaultUserAccessTokenDuration is the default duration for user access tokens.
|
|
9
|
+
const DefaultUserAccessTokenDuration = 15 * time.Minute
|
|
10
10
|
|
|
11
11
|
// DefaultServiceRefreshTokenDuration is the default duration for refresh tokens.
|
|
12
12
|
const DefaultServiceRefreshTokenDuration = 7 * 24 * time.Hour
|
|
@@ -15,8 +15,8 @@ import (
|
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
// GetAuthMiddleware returns a middleware function that authenticates all requests, except for the login endpoints.
|
|
18
|
-
// It adds the token to the request context.
|
|
19
|
-
func GetAuthMiddleware(
|
|
18
|
+
// It adds the JWT access token to the request context.
|
|
19
|
+
func GetAuthMiddleware(accessTokenSecret string) func(http.Handler) http.Handler {
|
|
20
20
|
write := write.NewWrite("ERROR", log.NewLog().With("handler", "GetAuthMiddleware"))
|
|
21
21
|
|
|
22
22
|
return func(next http.Handler) http.Handler {
|
|
@@ -35,15 +35,15 @@ func GetAuthMiddleware(secret string) func(http.Handler) http.Handler {
|
|
|
35
35
|
return
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
// validate the token
|
|
39
|
-
|
|
38
|
+
// validate the access token
|
|
39
|
+
accessToken, err := auth.ValidateToken(arr[1], accessTokenSecret)
|
|
40
40
|
if err != nil {
|
|
41
|
-
write.ResponseWithError(r.Context(), w, http.StatusUnauthorized, "invalid token")
|
|
41
|
+
write.ResponseWithError(r.Context(), w, http.StatusUnauthorized, "invalid access token")
|
|
42
42
|
return
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
// set the token in the request context
|
|
46
|
-
ctx := context.WithValue(r.Context(), models.
|
|
45
|
+
// set the access token in the request context
|
|
46
|
+
ctx := context.WithValue(r.Context(), models.AccessTokenKey, accessToken)
|
|
47
47
|
r = r.WithContext(ctx)
|
|
48
48
|
|
|
49
49
|
next.ServeHTTP(w, r)
|
package/go/models/constants.go
CHANGED
|
@@ -4,8 +4,8 @@ package models
|
|
|
4
4
|
type ContextKey string
|
|
5
5
|
|
|
6
6
|
const (
|
|
7
|
-
//
|
|
8
|
-
|
|
7
|
+
// AccessTokenKey is the key used to set/get the JWT access token in the request context.
|
|
8
|
+
AccessTokenKey = ContextKey("accessToken")
|
|
9
9
|
// RequestPathKey is the key used to set/get the request path in the request context.
|
|
10
10
|
RequestPathKey = ContextKey("requestPath")
|
|
11
11
|
// RequestMethodKey is the key used to set/get the request method in the request context.
|
|
@@ -17,6 +17,10 @@ const (
|
|
|
17
17
|
type TokenType string
|
|
18
18
|
|
|
19
19
|
const (
|
|
20
|
-
|
|
20
|
+
// RefreshToken represents a standard JWT refresh token.
|
|
21
|
+
RefreshToken TokenType = "refresh"
|
|
22
|
+
// UserToken represents a JWT access token for a user.
|
|
23
|
+
UserToken TokenType = "user"
|
|
24
|
+
// ServiceToken represents a JWT access token for a service.
|
|
21
25
|
ServiceToken TokenType = "service"
|
|
22
26
|
)
|
|
@@ -11,66 +11,66 @@ import (
|
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
type AuthTransport struct {
|
|
14
|
-
mu
|
|
15
|
-
transport
|
|
16
|
-
service
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
mu sync.RWMutex
|
|
15
|
+
transport http.RoundTripper
|
|
16
|
+
service string
|
|
17
|
+
accessTokenSecret string
|
|
18
|
+
accessToken *jwt.Token
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
func NewAuthTransport(transport http.RoundTripper, service string,
|
|
21
|
+
func NewAuthTransport(transport http.RoundTripper, service string, accessTokenSecret string) *AuthTransport {
|
|
22
22
|
return &AuthTransport{
|
|
23
|
-
mu:
|
|
24
|
-
transport:
|
|
25
|
-
service:
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
mu: sync.RWMutex{},
|
|
24
|
+
transport: transport,
|
|
25
|
+
service: service,
|
|
26
|
+
accessTokenSecret: accessTokenSecret,
|
|
27
|
+
accessToken: nil,
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
func (t *AuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
32
|
-
// get the token
|
|
32
|
+
// get the access token
|
|
33
33
|
t.mu.RLock()
|
|
34
|
-
|
|
34
|
+
accessToken := t.accessToken
|
|
35
35
|
t.mu.RUnlock()
|
|
36
36
|
|
|
37
|
-
// get the token expiration time
|
|
38
|
-
expirationTime := time.Time{}
|
|
39
|
-
if
|
|
40
|
-
e, err :=
|
|
37
|
+
// get the access token expiration time
|
|
38
|
+
expirationTime := jwt.NewNumericDate(time.Time{})
|
|
39
|
+
if accessToken != nil {
|
|
40
|
+
e, err := accessToken.Claims.GetExpirationTime()
|
|
41
41
|
if err != nil {
|
|
42
|
-
return nil, fmt.Errorf("failed to get the expiration time from the token: %w", err)
|
|
42
|
+
return nil, fmt.Errorf("failed to get the expiration time from the access token: %w", err)
|
|
43
43
|
}
|
|
44
|
-
expirationTime = e
|
|
44
|
+
expirationTime = e
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
// check if the token is expired or close to expiring
|
|
47
|
+
// check if the access token is expired or close to expiring
|
|
48
48
|
if expirationTime.Before(time.Now().Add(time.Minute)) {
|
|
49
|
-
err := t.
|
|
49
|
+
err := t.setNewAccessToken()
|
|
50
50
|
if err != nil {
|
|
51
|
-
return nil, fmt.Errorf("failed to set a new token: %w", err)
|
|
51
|
+
return nil, fmt.Errorf("failed to set a new access token: %w", err)
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// perform the request
|
|
56
|
-
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", t.
|
|
56
|
+
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", t.accessToken.Raw))
|
|
57
57
|
resp, err := t.transport.RoundTrip(req)
|
|
58
58
|
if err != nil {
|
|
59
59
|
return nil, err
|
|
60
60
|
}
|
|
61
61
|
|
|
62
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.
|
|
63
|
+
// this should ideally not happen since we are setting a new access token if the current one is expired or close to expiring,
|
|
64
|
+
// but it is still possible for the access token to be revoked.
|
|
65
65
|
if resp != nil && resp.StatusCode == http.StatusUnauthorized {
|
|
66
|
-
// set a new token
|
|
67
|
-
err := t.
|
|
66
|
+
// set a new access token
|
|
67
|
+
err := t.setNewAccessToken()
|
|
68
68
|
if err != nil {
|
|
69
|
-
return nil, fmt.Errorf("failed to set a new token: %w", err)
|
|
69
|
+
return nil, fmt.Errorf("failed to set a new access token: %w", err)
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// retry the request
|
|
73
|
-
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", t.
|
|
73
|
+
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", t.accessToken.Raw))
|
|
74
74
|
return t.transport.RoundTrip(req)
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -78,24 +78,24 @@ func (t *AuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
|
78
78
|
return resp, err
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
func (t *AuthTransport)
|
|
81
|
+
func (t *AuthTransport) setNewAccessToken() error {
|
|
82
82
|
t.mu.Lock()
|
|
83
83
|
defer t.mu.Unlock()
|
|
84
84
|
|
|
85
|
-
// create a new service token
|
|
86
|
-
|
|
85
|
+
// create a new service access token
|
|
86
|
+
rawAccessToken, err := auth.NewServiceAccessToken(t.accessTokenSecret, t.service, t.service, auth.DefaultServiceAccessTokenDuration)
|
|
87
87
|
if err != nil {
|
|
88
|
-
return fmt.Errorf("failed to create a new service token: %w", err)
|
|
88
|
+
return fmt.Errorf("failed to create a new service access token: %w", err)
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
// validate the token
|
|
92
|
-
|
|
91
|
+
// validate the access token
|
|
92
|
+
accessToken, err := auth.ValidateToken(rawAccessToken, t.accessTokenSecret)
|
|
93
93
|
if err != nil {
|
|
94
|
-
return fmt.Errorf("failed to validate the new token: %w", err)
|
|
94
|
+
return fmt.Errorf("failed to validate the new access token: %w", err)
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
// set the new token
|
|
98
|
-
t.
|
|
97
|
+
// set the new access token
|
|
98
|
+
t.accessToken = accessToken
|
|
99
99
|
|
|
100
100
|
return nil
|
|
101
101
|
}
|
package/go/request/http.go
CHANGED
|
@@ -3,9 +3,9 @@ package request
|
|
|
3
3
|
import "net/http"
|
|
4
4
|
|
|
5
5
|
// NewHTTPClientWithCommonTransports creates a new HTTP client with commonly shared transports.
|
|
6
|
-
func NewHTTPClientWithCommonTransports(service string,
|
|
6
|
+
func NewHTTPClientWithCommonTransports(service string, accessTokenSecret string) *http.Client {
|
|
7
7
|
//exhaustruct:ignore - other fields are optional
|
|
8
8
|
return &http.Client{
|
|
9
|
-
Transport: NewAuthTransport(http.DefaultTransport, service,
|
|
9
|
+
Transport: NewAuthTransport(http.DefaultTransport, service, accessTokenSecret),
|
|
10
10
|
}
|
|
11
11
|
}
|
package/go/utils/context.go
CHANGED
|
@@ -9,49 +9,49 @@ import (
|
|
|
9
9
|
"github.com/golang-jwt/jwt/v5"
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
//
|
|
13
|
-
func
|
|
14
|
-
token, ok := ctx.Value(models.
|
|
12
|
+
// GetAccessTokenFromContext retrieves the JWT access token from the given context.
|
|
13
|
+
func GetAccessTokenFromContext(ctx context.Context) *jwt.Token {
|
|
14
|
+
token, ok := ctx.Value(models.AccessTokenKey).(*jwt.Token)
|
|
15
15
|
if !ok {
|
|
16
16
|
return nil
|
|
17
17
|
}
|
|
18
18
|
return token
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
// GetActorIdFromContext retrieves the actor id from the
|
|
22
|
-
// The actor id is the subject of the JWT token
|
|
21
|
+
// GetActorIdFromContext retrieves the actor id from the JWT access token in the given context.
|
|
22
|
+
// The actor id is the subject of the JWT access token that represents the user id or service name that initiated the request.
|
|
23
23
|
func GetActorIdFromContext(ctx context.Context) (string, error) {
|
|
24
|
-
// get the token from the context
|
|
25
|
-
|
|
26
|
-
if
|
|
27
|
-
return "", errors.New("token not found in the context")
|
|
24
|
+
// get the access token from the context
|
|
25
|
+
accessToken := GetAccessTokenFromContext(ctx)
|
|
26
|
+
if accessToken == nil {
|
|
27
|
+
return "", errors.New("access token not found in the context")
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
// get the subject from the token
|
|
31
|
-
subject, err :=
|
|
30
|
+
// get the subject from the access token
|
|
31
|
+
subject, err := accessToken.Claims.GetSubject()
|
|
32
32
|
if err != nil {
|
|
33
|
-
return "", fmt.Errorf("failed to get the subject from the token: %w", err)
|
|
33
|
+
return "", fmt.Errorf("failed to get the subject from the access token: %w", err)
|
|
34
34
|
}
|
|
35
35
|
if subject == "" {
|
|
36
|
-
return "", errors.New("subject is empty in the token")
|
|
36
|
+
return "", errors.New("subject is empty in the access token")
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
return subject, nil
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
// GetTokenTypeFromContext retrieves the token type from the JWT token in the given context.
|
|
43
|
-
// The token type is a custom claim in the JWT token
|
|
42
|
+
// GetTokenTypeFromContext retrieves the token type from the JWT access token in the given context.
|
|
43
|
+
// The token type is a custom claim in the JWT access token that represents the type of the token (e.g. user or service).
|
|
44
44
|
func GetTokenTypeFromContext(ctx context.Context) (string, error) {
|
|
45
|
-
// get the token from the context
|
|
46
|
-
|
|
47
|
-
if
|
|
48
|
-
return "", errors.New("token not found in the context")
|
|
45
|
+
// get the access token from the context
|
|
46
|
+
accessToken := GetAccessTokenFromContext(ctx)
|
|
47
|
+
if accessToken == nil {
|
|
48
|
+
return "", errors.New("access token not found in the context")
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
// get the token type from the token
|
|
52
|
-
tokenType, ok :=
|
|
51
|
+
// get the token type from the access token
|
|
52
|
+
tokenType, ok := accessToken.Claims.(jwt.MapClaims)["type"].(string)
|
|
53
53
|
if !ok {
|
|
54
|
-
return "", errors.New("token type not found in the token")
|
|
54
|
+
return "", errors.New("token type not found in the access token")
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
return tokenType, nil
|
package/package.json
CHANGED
|
@@ -279,11 +279,15 @@ components:
|
|
|
279
279
|
application/json:
|
|
280
280
|
schema:
|
|
281
281
|
properties:
|
|
282
|
-
|
|
282
|
+
accessToken:
|
|
283
|
+
type: string
|
|
284
|
+
example: ABCD.1234.abcd
|
|
285
|
+
refreshToken:
|
|
283
286
|
type: string
|
|
284
287
|
example: ABCD.1234.abcd
|
|
285
288
|
required:
|
|
286
|
-
-
|
|
289
|
+
- accessToken
|
|
290
|
+
- refreshToken
|
|
287
291
|
|
|
288
292
|
NoContent:
|
|
289
293
|
description: No content
|