This repository has been archived by the owner on Dec 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 84
/
jwt.go
123 lines (107 loc) · 3.49 KB
/
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
// Copyright 2015-present Oursky Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package authtoken
import (
"errors"
"time"
jwt "github.com/dgrijalva/jwt-go"
"github.com/skygeario/skygear-server/pkg/server/uuid"
)
// JWTStore implements TokenStore by encoding user information into
// the access token string. This store does not keep state.
type JWTStore struct {
secret string
expiry int64
}
// NewJWTStore creates a JWT token store.
func NewJWTStore(secret string, expiry int64) *JWTStore {
if secret == "" {
panic("jwt store is not configured with a secret")
}
store := JWTStore{
secret: secret,
expiry: expiry,
}
return &store
}
// NewToken creates a new token for this token store.
func (r *JWTStore) NewToken(appName string, userInfoID string) (Token, error) {
claims := jwt.StandardClaims{
Id: uuid.New(),
IssuedAt: time.Now().Unix(),
Issuer: appName,
Subject: userInfoID,
}
if r.expiry > 0 {
claims.ExpiresAt = time.Now().Unix() + r.expiry
}
jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
signedString, err := jwtToken.SignedString([]byte(r.secret))
if err != nil {
return Token{}, err
}
token := Token{}
r.setTokenFromClaims(claims, &token)
token.AccessToken = signedString
return token, nil
}
// Get decodes and verifies the access token for user information. It returns
// the access token containing information about the user.
func (r *JWTStore) Get(accessToken string, token *Token) error {
claims := jwt.StandardClaims{}
jwtToken, err := jwt.ParseWithClaims(accessToken, &claims, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, &NotFoundError{accessToken, errors.New("unexpected algorithm in token")}
}
return []byte(r.secret), nil
})
if err != nil {
return &NotFoundError{accessToken, err}
}
if jwtToken.Valid {
r.setTokenFromClaims(claims, token)
} else {
return &NotFoundError{accessToken, errors.New("invalid token")}
}
// The token is considered valid by the JWTStore. (i.e. the token
// has a valid signature and the signature is verified with the secret.)
//
// In skygear-server, the `InjectUserIfPresent` preprocessor is
// responsible for checking if the token is still valid. A token
// might become invalid if the user has changed the password after the
// token is generated.
return nil
}
func (r *JWTStore) setTokenFromClaims(claims jwt.StandardClaims, token *Token) {
if claims.ExpiresAt > 0 {
token.ExpiredAt = time.Unix(claims.ExpiresAt, 0)
} else {
token.ExpiredAt = time.Time{}
}
if claims.IssuedAt > 0 {
token.issuedAt = time.Unix(claims.IssuedAt, 0)
} else {
token.issuedAt = time.Time{}
}
token.AppName = claims.Issuer
token.UserInfoID = claims.Subject
}
// Put does nothing because the JWT token store does not store token.
func (r *JWTStore) Put(token *Token) error {
return nil
}
// Delete does nothing because the JWT token store does not store token.
func (r *JWTStore) Delete(accessToken string) error {
return nil
}