-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkeys.go
More file actions
143 lines (126 loc) · 4.11 KB
/
keys.go
File metadata and controls
143 lines (126 loc) · 4.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package auth
import (
"context"
"encoding/base64"
"errors"
"fmt"
"github.com/bottledcode/durable-php/cli/appcontext"
"github.com/bottledcode/durable-php/cli/config"
"github.com/golang-jwt/jwt/v4"
"net/http"
"strings"
"time"
)
// Get the last secret key from the config
func getActiveKey(config *config.Config) ([]byte, error) {
if len(config.Extensions.Authz.Secrets) == 0 {
return nil, errors.New("no secrets given in config")
}
key := config.Extensions.Authz.Secrets[len(config.Extensions.Authz.Secrets)-1]
decoded, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return nil, err
}
return decoded, nil
}
// DecorateContextWithUser takes a context and a user object and decorates the context with the user information.
func DecorateContextWithUser(ctx context.Context, user *User) context.Context {
if user == nil {
return ctx
}
return context.WithValue(ctx, appcontext.CurrentUserKey, user)
}
func GetUserFromContext(ctx context.Context) *User {
user, _ := ctx.Value(appcontext.CurrentUserKey).(*User)
return user
}
// ExtractUser extracts user information from the Authorization token in the HTTP request header.
// It returns the user and a boolean indicating if the extraction was successful.
//
// It expects the token in the "Bearer" format in the Authorization header.
// The token is parsed using the jwt.Parse function, which verifies the token's signing method and validity.
//
// If the token is valid and the necessary claims are present, ExtractUser constructs a User struct
// with the UserId and Roles from the token claims.
//
// The getRoles function converts the roles from the token claims to a slice of Role types.
//
// Parameters:
// - r: The *http.Request containing the Authorization header with the token.
// - config: The *config.Config object containing the necessary configuration data.
//
// Returns:
// - user: A pointer to the extracted User object if the extraction was successful, otherwise nil.
// - ok: A boolean indicating if the extraction was successful (true) or not (false).
func ExtractUser(r *http.Request, config *config.Config) (user *User, ok bool) {
tokenString := r.Header.Get("Authorization")
if tokenString == "" {
return nil, false
}
tokenParts := strings.SplitN(tokenString, " ", 2)
if tokenParts[0] != "Bearer" {
return nil, false
}
token, err := jwt.Parse(tokenParts[1], func(token *jwt.Token) (interface{}, error) {
if token.Method.Alg() != jwt.SigningMethodHS256.Alg() {
return nil, fmt.Errorf("unexpected signing method")
}
key, err := getActiveKey(config)
if err != nil {
return "", err
}
return key, nil
}, jwt.WithValidMethods([]string{"HS256"}))
if err != nil {
return nil, false
}
getRoles := func(roles []interface{}) []Role {
rolesSlice := make([]Role, len(roles))
for i, r := range roles {
rolesSlice[i] = Role(r.(string))
}
return rolesSlice
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
userId, ok := claims["sub"].(string)
if !ok {
return nil, false
}
rol, ok := claims["roles"].([]interface{})
if !ok {
return nil, false
}
return &User{
UserId: UserId(userId),
Roles: getRoles(rol),
}, true
}
return nil, false
}
// CreateUser creates a new JWT token with the specified userID, roles, and configuration.
// The token is signed using the active secret key from the config.
// The token will expire in 72 hours and is valid starting from 5 minutes ago.
// Returns the signed token string or an error if the signing process fails.
func CreateUser(userId UserId, role []Role, claims map[string]interface{}, config *config.Config) (string, error) {
claimMap := jwt.MapClaims{
"sub": userId,
"exp": time.Now().Add(72 * time.Hour).Unix(),
"iat": time.Now().Add(-5 * time.Minute).Unix(),
"nbf": time.Now().Add(-5 * time.Minute).Unix(),
"roles": role,
}
for k, v := range claims {
k = strings.TrimSpace(k)
claimMap[k] = v
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claimMap)
key, err := getActiveKey(config)
if err != nil {
return "", err
}
signedString, err := token.SignedString(key)
if err != nil {
return "", err
}
return signedString, nil
}