/
ticket_jwt.go
154 lines (131 loc) · 3.38 KB
/
ticket_jwt.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
143
144
145
146
147
148
149
150
151
152
153
154
package types
import (
"encoding/base64"
"encoding/json"
fmt "fmt"
"strings"
gtime "time"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/golang-jwt/jwt/v4"
"github.com/tendermint/tendermint/types/time"
)
// jwtTicket is the Ticket implementer.
type jwtTicket struct {
value string
header string
payload string
signature string
exp time.WeightedTime
clm *jwt.RegisteredClaims
}
// NewJwtTicket create a new jwt ticket from the given ticket.
func NewJwtTicket(ticketStr string) (Ticket, error) {
var err error
t := jwtTicket{
value: ticketStr,
}
err = t.initFromValue()
if err != nil {
return nil, err
}
return &t, nil
}
// Unmarshal the information of the ticket to the v. v must be a pointer.
func (t *jwtTicket) Unmarshal(v interface{}) error {
// data = json.Unmarshal(base64.Decode(payload))
var err error
bs, err := base64.RawURLEncoding.DecodeString(t.payload)
if err != nil {
return err
}
err = json.Unmarshal(bs, v)
if err != nil {
return err
}
return nil
}
// Verify verifies the ticket signature with the given public key. if the ticket is verified by the
// the key, then return nil else return invalid signature error
func (t *jwtTicket) Verify(key string) error {
_, err := t.verifyJwtKey(key)
if err == nil {
return nil
}
if err != ErrInvalidSignature {
return err
}
return ErrInvalidSignature
}
// VerifyAny verifies the ticket signature with the given public keys. if the ticket is verified by the
// the key, then return nil else return invalid signature error
func (t *jwtTicket) VerifyAny(keys []string) error {
verified := false
for _, pubKey := range keys {
if err := t.Verify(pubKey); err == nil {
// if no error, this means that ticked verified with this public key
verified = true
break
}
}
if !verified {
return ErrInvalidSignature
}
return nil
}
func (t *jwtTicket) ValidateExpiry(ctx sdk.Context) error {
// validate the expiration
if !t.exp.Time.After(ctx.BlockTime()) {
return ErrTicketExpired
}
return nil
}
// initFromValue initializes the ticket from the raw value.few validation happening over this process.
func (t *jwtTicket) initFromValue() error {
var err error
ts := strings.Split(t.value, JWTSeparator)
if len(ts) < 3 {
return ErrInvalidTicketFormat
}
t.header = ts[JWTHeaderIndex]
t.payload = ts[JWTPayloadIndex]
t.signature = ts[JWTPayloadIndex+1]
clm := jwt.RegisteredClaims{}
err = t.Unmarshal(&clm)
if err != nil {
return err
}
t.clm = &clm
if t.clm.ExpiresAt == nil {
return ErrExpirationRequired
}
gt := gtime.Unix(t.clm.ExpiresAt.Unix(), 0)
t.exp = *time.NewWeightedTime(gt, DefaultTimeWeight)
return nil
}
// verifyJwtKey verify a Ticket with the key
func (t *jwtTicket) verifyJwtKey(key string) (bool, error) {
token := t.header + "." + t.payload + "." + t.signature
parser := jwt.NewParser(
jwt.WithoutClaimsValidation(),
)
parsedToken, err := parser.Parse(token, func(t *jwt.Token) (interface{}, error) {
parsedPubKey, err := jwt.ParseEdPublicKeyFromPEM([]byte(key))
if err != nil {
return nil, err
}
return parsedPubKey, nil
})
if err != nil {
return false, err
}
if parsedToken.Valid {
return true, nil
}
return false, ErrInvalidSignature
}
func IsValidJwtToken(s string) error {
if _, err := jwt.ParseEdPublicKeyFromPEM([]byte(s)); err != nil {
return fmt.Errorf("public key %s is not valid jwt token %s", s, err)
}
return nil
}