-
Notifications
You must be signed in to change notification settings - Fork 1
/
tokens_service.go
142 lines (120 loc) · 3.75 KB
/
tokens_service.go
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
package warrant
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/golang-jwt/jwt"
"github.com/pivotal-cf-experimental/warrant/internal/documents"
"github.com/pivotal-cf-experimental/warrant/internal/network"
)
// TokensService provides access to common token actions. Using this service,
// you can decode a token and fetch the signing key to validate a token.
type TokensService struct {
config Config
}
// SigningKey is the representation of the key used to validate a token.
type SigningKey struct {
// id for the signing key
KeyId string
// Algorithm indicates the kind of key used to sign tokens.
// Keys can be either symmetric or asymmetric.
Algorithm string
// Value is a string representation of the key. In the case of a symmetric key,
// this is the shared secret value. for asymmetric keys, this is the public key
// of the keypair.
Value string
}
// NewTokensService returns a TokensService initialized with the given Config.
func NewTokensService(config Config) TokensService {
return TokensService{
config: config,
}
}
// Decode returns a decoded token value. The returned value represents the
// token's claims section.
func (ts TokensService) Decode(token string) (Token, error) {
segments := strings.Split(token, ".")
if len(segments) != 3 {
return Token{}, InvalidTokenError{fmt.Errorf("invalid number of segments in token (%d/3)", len(segments))}
}
headerSegment, err := jwt.DecodeSegment(segments[0])
if err != nil {
return Token{}, InvalidTokenError{fmt.Errorf("header cannot be decoded: %s", err)}
}
var header struct {
Alg string `json:"alg"`
KeyID string `json:"kid"`
}
err = json.Unmarshal(headerSegment, &header)
if err != nil {
return Token{}, InvalidTokenError{fmt.Errorf("header cannot be parsed: %s", err)}
}
claims, err := jwt.DecodeSegment(segments[1])
if err != nil {
return Token{}, InvalidTokenError{fmt.Errorf("claims cannot be decoded: %s", err)}
}
t := Token{
Algorithm: header.Alg,
KeyID: header.KeyID,
Segments: TokenSegments{
Header: segments[0],
Claims: segments[1],
Signature: segments[2],
},
}
err = json.Unmarshal(claims, &t)
if err != nil {
return Token{}, InvalidTokenError{fmt.Errorf("token cannot be parsed: %s", err)}
}
return t, nil
}
// GetSigningKey makes a request to UAA to retrieve the SigningKey used to
// generate valid tokens.
func (ts TokensService) GetSigningKey() (SigningKey, error) {
resp, err := newNetworkClient(ts.config).MakeRequest(network.Request{
Method: "GET",
Path: "/token_key",
AcceptableStatusCodes: []int{http.StatusOK},
})
if err != nil {
return SigningKey{}, translateError(err)
}
var response documents.TokenKeyResponse
err = json.Unmarshal(resp.Body, &response)
if err != nil {
return SigningKey{}, MalformedResponseError{err}
}
key := SigningKey{
KeyId: response.Kid,
Algorithm: response.Alg,
Value: response.Value,
}
return key, nil
}
// GetSigningKeys makes a request to UAA to retrieve the SigningKeys used to
// generate valid tokens.
func (ts TokensService) GetSigningKeys() ([]SigningKey, error) {
resp, err := newNetworkClient(ts.config).MakeRequest(network.Request{
Method: "GET",
Path: "/token_keys",
AcceptableStatusCodes: []int{http.StatusOK},
})
if err != nil {
return []SigningKey{}, translateError(err)
}
var response documents.TokenKeysResponse
err = json.Unmarshal(resp.Body, &response)
if err != nil {
return []SigningKey{}, MalformedResponseError{err}
}
signingKeys := make([]SigningKey, 0, len(response.Keys))
for _, key := range response.Keys {
signingKeys = append(signingKeys, SigningKey{
KeyId: key.Kid,
Algorithm: key.Alg,
Value: key.Value,
})
}
return signingKeys, nil
}