github.com/jacobsoderblom/buffalo@v0.11.0/middleware/tokenauth/tokenauth.go (about) 1 // Package tokenauth provides jwt token authorisation middleware 2 // supports HMAC, RSA, ECDSA, RSAPSS algorithms 3 // uses github.com/dgrijalva/jwt-go for jwt implementation 4 package tokenauth 5 6 import ( 7 "io/ioutil" 8 "log" 9 "net/http" 10 "strings" 11 12 "github.com/dgrijalva/jwt-go" 13 "github.com/gobuffalo/envy" 14 15 "github.com/gobuffalo/buffalo" 16 "github.com/pkg/errors" 17 ) 18 19 var ( 20 // ErrTokenInvalid is returned when the token provided is invalid 21 ErrTokenInvalid = errors.New("token invalid") 22 // ErrNoToken is returned if no token is supplied in the request. 23 ErrNoToken = errors.New("token not found in request") 24 // ErrBadSigningMethod is returned if the token sign method in the request 25 // does not match the signing method used 26 ErrBadSigningMethod = errors.New("unexpected signing method") 27 ) 28 29 // Options for the JWT middleware 30 type Options struct { 31 SignMethod jwt.SigningMethod 32 GetKey func(jwt.SigningMethod) (interface{}, error) 33 } 34 35 // New enables jwt token verification if no Sign method is provided, 36 // by default uses HMAC 37 func New(options Options) buffalo.MiddlewareFunc { 38 // set sign method to HMAC if not provided 39 if options.SignMethod == nil { 40 options.SignMethod = jwt.SigningMethodHS256 41 } 42 if options.GetKey == nil { 43 options.GetKey = selectGetKeyFunc(options.SignMethod) 44 } 45 // get key for validation 46 key, err := options.GetKey(options.SignMethod) 47 // if error on getting key exit. 48 if err != nil { 49 log.Fatal(errors.Wrap(err, "couldn't get key")) 50 } 51 return func(next buffalo.Handler) buffalo.Handler { 52 return func(c buffalo.Context) error { 53 // get Authorisation header value 54 authString := c.Request().Header.Get("Authorization") 55 56 tokenString, err := getJwtToken(authString) 57 // if error on getting the token, return with status unauthorized 58 if err != nil { 59 return c.Error(http.StatusUnauthorized, err) 60 } 61 62 // validating and parsing the tokenString 63 token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { 64 // Validating if algorithm used for signing is same as the algorithm in token 65 if token.Method.Alg() != options.SignMethod.Alg() { 66 return nil, ErrBadSigningMethod 67 } 68 return key, nil 69 }) 70 // if error validating jwt token, return with status unauthorized 71 if err != nil { 72 return c.Error(http.StatusUnauthorized, err) 73 } 74 75 // set the claims as context parameter. 76 // so that the actions can use the claims from jwt token 77 c.Set("claims", token.Claims) 78 // calling next handler 79 err = next(c) 80 81 return err 82 } 83 } 84 } 85 86 // selectGetKeyFunc is an helper function to choose the GetKey function 87 // according to the Signing method used 88 func selectGetKeyFunc(method jwt.SigningMethod) func(jwt.SigningMethod) (interface{}, error) { 89 switch method.(type) { 90 case *jwt.SigningMethodRSA: 91 return GetKeyRSA 92 case *jwt.SigningMethodECDSA: 93 return GetKeyECDSA 94 case *jwt.SigningMethodRSAPSS: 95 return GetKeyRSAPSS 96 default: 97 return GetHMACKey 98 } 99 } 100 101 // GetHMACKey gets secret key from env 102 func GetHMACKey(jwt.SigningMethod) (interface{}, error) { 103 key, err := envy.MustGet("JWT_SECRET") 104 return []byte(key), err 105 } 106 107 // GetKeyRSA gets the public key file location from env and returns rsa.PublicKey 108 func GetKeyRSA(jwt.SigningMethod) (interface{}, error) { 109 key, err := envy.MustGet("JWT_PUBLIC_KEY") 110 if err != nil { 111 return nil, err 112 } 113 keyData, err := ioutil.ReadFile(key) 114 if err != nil { 115 return nil, err 116 } 117 return jwt.ParseRSAPublicKeyFromPEM(keyData) 118 } 119 120 // GetKeyRSAPSS uses GetKeyRSA() since both requires rsa.PublicKey 121 func GetKeyRSAPSS(signingMethod jwt.SigningMethod) (interface{}, error) { 122 return GetKeyRSA(signingMethod) 123 } 124 125 // GetKeyECDSA gets the public.pem file location from env and returns ecdsa.PublicKey 126 func GetKeyECDSA(jwt.SigningMethod) (interface{}, error) { 127 key, err := envy.MustGet("JWT_PUBLIC_KEY") 128 if err != nil { 129 return nil, err 130 } 131 keyData, err := ioutil.ReadFile(key) 132 if err != nil { 133 return nil, err 134 } 135 return jwt.ParseECPublicKeyFromPEM(keyData) 136 } 137 138 // getJwtToken gets the token from the Authorisation header 139 // removes the Bearer part from the authorisation header value. 140 // returns No token error if Token is not found 141 // returns Token Invalid error if the token value cannot be obtained by removing `Bearer ` 142 func getJwtToken(authString string) (string, error) { 143 if authString == "" { 144 return "", ErrNoToken 145 } 146 splitToken := strings.Split(authString, "Bearer ") 147 if len(splitToken) != 2 { 148 return "", ErrTokenInvalid 149 } 150 tokenString := splitToken[1] 151 return tokenString, nil 152 }