This repository has been archived by the owner on Jul 7, 2020. It is now read-only.
forked from gobuffalo/buffalo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tokenauth.go
152 lines (136 loc) · 4.43 KB
/
tokenauth.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
// Package tokenauth provides jwt token authorisation middleware
// supports HMAC, RSA, ECDSA, RSAPSS algorithms
// uses github.com/dgrijalva/jwt-go for jwt implementation
package tokenauth
import (
"io/ioutil"
"log"
"net/http"
"strings"
"github.com/dgrijalva/jwt-go"
"github.com/gobuffalo/envy"
"github.com/gobuffalo/buffalo"
"github.com/pkg/errors"
)
var (
// ErrTokenInvalid is returned when the token provided is invalid
ErrTokenInvalid = errors.New("token invalid")
// ErrNoToken is returned if no token is supplied in the request.
ErrNoToken = errors.New("token not found in request")
// ErrBadSigningMethod is returned if the token sign method in the request
// does not match the signing method used
ErrBadSigningMethod = errors.New("unexpected signing method")
)
// Options for the JWT middleware
type Options struct {
SignMethod jwt.SigningMethod
GetKey func(jwt.SigningMethod) (interface{}, error)
}
// New enables jwt token verification if no Sign method is provided,
// by default uses HMAC
func New(options Options) buffalo.MiddlewareFunc {
// set sign method to HMAC if not provided
if options.SignMethod == nil {
options.SignMethod = jwt.SigningMethodHS256
}
if options.GetKey == nil {
options.GetKey = selectGetKeyFunc(options.SignMethod)
}
// get key for validation
key, err := options.GetKey(options.SignMethod)
// if error on getting key exit.
if err != nil {
log.Fatal(errors.Wrap(err, "couldn't get key"))
}
return func(next buffalo.Handler) buffalo.Handler {
return func(c buffalo.Context) error {
// get Authorisation header value
authString := c.Request().Header.Get("Authorization")
tokenString, err := getJwtToken(authString)
// if error on getting the token, return with status unauthorized
if err != nil {
return c.Error(http.StatusUnauthorized, err)
}
// validating and parsing the tokenString
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
// Validating if algorithm used for signing is same as the algorithm in token
if token.Method.Alg() != options.SignMethod.Alg() {
return nil, ErrBadSigningMethod
}
return key, nil
})
// if error validating jwt token, return with status unauthorized
if err != nil {
return c.Error(http.StatusUnauthorized, err)
}
// set the claims as context parameter.
// so that the actions can use the claims from jwt token
c.Set("claims", token.Claims)
// calling next handler
err = next(c)
return err
}
}
}
// selectGetKeyFunc is an helper function to choose the GetKey function
// according to the Signing method used
func selectGetKeyFunc(method jwt.SigningMethod) func(jwt.SigningMethod) (interface{}, error) {
switch method.(type) {
case *jwt.SigningMethodRSA:
return GetKeyRSA
case *jwt.SigningMethodECDSA:
return GetKeyECDSA
case *jwt.SigningMethodRSAPSS:
return GetKeyRSAPSS
default:
return GetHMACKey
}
}
// GetHMACKey gets secret key from env
func GetHMACKey(jwt.SigningMethod) (interface{}, error) {
key, err := envy.MustGet("JWT_SECRET")
return []byte(key), err
}
// GetKeyRSA gets the public key file location from env and returns rsa.PublicKey
func GetKeyRSA(jwt.SigningMethod) (interface{}, error) {
key, err := envy.MustGet("JWT_PUBLIC_KEY")
if err != nil {
return nil, err
}
keyData, err := ioutil.ReadFile(key)
if err != nil {
return nil, err
}
return jwt.ParseRSAPublicKeyFromPEM(keyData)
}
// GetKeyRSAPSS uses GetKeyRSA() since both requires rsa.PublicKey
func GetKeyRSAPSS(signingMethod jwt.SigningMethod) (interface{}, error) {
return GetKeyRSA(signingMethod)
}
// GetKeyECDSA gets the public.pem file location from env and returns ecdsa.PublicKey
func GetKeyECDSA(jwt.SigningMethod) (interface{}, error) {
key, err := envy.MustGet("JWT_PUBLIC_KEY")
if err != nil {
return nil, err
}
keyData, err := ioutil.ReadFile(key)
if err != nil {
return nil, err
}
return jwt.ParseECPublicKeyFromPEM(keyData)
}
// getJwtToken gets the token from the Authorisation header
// removes the Bearer part from the authorisation header value.
// returns No token error if Token is not found
// returns Token Invalid error if the token value cannot be obtained by removing `Bearer `
func getJwtToken(authString string) (string, error) {
if authString == "" {
return "", ErrNoToken
}
splitToken := strings.Split(authString, "Bearer ")
if len(splitToken) != 2 {
return "", ErrTokenInvalid
}
tokenString := splitToken[1]
return tokenString, nil
}