Replace dgrijalva/jwt-go with thermokarst/jwt
This commit is contained in:
parent
c7f534271e
commit
77cb7828cf
30 changed files with 824 additions and 1528 deletions
9
Godeps/Godeps.json
generated
9
Godeps/Godeps.json
generated
|
@ -11,11 +11,6 @@
|
|||
"Comment": "1.2.0-93-g2bcd11f",
|
||||
"Rev": "2bcd11f863d540a1b190dc03b6c4f634c6ae91d4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/dgrijalva/jwt-go",
|
||||
"Comment": "v2.2.0-15-g61124b6",
|
||||
"Rev": "61124b62ad244d655f87d944aefaa2ae5a0d2f16"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/context",
|
||||
"Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd"
|
||||
|
@ -42,6 +37,10 @@
|
|||
"Comment": "go1.0-cutoff-29-g30ed220",
|
||||
"Rev": "30ed2200d7ec99cf17272292f1d4b7b0bd7165db"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/thermokarst/jwt",
|
||||
"Rev": "774185ba9ebde2a02689173b736b760a13a6b777"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/bcrypt",
|
||||
"Rev": "1351f936d976c60a0a48d728281922cf63eafb8d"
|
||||
|
|
4
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/.gitignore
generated
vendored
4
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/.gitignore
generated
vendored
|
@ -1,4 +0,0 @@
|
|||
.DS_Store
|
||||
bin
|
||||
|
||||
|
8
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/LICENSE
generated
vendored
8
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/LICENSE
generated
vendored
|
@ -1,8 +0,0 @@
|
|||
Copyright (c) 2012 Dave Grijalva
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
55
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/README.md
generated
vendored
55
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/README.md
generated
vendored
|
@ -1,55 +0,0 @@
|
|||
A [go](http://www.golang.org) (or 'golang' for search engine friendliness) implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-jones-json-web-token.html)
|
||||
|
||||
**NOTICE:** We recently introduced a breaking change in the API. Please refer to [VERSION_HISTORY.md](VERSION_HISTORY.md) for details.
|
||||
|
||||
## What the heck is a JWT?
|
||||
|
||||
In short, it's a signed JSON object that does something useful (for example, authentication). It's commonly used for `Bearer` tokens in Oauth 2. A token is made of three parts, separated by `.`'s. The first two parts are JSON objects, that have been [base64url](http://tools.ietf.org/html/rfc4648) encoded. The last part is the signature, encoded the same way.
|
||||
|
||||
The first part is called the header. It contains the necessary information for verifying the last part, the signature. For example, which encryption method was used for signing and what key was used.
|
||||
|
||||
The part in the middle is the interesting bit. It's called the Claims and contains the actual stuff you care about. Refer to [the RFC](http://self-issued.info/docs/draft-jones-json-web-token.html) for information about reserved keys and the proper way to add your own.
|
||||
|
||||
## What's in the box?
|
||||
|
||||
This library supports the parsing and verification as well as the generation and signing of JWTs. Current supported signing algorithms are RSA256 and HMAC SHA256, though hooks are present for adding your own.
|
||||
|
||||
## Parse and Verify
|
||||
|
||||
Parsing and verifying tokens is pretty straight forward. You pass in the token and a function for looking up the key. This is done as a callback since you may need to parse the token to find out what signing method and key was used.
|
||||
|
||||
```go
|
||||
token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface{}, error) {
|
||||
return myLookupKey(token.Header["kid"])
|
||||
})
|
||||
|
||||
if err == nil && token.Valid {
|
||||
deliverGoodness("!")
|
||||
} else {
|
||||
deliverUtterRejection(":(")
|
||||
}
|
||||
```
|
||||
|
||||
## Create a token
|
||||
|
||||
```go
|
||||
// Create the token
|
||||
token := jwt.New(jwt.SigningMethodHS256)
|
||||
// Set some claims
|
||||
token.Claims["foo"] = "bar"
|
||||
token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
|
||||
// Sign and get the complete encoded token as a string
|
||||
tokenString, err := token.SignedString(mySigningKey)
|
||||
```
|
||||
|
||||
## Project Status & Versioning
|
||||
|
||||
This library is considered production ready. Feedback and feature requests are appreciated. The API should be considered stable. There should be very few backwards-incompatible changes outside of major version updates (and only with good reason).
|
||||
|
||||
This project uses [Semantic Versioning 2.0.0](http://semver.org). Accepted pull requests will land on `master`. Periodically, versions will be tagged from `master`. You can find all the releases on [the project releases page](https://github.com/dgrijalva/jwt-go/releases).
|
||||
|
||||
## More
|
||||
|
||||
Documentation can be found [on godoc.org](http://godoc.org/github.com/dgrijalva/jwt-go).
|
||||
|
||||
The command line utility included in this project (cmd/jwt) provides a straightforward example of token creation and parsing as well as a useful tool for debugging your own integration. For a more http centric example, see [this gist](https://gist.github.com/cryptix/45c33ecf0ae54828e63b).
|
54
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md
generated
vendored
54
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/VERSION_HISTORY.md
generated
vendored
|
@ -1,54 +0,0 @@
|
|||
## `jwt-go` Version History
|
||||
|
||||
#### 2.2.0
|
||||
|
||||
* Gracefully handle a `nil` `Keyfunc` being passed to `Parse`. Result will now be the parsed token and an error, instead of a panic.
|
||||
|
||||
#### 2.1.0
|
||||
|
||||
Backwards compatible API change that was missed in 2.0.0.
|
||||
|
||||
* The `SignedString` method on `Token` now takes `interface{}` instead of `[]byte`
|
||||
|
||||
#### 2.0.0
|
||||
|
||||
There were two major reasons for breaking backwards compatibility with this update. The first was a refactor required to expand the width of the RSA and HMAC-SHA signing implementations. There will likely be no required code changes to support this change.
|
||||
|
||||
The second update, while unfortunately requiring a small change in integration, is required to open up this library to other signing methods. Not all keys used for all signing methods have a single standard on-disk representation. Requiring `[]byte` as the type for all keys proved too limiting. Additionally, this implementation allows for pre-parsed tokens to be reused, which might matter in an application that parses a high volume of tokens with a small set of keys. Backwards compatibilty has been maintained for passing `[]byte` to the RSA signing methods, but they will also accept `*rsa.PublicKey` and `*rsa.PrivateKey`.
|
||||
|
||||
It is likely the only integration change required here will be to change `func(t *jwt.Token) ([]byte, error)` to `func(t *jwt.Token) (interface{}, error)` when calling `Parse`.
|
||||
|
||||
* **Compatibility Breaking Changes**
|
||||
* `SigningMethodHS256` is now `*SigningMethodHMAC` instead of `type struct`
|
||||
* `SigningMethodRS256` is now `*SigningMethodRSA` instead of `type struct`
|
||||
* `KeyFunc` now returns `interface{}` instead of `[]byte`
|
||||
* `SigningMethod.Sign` now takes `interface{}` instead of `[]byte` for the key
|
||||
* `SigningMethod.Verify` now takes `interface{}` instead of `[]byte` for the key
|
||||
* Renamed type `SigningMethodHS256` to `SigningMethodHMAC`. Specific sizes are now just instances of this type.
|
||||
* Added public package global `SigningMethodHS256`
|
||||
* Added public package global `SigningMethodHS384`
|
||||
* Added public package global `SigningMethodHS512`
|
||||
* Renamed type `SigningMethodRS256` to `SigningMethodRSA`. Specific sizes are now just instances of this type.
|
||||
* Added public package global `SigningMethodRS256`
|
||||
* Added public package global `SigningMethodRS384`
|
||||
* Added public package global `SigningMethodRS512`
|
||||
* Moved sample private key for HMAC tests from an inline value to a file on disk. Value is unchanged.
|
||||
* Refactored the RSA implementation to be easier to read
|
||||
* Exposed helper methods `ParseRSAPrivateKeyFromPEM` and `ParseRSAPublicKeyFromPEM`
|
||||
|
||||
#### 1.0.2
|
||||
|
||||
* Fixed bug in parsing public keys from certificates
|
||||
* Added more tests around the parsing of keys for RS256
|
||||
* Code refactoring in RS256 implementation. No functional changes
|
||||
|
||||
#### 1.0.1
|
||||
|
||||
* Fixed panic if RS256 signing method was passed an invalid key
|
||||
|
||||
#### 1.0.0
|
||||
|
||||
* First versioned release
|
||||
* API stabilized
|
||||
* Supports creating, signing, parsing, and validating JWT tokens
|
||||
* Supports RS256 and HS256 signing methods
|
186
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/cmd/jwt/app.go
generated
vendored
186
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/cmd/jwt/app.go
generated
vendored
|
@ -1,186 +0,0 @@
|
|||
// A useful example app. You can use this to debug your tokens on the command line.
|
||||
// This is also a great place to look at how you might use this library.
|
||||
//
|
||||
// Example usage:
|
||||
// The following will create and sign a token, then verify it and output the original claims.
|
||||
// echo {\"foo\":\"bar\"} | bin/jwt -key test/sample_key -alg RS256 -sign - | bin/jwt -key test/sample_key.pub -verify -
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
var (
|
||||
// Options
|
||||
flagAlg = flag.String("alg", "", "signing algorithm identifier")
|
||||
flagKey = flag.String("key", "", "path to key file or '-' to read from stdin")
|
||||
flagCompact = flag.Bool("compact", false, "output compact JSON")
|
||||
flagDebug = flag.Bool("debug", false, "print out all kinds of debug data")
|
||||
|
||||
// Modes - exactly one of these is required
|
||||
flagSign = flag.String("sign", "", "path to claims object to sign or '-' to read from stdin")
|
||||
flagVerify = flag.String("verify", "", "path to JWT token to verify or '-' to read from stdin")
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Usage message if you ask for -help or if you mess up inputs.
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, " One of the following flags is required: sign, verify\n")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
// Parse command line options
|
||||
flag.Parse()
|
||||
|
||||
// Do the thing. If something goes wrong, print error to stderr
|
||||
// and exit with a non-zero status code
|
||||
if err := start(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out which thing to do and then do that
|
||||
func start() error {
|
||||
if *flagSign != "" {
|
||||
return signToken()
|
||||
} else if *flagVerify != "" {
|
||||
return verifyToken()
|
||||
} else {
|
||||
flag.Usage()
|
||||
return fmt.Errorf("None of the required flags are present. What do you want me to do?")
|
||||
}
|
||||
}
|
||||
|
||||
// Helper func: Read input from specified file or stdin
|
||||
func loadData(p string) ([]byte, error) {
|
||||
if p == "" {
|
||||
return nil, fmt.Errorf("No path specified")
|
||||
}
|
||||
|
||||
var rdr io.Reader
|
||||
if p == "-" {
|
||||
rdr = os.Stdin
|
||||
} else {
|
||||
if f, err := os.Open(p); err == nil {
|
||||
rdr = f
|
||||
defer f.Close()
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return ioutil.ReadAll(rdr)
|
||||
}
|
||||
|
||||
// Print a json object in accordance with the prophecy (or the command line options)
|
||||
func printJSON(j interface{}) error {
|
||||
var out []byte
|
||||
var err error
|
||||
|
||||
if *flagCompact == false {
|
||||
out, err = json.MarshalIndent(j, "", " ")
|
||||
} else {
|
||||
out, err = json.Marshal(j)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify a token and output the claims. This is a great example
|
||||
// of how to verify and view a token.
|
||||
func verifyToken() error {
|
||||
// get the token
|
||||
tokData, err := loadData(*flagVerify)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't read token: %v", err)
|
||||
}
|
||||
|
||||
// trim possible whitespace from token
|
||||
tokData = regexp.MustCompile(`\s*$`).ReplaceAll(tokData, []byte{})
|
||||
if *flagDebug {
|
||||
fmt.Fprintf(os.Stderr, "Token len: %v bytes\n", len(tokData))
|
||||
}
|
||||
|
||||
// Parse the token. Load the key from command line option
|
||||
token, err := jwt.Parse(string(tokData), func(t *jwt.Token) (interface{}, error) {
|
||||
return loadData(*flagKey)
|
||||
})
|
||||
|
||||
// Print some debug data
|
||||
if *flagDebug && token != nil {
|
||||
fmt.Fprintf(os.Stderr, "Header:\n%v\n", token.Header)
|
||||
fmt.Fprintf(os.Stderr, "Claims:\n%v\n", token.Claims)
|
||||
}
|
||||
|
||||
// Print an error if we can't parse for some reason
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't parse token: %v", err)
|
||||
}
|
||||
|
||||
// Is token invalid?
|
||||
if !token.Valid {
|
||||
return fmt.Errorf("Token is invalid")
|
||||
}
|
||||
|
||||
// Print the token details
|
||||
if err := printJSON(token.Claims); err != nil {
|
||||
return fmt.Errorf("Failed to output claims: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create, sign, and output a token. This is a great, simple example of
|
||||
// how to use this library to create and sign a token.
|
||||
func signToken() error {
|
||||
// get the token data from command line arguments
|
||||
tokData, err := loadData(*flagSign)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't read token: %v", err)
|
||||
} else if *flagDebug {
|
||||
fmt.Fprintf(os.Stderr, "Token: %v bytes", len(tokData))
|
||||
}
|
||||
|
||||
// parse the JSON of the claims
|
||||
var claims map[string]interface{}
|
||||
if err := json.Unmarshal(tokData, &claims); err != nil {
|
||||
return fmt.Errorf("Couldn't parse claims JSON: %v", err)
|
||||
}
|
||||
|
||||
// get the key
|
||||
keyData, err := loadData(*flagKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't read key: %v", err)
|
||||
}
|
||||
|
||||
// get the signing alg
|
||||
alg := jwt.GetSigningMethod(*flagAlg)
|
||||
if alg == nil {
|
||||
return fmt.Errorf("Couldn't find signing method: %v", *flagAlg)
|
||||
}
|
||||
|
||||
// create a new token
|
||||
token := jwt.New(alg)
|
||||
token.Claims = claims
|
||||
|
||||
if out, err := token.SignedString(keyData); err == nil {
|
||||
fmt.Println(out)
|
||||
} else {
|
||||
return fmt.Errorf("Error signing token: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
4
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/doc.go
generated
vendored
4
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/doc.go
generated
vendored
|
@ -1,4 +0,0 @@
|
|||
// Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html
|
||||
//
|
||||
// See README.md for more info.
|
||||
package jwt
|
52
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/example_test.go
generated
vendored
52
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/example_test.go
generated
vendored
|
@ -1,52 +0,0 @@
|
|||
package jwt_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"time"
|
||||
)
|
||||
|
||||
func ExampleParse(myToken string, myLookupKey func(interface{}) (interface{}, error)) {
|
||||
token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface{}, error) {
|
||||
return myLookupKey(token.Header["kid"])
|
||||
})
|
||||
|
||||
if err == nil && token.Valid {
|
||||
fmt.Println("Your token is valid. I like your style.")
|
||||
} else {
|
||||
fmt.Println("This token is terrible! I cannot accept this.")
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleNew(mySigningKey []byte) (string, error) {
|
||||
// Create the token
|
||||
token := jwt.New(jwt.SigningMethodHS256)
|
||||
// Set some claims
|
||||
token.Claims["foo"] = "bar"
|
||||
token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
|
||||
// Sign and get the complete encoded token as a string
|
||||
tokenString, err := token.SignedString(mySigningKey)
|
||||
return tokenString, err
|
||||
}
|
||||
|
||||
func ExampleParse_errorChecking(myToken string, myLookupKey func(interface{}) (interface{}, error)) {
|
||||
token, err := jwt.Parse(myToken, func(token *jwt.Token) (interface{}, error) {
|
||||
return myLookupKey(token.Header["kid"])
|
||||
})
|
||||
|
||||
if token.Valid {
|
||||
fmt.Println("You look nice today")
|
||||
} else if ve, ok := err.(*jwt.ValidationError); ok {
|
||||
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
|
||||
fmt.Println("That's not even a token")
|
||||
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
|
||||
// Token is either expired or not active yet
|
||||
fmt.Println("Timing is everything")
|
||||
} else {
|
||||
fmt.Println("Couldn't handle this token:", err)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("Couldn't handle this token:", err)
|
||||
}
|
||||
|
||||
}
|
84
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/hmac.go
generated
vendored
84
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/hmac.go
generated
vendored
|
@ -1,84 +0,0 @@
|
|||
package jwt
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/hmac"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Implements the HMAC-SHA family of signing methods signing methods
|
||||
type SigningMethodHMAC struct {
|
||||
Name string
|
||||
Hash crypto.Hash
|
||||
}
|
||||
|
||||
// Specific instances for HS256 and company
|
||||
var (
|
||||
SigningMethodHS256 *SigningMethodHMAC
|
||||
SigningMethodHS384 *SigningMethodHMAC
|
||||
SigningMethodHS512 *SigningMethodHMAC
|
||||
ErrSignatureInvalid = errors.New("signature is invalid")
|
||||
)
|
||||
|
||||
func init() {
|
||||
// HS256
|
||||
SigningMethodHS256 = &SigningMethodHMAC{"HS256", crypto.SHA256}
|
||||
RegisterSigningMethod(SigningMethodHS256.Alg(), func() SigningMethod {
|
||||
return SigningMethodHS256
|
||||
})
|
||||
|
||||
// HS384
|
||||
SigningMethodHS384 = &SigningMethodHMAC{"HS384", crypto.SHA384}
|
||||
RegisterSigningMethod(SigningMethodHS384.Alg(), func() SigningMethod {
|
||||
return SigningMethodHS384
|
||||
})
|
||||
|
||||
// HS512
|
||||
SigningMethodHS512 = &SigningMethodHMAC{"HS512", crypto.SHA512}
|
||||
RegisterSigningMethod(SigningMethodHS512.Alg(), func() SigningMethod {
|
||||
return SigningMethodHS512
|
||||
})
|
||||
}
|
||||
|
||||
func (m *SigningMethodHMAC) Alg() string {
|
||||
return m.Name
|
||||
}
|
||||
|
||||
func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error {
|
||||
if keyBytes, ok := key.([]byte); ok {
|
||||
var sig []byte
|
||||
var err error
|
||||
if sig, err = DecodeSegment(signature); err == nil {
|
||||
if !m.Hash.Available() {
|
||||
return ErrHashUnavailable
|
||||
}
|
||||
|
||||
hasher := hmac.New(m.Hash.New, keyBytes)
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
if !hmac.Equal(sig, hasher.Sum(nil)) {
|
||||
err = ErrSignatureInvalid
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return ErrInvalidKey
|
||||
}
|
||||
|
||||
// Implements the Sign method from SigningMethod for this signing method.
|
||||
// Key must be []byte
|
||||
func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) {
|
||||
if keyBytes, ok := key.([]byte); ok {
|
||||
if !m.Hash.Available() {
|
||||
return "", ErrHashUnavailable
|
||||
}
|
||||
|
||||
hasher := hmac.New(m.Hash.New, keyBytes)
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
return EncodeSegment(hasher.Sum(nil)), nil
|
||||
}
|
||||
|
||||
return "", ErrInvalidKey
|
||||
}
|
79
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/hmac_test.go
generated
vendored
79
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/hmac_test.go
generated
vendored
|
@ -1,79 +0,0 @@
|
|||
package jwt_test
|
||||
|
||||
import (
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var hmacTestData = []struct {
|
||||
name string
|
||||
tokenString string
|
||||
alg string
|
||||
claims map[string]interface{}
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
"web sample",
|
||||
"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk",
|
||||
"HS256",
|
||||
map[string]interface{}{"iss": "joe", "exp": 1300819380, "http://example.com/is_root": true},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"HS384",
|
||||
"eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuMzAwODE5MzhlKzA5LCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6dHJ1ZSwiaXNzIjoiam9lIn0.KWZEuOD5lbBxZ34g7F-SlVLAQ_r5KApWNWlZIIMyQVz5Zs58a7XdNzj5_0EcNoOy",
|
||||
"HS384",
|
||||
map[string]interface{}{"iss": "joe", "exp": 1300819380, "http://example.com/is_root": true},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"HS512",
|
||||
"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEuMzAwODE5MzhlKzA5LCJodHRwOi8vZXhhbXBsZS5jb20vaXNfcm9vdCI6dHJ1ZSwiaXNzIjoiam9lIn0.CN7YijRX6Aw1n2jyI2Id1w90ja-DEMYiWixhYCyHnrZ1VfJRaFQz1bEbjjA5Fn4CLYaUG432dEYmSbS4Saokmw",
|
||||
"HS512",
|
||||
map[string]interface{}{"iss": "joe", "exp": 1300819380, "http://example.com/is_root": true},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"web sample: invalid",
|
||||
"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXo",
|
||||
"HS256",
|
||||
map[string]interface{}{"iss": "joe", "exp": 1300819380, "http://example.com/is_root": true},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
// Sample data from http://tools.ietf.org/html/draft-jones-json-web-signature-04#appendix-A.1
|
||||
var hmacTestKey, _ = ioutil.ReadFile("test/hmacTestKey")
|
||||
|
||||
func TestHMACVerify(t *testing.T) {
|
||||
for _, data := range hmacTestData {
|
||||
parts := strings.Split(data.tokenString, ".")
|
||||
|
||||
method := jwt.GetSigningMethod(data.alg)
|
||||
err := method.Verify(strings.Join(parts[0:2], "."), parts[2], hmacTestKey)
|
||||
if data.valid && err != nil {
|
||||
t.Errorf("[%v] Error while verifying key: %v", data.name, err)
|
||||
}
|
||||
if !data.valid && err == nil {
|
||||
t.Errorf("[%v] Invalid key passed validation", data.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHMACSign(t *testing.T) {
|
||||
for _, data := range hmacTestData {
|
||||
if data.valid {
|
||||
parts := strings.Split(data.tokenString, ".")
|
||||
method := jwt.GetSigningMethod(data.alg)
|
||||
sig, err := method.Sign(strings.Join(parts[0:2], "."), hmacTestKey)
|
||||
if err != nil {
|
||||
t.Errorf("[%v] Error signing token: %v", data.name, err)
|
||||
}
|
||||
if sig != parts[2] {
|
||||
t.Errorf("[%v] Incorrect signature.\nwas:\n%v\nexpecting:\n%v", data.name, sig, parts[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
237
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/jwt.go
generated
vendored
237
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/jwt.go
generated
vendored
|
@ -1,237 +0,0 @@
|
|||
package jwt
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time).
|
||||
// You can override it to use another time value. This is useful for testing or if your
|
||||
// server uses a different time zone than your tokens.
|
||||
var TimeFunc = time.Now
|
||||
|
||||
// Parse methods use this callback function to supply
|
||||
// the key for verification. The function receives the parsed,
|
||||
// but unverified Token. This allows you to use propries in the
|
||||
// Header of the token (such as `kid`) to identify which key to use.
|
||||
type Keyfunc func(*Token) (interface{}, error)
|
||||
|
||||
// Error constants
|
||||
var (
|
||||
ErrInvalidKey = errors.New("key is invalid or of invalid type")
|
||||
ErrHashUnavailable = errors.New("the requested hash function is unavailable")
|
||||
ErrNoTokenInRequest = errors.New("no token present in request")
|
||||
)
|
||||
|
||||
// A JWT Token. Different fields will be used depending on whether you're
|
||||
// creating or parsing/verifying a token.
|
||||
type Token struct {
|
||||
Raw string // The raw token. Populated when you Parse a token
|
||||
Method SigningMethod // The signing method used or to be used
|
||||
Header map[string]interface{} // The first segment of the token
|
||||
Claims map[string]interface{} // The second segment of the token
|
||||
Signature string // The third segment of the token. Populated when you Parse a token
|
||||
Valid bool // Is the token valid? Populated when you Parse/Verify a token
|
||||
}
|
||||
|
||||
// Create a new Token. Takes a signing method
|
||||
func New(method SigningMethod) *Token {
|
||||
return &Token{
|
||||
Header: map[string]interface{}{
|
||||
"typ": "JWT",
|
||||
"alg": method.Alg(),
|
||||
},
|
||||
Claims: make(map[string]interface{}),
|
||||
Method: method,
|
||||
}
|
||||
}
|
||||
|
||||
// Get the complete, signed token
|
||||
func (t *Token) SignedString(key interface{}) (string, error) {
|
||||
var sig, sstr string
|
||||
var err error
|
||||
if sstr, err = t.SigningString(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if sig, err = t.Method.Sign(sstr, key); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Join([]string{sstr, sig}, "."), nil
|
||||
}
|
||||
|
||||
// Generate the signing string. This is the
|
||||
// most expensive part of the whole deal. Unless you
|
||||
// need this for something special, just go straight for
|
||||
// the SignedString.
|
||||
func (t *Token) SigningString() (string, error) {
|
||||
var err error
|
||||
parts := make([]string, 2)
|
||||
for i, _ := range parts {
|
||||
var source map[string]interface{}
|
||||
if i == 0 {
|
||||
source = t.Header
|
||||
} else {
|
||||
source = t.Claims
|
||||
}
|
||||
|
||||
var jsonValue []byte
|
||||
if jsonValue, err = json.Marshal(source); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
parts[i] = EncodeSegment(jsonValue)
|
||||
}
|
||||
return strings.Join(parts, "."), nil
|
||||
}
|
||||
|
||||
// Parse, validate, and return a token.
|
||||
// keyFunc will receive the parsed token and should return the key for validating.
|
||||
// If everything is kosher, err will be nil
|
||||
func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
|
||||
parts := strings.Split(tokenString, ".")
|
||||
if len(parts) != 3 {
|
||||
return nil, &ValidationError{err: "token contains an invalid number of segments", Errors: ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
var err error
|
||||
token := &Token{Raw: tokenString}
|
||||
// parse Header
|
||||
var headerBytes []byte
|
||||
if headerBytes, err = DecodeSegment(parts[0]); err != nil {
|
||||
return token, &ValidationError{err: err.Error(), Errors: ValidationErrorMalformed}
|
||||
}
|
||||
if err = json.Unmarshal(headerBytes, &token.Header); err != nil {
|
||||
return token, &ValidationError{err: err.Error(), Errors: ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
// parse Claims
|
||||
var claimBytes []byte
|
||||
if claimBytes, err = DecodeSegment(parts[1]); err != nil {
|
||||
return token, &ValidationError{err: err.Error(), Errors: ValidationErrorMalformed}
|
||||
}
|
||||
if err = json.Unmarshal(claimBytes, &token.Claims); err != nil {
|
||||
return token, &ValidationError{err: err.Error(), Errors: ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
// Lookup signature method
|
||||
if method, ok := token.Header["alg"].(string); ok {
|
||||
if token.Method = GetSigningMethod(method); token.Method == nil {
|
||||
return token, &ValidationError{err: "signing method (alg) is unavailable.", Errors: ValidationErrorUnverifiable}
|
||||
}
|
||||
} else {
|
||||
return token, &ValidationError{err: "signing method (alg) is unspecified.", Errors: ValidationErrorUnverifiable}
|
||||
}
|
||||
|
||||
// Lookup key
|
||||
var key interface{}
|
||||
if keyFunc == nil {
|
||||
// keyFunc was not provided. short circuiting validation
|
||||
return token, &ValidationError{err: "no Keyfunc was provided.", Errors: ValidationErrorUnverifiable}
|
||||
}
|
||||
if key, err = keyFunc(token); err != nil {
|
||||
// keyFunc returned an error
|
||||
return token, &ValidationError{err: err.Error(), Errors: ValidationErrorUnverifiable}
|
||||
}
|
||||
|
||||
// Check expiration times
|
||||
vErr := &ValidationError{}
|
||||
now := TimeFunc().Unix()
|
||||
if exp, ok := token.Claims["exp"].(float64); ok {
|
||||
if now > int64(exp) {
|
||||
vErr.err = "token is expired"
|
||||
vErr.Errors |= ValidationErrorExpired
|
||||
}
|
||||
}
|
||||
if nbf, ok := token.Claims["nbf"].(float64); ok {
|
||||
if now < int64(nbf) {
|
||||
vErr.err = "token is not valid yet"
|
||||
vErr.Errors |= ValidationErrorNotValidYet
|
||||
}
|
||||
}
|
||||
|
||||
// Perform validation
|
||||
if err = token.Method.Verify(strings.Join(parts[0:2], "."), parts[2], key); err != nil {
|
||||
vErr.err = err.Error()
|
||||
vErr.Errors |= ValidationErrorSignatureInvalid
|
||||
}
|
||||
|
||||
if vErr.valid() {
|
||||
token.Valid = true
|
||||
return token, nil
|
||||
}
|
||||
|
||||
return token, vErr
|
||||
}
|
||||
|
||||
// The errors that might occur when parsing and validating a token
|
||||
const (
|
||||
ValidationErrorMalformed uint32 = 1 << iota // Token is malformed
|
||||
ValidationErrorUnverifiable // Token could not be verified because of signing problems
|
||||
ValidationErrorSignatureInvalid // Signature validation failed
|
||||
ValidationErrorExpired // Exp validation failed
|
||||
ValidationErrorNotValidYet // NBF validation failed
|
||||
)
|
||||
|
||||
// The error from Parse if token is not valid
|
||||
type ValidationError struct {
|
||||
err string
|
||||
Errors uint32 // bitfield. see ValidationError... constants
|
||||
}
|
||||
|
||||
// Validation error is an error type
|
||||
func (e ValidationError) Error() string {
|
||||
if e.err == "" {
|
||||
return "token is invalid"
|
||||
}
|
||||
return e.err
|
||||
}
|
||||
|
||||
// No errors
|
||||
func (e *ValidationError) valid() bool {
|
||||
if e.Errors > 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Try to find the token in an http.Request.
|
||||
// This method will call ParseMultipartForm if there's no token in the header.
|
||||
// Currently, it looks in the Authorization header as well as
|
||||
// looking for an 'access_token' request parameter in req.Form.
|
||||
func ParseFromRequest(req *http.Request, keyFunc Keyfunc) (token *Token, err error) {
|
||||
|
||||
// Look for an Authorization header
|
||||
if ah := req.Header.Get("Authorization"); ah != "" {
|
||||
// Should be a bearer token
|
||||
if len(ah) > 6 && strings.ToUpper(ah[0:6]) == "BEARER" {
|
||||
return Parse(ah[7:], keyFunc)
|
||||
}
|
||||
}
|
||||
|
||||
// Look for "access_token" parameter
|
||||
req.ParseMultipartForm(10e6)
|
||||
if tokStr := req.Form.Get("access_token"); tokStr != "" {
|
||||
return Parse(tokStr, keyFunc)
|
||||
}
|
||||
|
||||
return nil, ErrNoTokenInRequest
|
||||
|
||||
}
|
||||
|
||||
// Encode JWT specific base64url encoding with padding stripped
|
||||
func EncodeSegment(seg []byte) string {
|
||||
return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=")
|
||||
}
|
||||
|
||||
// Decode JWT specific base64url encoding with padding stripped
|
||||
func DecodeSegment(seg string) ([]byte, error) {
|
||||
if l := len(seg) % 4; l > 0 {
|
||||
seg += strings.Repeat("=", 4-l)
|
||||
}
|
||||
|
||||
return base64.URLEncoding.DecodeString(seg)
|
||||
}
|
174
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/jwt_test.go
generated
vendored
174
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/jwt_test.go
generated
vendored
|
@ -1,174 +0,0 @@
|
|||
package jwt_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
jwtTestDefaultKey []byte
|
||||
defaultKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return jwtTestDefaultKey, nil }
|
||||
emptyKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return nil, nil }
|
||||
errorKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return nil, fmt.Errorf("error loading key") }
|
||||
nilKeyFunc jwt.Keyfunc = nil
|
||||
)
|
||||
|
||||
var jwtTestData = []struct {
|
||||
name string
|
||||
tokenString string
|
||||
keyfunc jwt.Keyfunc
|
||||
claims map[string]interface{}
|
||||
valid bool
|
||||
errors uint32
|
||||
}{
|
||||
{
|
||||
"basic",
|
||||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
|
||||
defaultKeyFunc,
|
||||
map[string]interface{}{"foo": "bar"},
|
||||
true,
|
||||
0,
|
||||
},
|
||||
{
|
||||
"basic expired",
|
||||
"", // autogen
|
||||
defaultKeyFunc,
|
||||
map[string]interface{}{"foo": "bar", "exp": float64(time.Now().Unix() - 100)},
|
||||
false,
|
||||
jwt.ValidationErrorExpired,
|
||||
},
|
||||
{
|
||||
"basic nbf",
|
||||
"", // autogen
|
||||
defaultKeyFunc,
|
||||
map[string]interface{}{"foo": "bar", "nbf": float64(time.Now().Unix() + 100)},
|
||||
false,
|
||||
jwt.ValidationErrorNotValidYet,
|
||||
},
|
||||
{
|
||||
"expired and nbf",
|
||||
"", // autogen
|
||||
defaultKeyFunc,
|
||||
map[string]interface{}{"foo": "bar", "nbf": float64(time.Now().Unix() + 100), "exp": float64(time.Now().Unix() - 100)},
|
||||
false,
|
||||
jwt.ValidationErrorNotValidYet | jwt.ValidationErrorExpired,
|
||||
},
|
||||
{
|
||||
"basic invalid",
|
||||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.EhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
|
||||
defaultKeyFunc,
|
||||
map[string]interface{}{"foo": "bar"},
|
||||
false,
|
||||
jwt.ValidationErrorSignatureInvalid,
|
||||
},
|
||||
{
|
||||
"basic nokeyfunc",
|
||||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
|
||||
nilKeyFunc,
|
||||
map[string]interface{}{"foo": "bar"},
|
||||
false,
|
||||
jwt.ValidationErrorUnverifiable,
|
||||
},
|
||||
{
|
||||
"basic nokey",
|
||||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
|
||||
emptyKeyFunc,
|
||||
map[string]interface{}{"foo": "bar"},
|
||||
false,
|
||||
jwt.ValidationErrorSignatureInvalid,
|
||||
},
|
||||
{
|
||||
"basic errorkey",
|
||||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
|
||||
errorKeyFunc,
|
||||
map[string]interface{}{"foo": "bar"},
|
||||
false,
|
||||
jwt.ValidationErrorUnverifiable,
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
var e error
|
||||
if jwtTestDefaultKey, e = ioutil.ReadFile("test/sample_key.pub"); e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
func makeSample(c map[string]interface{}) string {
|
||||
key, e := ioutil.ReadFile("test/sample_key")
|
||||
if e != nil {
|
||||
panic(e.Error())
|
||||
}
|
||||
|
||||
token := jwt.New(jwt.SigningMethodRS256)
|
||||
token.Claims = c
|
||||
s, e := token.SignedString(key)
|
||||
|
||||
if e != nil {
|
||||
panic(e.Error())
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func TestJWT(t *testing.T) {
|
||||
for _, data := range jwtTestData {
|
||||
if data.tokenString == "" {
|
||||
data.tokenString = makeSample(data.claims)
|
||||
}
|
||||
token, err := jwt.Parse(data.tokenString, data.keyfunc)
|
||||
|
||||
if !reflect.DeepEqual(data.claims, token.Claims) {
|
||||
t.Errorf("[%v] Claims mismatch. Expecting: %v Got: %v", data.name, data.claims, token.Claims)
|
||||
}
|
||||
if data.valid && err != nil {
|
||||
t.Errorf("[%v] Error while verifying token: %T:%v", data.name, err, err)
|
||||
}
|
||||
if !data.valid && err == nil {
|
||||
t.Errorf("[%v] Invalid token passed validation", data.name)
|
||||
}
|
||||
if data.errors != 0 {
|
||||
if err == nil {
|
||||
t.Errorf("[%v] Expecting error. Didn't get one.", data.name)
|
||||
} else {
|
||||
// compare the bitfield part of the error
|
||||
if err.(*jwt.ValidationError).Errors != data.errors {
|
||||
t.Errorf("[%v] Errors don't match expectation", data.name)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRequest(t *testing.T) {
|
||||
// Bearer token request
|
||||
for _, data := range jwtTestData {
|
||||
if data.tokenString == "" {
|
||||
data.tokenString = makeSample(data.claims)
|
||||
}
|
||||
|
||||
r, _ := http.NewRequest("GET", "/", nil)
|
||||
r.Header.Set("Authorization", fmt.Sprintf("Bearer %v", data.tokenString))
|
||||
token, err := jwt.ParseFromRequest(r, data.keyfunc)
|
||||
|
||||
if token == nil {
|
||||
t.Errorf("[%v] Token was not found: %v", data.name, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(data.claims, token.Claims) {
|
||||
t.Errorf("[%v] Claims mismatch. Expecting: %v Got: %v", data.name, data.claims, token.Claims)
|
||||
}
|
||||
if data.valid && err != nil {
|
||||
t.Errorf("[%v] Error while verifying token: %v", data.name, err)
|
||||
}
|
||||
if !data.valid && err == nil {
|
||||
t.Errorf("[%v] Invalid token passed validation", data.name)
|
||||
}
|
||||
}
|
||||
}
|
114
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/rsa.go
generated
vendored
114
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/rsa.go
generated
vendored
|
@ -1,114 +0,0 @@
|
|||
package jwt
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
)
|
||||
|
||||
// Implements the RSA family of signing methods signing methods
|
||||
type SigningMethodRSA struct {
|
||||
Name string
|
||||
Hash crypto.Hash
|
||||
}
|
||||
|
||||
// Specific instances for RS256 and company
|
||||
var (
|
||||
SigningMethodRS256 *SigningMethodRSA
|
||||
SigningMethodRS384 *SigningMethodRSA
|
||||
SigningMethodRS512 *SigningMethodRSA
|
||||
)
|
||||
|
||||
func init() {
|
||||
// RS256
|
||||
SigningMethodRS256 = &SigningMethodRSA{"RS256", crypto.SHA256}
|
||||
RegisterSigningMethod(SigningMethodRS256.Alg(), func() SigningMethod {
|
||||
return SigningMethodRS256
|
||||
})
|
||||
|
||||
// RS384
|
||||
SigningMethodRS384 = &SigningMethodRSA{"RS384", crypto.SHA384}
|
||||
RegisterSigningMethod(SigningMethodRS384.Alg(), func() SigningMethod {
|
||||
return SigningMethodRS384
|
||||
})
|
||||
|
||||
// RS512
|
||||
SigningMethodRS512 = &SigningMethodRSA{"RS512", crypto.SHA512}
|
||||
RegisterSigningMethod(SigningMethodRS512.Alg(), func() SigningMethod {
|
||||
return SigningMethodRS512
|
||||
})
|
||||
}
|
||||
|
||||
func (m *SigningMethodRSA) Alg() string {
|
||||
return m.Name
|
||||
}
|
||||
|
||||
// Implements the Verify method from SigningMethod
|
||||
// For this signing method, must be either a PEM encoded PKCS1 or PKCS8 RSA public key as
|
||||
// []byte, or an rsa.PublicKey structure.
|
||||
func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error {
|
||||
var err error
|
||||
|
||||
// Decode the signature
|
||||
var sig []byte
|
||||
if sig, err = DecodeSegment(signature); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rsaKey *rsa.PublicKey
|
||||
|
||||
switch k := key.(type) {
|
||||
case []byte:
|
||||
if rsaKey, err = ParseRSAPublicKeyFromPEM(k); err != nil {
|
||||
return err
|
||||
}
|
||||
case *rsa.PublicKey:
|
||||
rsaKey = k
|
||||
default:
|
||||
return ErrInvalidKey
|
||||
}
|
||||
|
||||
// Create hasher
|
||||
if !m.Hash.Available() {
|
||||
return ErrHashUnavailable
|
||||
}
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
// Verify the signature
|
||||
return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig)
|
||||
}
|
||||
|
||||
// Implements the Sign method from SigningMethod
|
||||
// For this signing method, must be either a PEM encoded PKCS1 or PKCS8 RSA private key as
|
||||
// []byte, or an rsa.PrivateKey structure.
|
||||
func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) {
|
||||
var err error
|
||||
var rsaKey *rsa.PrivateKey
|
||||
|
||||
switch k := key.(type) {
|
||||
case []byte:
|
||||
if rsaKey, err = ParseRSAPrivateKeyFromPEM(k); err != nil {
|
||||
return "", err
|
||||
}
|
||||
case *rsa.PrivateKey:
|
||||
rsaKey = k
|
||||
default:
|
||||
return "", ErrInvalidKey
|
||||
}
|
||||
|
||||
// Create the hasher
|
||||
if !m.Hash.Available() {
|
||||
return "", ErrHashUnavailable
|
||||
}
|
||||
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
// Sign the string and return the encoded bytes
|
||||
if sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil)); err == nil {
|
||||
return EncodeSegment(sigBytes), nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
144
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/rsa_test.go
generated
vendored
144
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/rsa_test.go
generated
vendored
|
@ -1,144 +0,0 @@
|
|||
package jwt_test
|
||||
|
||||
import (
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var rsaTestData = []struct {
|
||||
name string
|
||||
tokenString string
|
||||
alg string
|
||||
claims map[string]interface{}
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
"Basic RS256",
|
||||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
|
||||
"RS256",
|
||||
map[string]interface{}{"foo": "bar"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"Basic RS384",
|
||||
"eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.W-jEzRfBigtCWsinvVVuldiuilzVdU5ty0MvpLaSaqK9PlAWWlDQ1VIQ_qSKzwL5IXaZkvZFJXT3yL3n7OUVu7zCNJzdwznbC8Z-b0z2lYvcklJYi2VOFRcGbJtXUqgjk2oGsiqUMUMOLP70TTefkpsgqDxbRh9CDUfpOJgW-dU7cmgaoswe3wjUAUi6B6G2YEaiuXC0XScQYSYVKIzgKXJV8Zw-7AN_DBUI4GkTpsvQ9fVVjZM9csQiEXhYekyrKu1nu_POpQonGd8yqkIyXPECNmmqH5jH4sFiF67XhD7_JpkvLziBpI-uh86evBUadmHhb9Otqw3uV3NTaXLzJw",
|
||||
"RS384",
|
||||
map[string]interface{}{"foo": "bar"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"Basic RS512",
|
||||
"eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.zBlLlmRrUxx4SJPUbV37Q1joRcI9EW13grnKduK3wtYKmDXbgDpF1cZ6B-2Jsm5RB8REmMiLpGms-EjXhgnyh2TSHE-9W2gA_jvshegLWtwRVDX40ODSkTb7OVuaWgiy9y7llvcknFBTIg-FnVPVpXMmeV_pvwQyhaz1SSwSPrDyxEmksz1hq7YONXhXPpGaNbMMeDTNP_1oj8DZaqTIL9TwV8_1wb2Odt_Fy58Ke2RVFijsOLdnyEAjt2n9Mxihu9i3PhNBkkxa2GbnXBfq3kzvZ_xxGGopLdHhJjcGWXO-NiwI9_tiu14NRv4L2xC0ItD9Yz68v2ZIZEp_DuzwRQ",
|
||||
"RS512",
|
||||
map[string]interface{}{"foo": "bar"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"basic invalid: foo => bar",
|
||||
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.EhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",
|
||||
"RS256",
|
||||
map[string]interface{}{"foo": "bar"},
|
||||
false,
|
||||
},
|
||||
}
|
||||
|
||||
func TestRSAVerify(t *testing.T) {
|
||||
key, _ := ioutil.ReadFile("test/sample_key.pub")
|
||||
|
||||
for _, data := range rsaTestData {
|
||||
parts := strings.Split(data.tokenString, ".")
|
||||
|
||||
method := jwt.GetSigningMethod(data.alg)
|
||||
err := method.Verify(strings.Join(parts[0:2], "."), parts[2], key)
|
||||
if data.valid && err != nil {
|
||||
t.Errorf("[%v] Error while verifying key: %v", data.name, err)
|
||||
}
|
||||
if !data.valid && err == nil {
|
||||
t.Errorf("[%v] Invalid key passed validation", data.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSASign(t *testing.T) {
|
||||
key, _ := ioutil.ReadFile("test/sample_key")
|
||||
|
||||
for _, data := range rsaTestData {
|
||||
if data.valid {
|
||||
parts := strings.Split(data.tokenString, ".")
|
||||
method := jwt.GetSigningMethod(data.alg)
|
||||
sig, err := method.Sign(strings.Join(parts[0:2], "."), key)
|
||||
if err != nil {
|
||||
t.Errorf("[%v] Error signing token: %v", data.name, err)
|
||||
}
|
||||
if sig != parts[2] {
|
||||
t.Errorf("[%v] Incorrect signature.\nwas:\n%v\nexpecting:\n%v", data.name, sig, parts[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSAVerifyWithPreParsedPrivateKey(t *testing.T) {
|
||||
key, _ := ioutil.ReadFile("test/sample_key.pub")
|
||||
parsedKey, err := jwt.ParseRSAPublicKeyFromPEM(key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testData := rsaTestData[0]
|
||||
parts := strings.Split(testData.tokenString, ".")
|
||||
err = jwt.SigningMethodRS256.Verify(strings.Join(parts[0:2], "."), parts[2], parsedKey)
|
||||
if err != nil {
|
||||
t.Errorf("[%v] Error while verifying key: %v", testData.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSAWithPreParsedPrivateKey(t *testing.T) {
|
||||
key, _ := ioutil.ReadFile("test/sample_key")
|
||||
parsedKey, err := jwt.ParseRSAPrivateKeyFromPEM(key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
testData := rsaTestData[0]
|
||||
parts := strings.Split(testData.tokenString, ".")
|
||||
sig, err := jwt.SigningMethodRS256.Sign(strings.Join(parts[0:2], "."), parsedKey)
|
||||
if err != nil {
|
||||
t.Errorf("[%v] Error signing token: %v", testData.name, err)
|
||||
}
|
||||
if sig != parts[2] {
|
||||
t.Errorf("[%v] Incorrect signature.\nwas:\n%v\nexpecting:\n%v", testData.name, sig, parts[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRSAKeyParsing(t *testing.T) {
|
||||
key, _ := ioutil.ReadFile("test/sample_key")
|
||||
pubKey, _ := ioutil.ReadFile("test/sample_key.pub")
|
||||
badKey := []byte("All your base are belong to key")
|
||||
|
||||
// Test parsePrivateKey
|
||||
if _, e := jwt.ParseRSAPrivateKeyFromPEM(key); e != nil {
|
||||
t.Errorf("Failed to parse valid private key: %v", e)
|
||||
}
|
||||
|
||||
if k, e := jwt.ParseRSAPrivateKeyFromPEM(pubKey); e == nil {
|
||||
t.Errorf("Parsed public key as valid private key: %v", k)
|
||||
}
|
||||
|
||||
if k, e := jwt.ParseRSAPrivateKeyFromPEM(badKey); e == nil {
|
||||
t.Errorf("Parsed invalid key as valid private key: %v", k)
|
||||
}
|
||||
|
||||
// Test parsePublicKey
|
||||
if _, e := jwt.ParseRSAPublicKeyFromPEM(pubKey); e != nil {
|
||||
t.Errorf("Failed to parse valid public key: %v", e)
|
||||
}
|
||||
|
||||
if k, e := jwt.ParseRSAPublicKeyFromPEM(key); e == nil {
|
||||
t.Errorf("Parsed private key as valid public key: %v", k)
|
||||
}
|
||||
|
||||
if k, e := jwt.ParseRSAPublicKeyFromPEM(badKey); e == nil {
|
||||
t.Errorf("Parsed invalid key as valid private key: %v", k)
|
||||
}
|
||||
|
||||
}
|
68
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/rsa_utils.go
generated
vendored
68
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/rsa_utils.go
generated
vendored
|
@ -1,68 +0,0 @@
|
|||
package jwt
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrKeyMustBePEMEncoded = errors.New("Invalid Key: Key must be PEM encoded PKCS1 or PKCS8 private key")
|
||||
ErrNotRSAPrivateKey = errors.New("Key is not a valid RSA private key")
|
||||
)
|
||||
|
||||
// Parse PEM encoded PKCS1 or PKCS8 private key
|
||||
func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) {
|
||||
var err error
|
||||
|
||||
// Parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, ErrKeyMustBePEMEncoded
|
||||
}
|
||||
|
||||
var parsedKey interface{}
|
||||
if parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
|
||||
if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var pkey *rsa.PrivateKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok {
|
||||
return nil, ErrNotRSAPrivateKey
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
||||
|
||||
// Parse PEM encoded PKCS1 or PKCS8 public key
|
||||
func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) {
|
||||
var err error
|
||||
|
||||
// Parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, ErrKeyMustBePEMEncoded
|
||||
}
|
||||
|
||||
// Parse the key
|
||||
var parsedKey interface{}
|
||||
if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
|
||||
if cert, err := x509.ParseCertificate(block.Bytes); err == nil {
|
||||
parsedKey = cert.PublicKey
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var pkey *rsa.PublicKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*rsa.PublicKey); !ok {
|
||||
return nil, ErrNotRSAPrivateKey
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
24
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/signing_method.go
generated
vendored
24
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/signing_method.go
generated
vendored
|
@ -1,24 +0,0 @@
|
|||
package jwt
|
||||
|
||||
var signingMethods = map[string]func() SigningMethod{}
|
||||
|
||||
// Signing method
|
||||
type SigningMethod interface {
|
||||
Verify(signingString, signature string, key interface{}) error
|
||||
Sign(signingString string, key interface{}) (string, error)
|
||||
Alg() string
|
||||
}
|
||||
|
||||
// Register the "alg" name and a factory function for signing method.
|
||||
// This is typically done during init() in the method's implementation
|
||||
func RegisterSigningMethod(alg string, f func() SigningMethod) {
|
||||
signingMethods[alg] = f
|
||||
}
|
||||
|
||||
// Get a signing method from an "alg" string
|
||||
func GetSigningMethod(alg string) (method SigningMethod) {
|
||||
if methodF, ok := signingMethods[alg]; ok {
|
||||
method = methodF()
|
||||
}
|
||||
return
|
||||
}
|
1
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/hmacTestKey
generated
vendored
1
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/hmacTestKey
generated
vendored
|
@ -1 +0,0 @@
|
|||
#5K+・シミew{ヲ住ウ(跼Tノ(ゥ┫メP.ソモ燾辻G<>感テwb="=.!r.Oタヘ奎gミ」
|
27
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/sample_key
generated
vendored
27
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/sample_key
generated
vendored
|
@ -1,27 +0,0 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEA4f5wg5l2hKsTeNem/V41fGnJm6gOdrj8ym3rFkEU/wT8RDtn
|
||||
SgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7mCpz9Er5qLaMXJwZxzHzAahlfA0i
|
||||
cqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBpHssPnpYGIn20ZZuNlX2BrClciHhC
|
||||
PUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2XrHhR+1DcKJzQBSTAGnpYVaqpsAR
|
||||
ap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3bODIRe1AuTyHceAbewn8b462yEWKA
|
||||
Rdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy7wIDAQABAoIBAQCwia1k7+2oZ2d3
|
||||
n6agCAbqIE1QXfCmh41ZqJHbOY3oRQG3X1wpcGH4Gk+O+zDVTV2JszdcOt7E5dAy
|
||||
MaomETAhRxB7hlIOnEN7WKm+dGNrKRvV0wDU5ReFMRHg31/Lnu8c+5BvGjZX+ky9
|
||||
POIhFFYJqwCRlopGSUIxmVj5rSgtzk3iWOQXr+ah1bjEXvlxDOWkHN6YfpV5ThdE
|
||||
KdBIPGEVqa63r9n2h+qazKrtiRqJqGnOrHzOECYbRFYhexsNFz7YT02xdfSHn7gM
|
||||
IvabDDP/Qp0PjE1jdouiMaFHYnLBbgvlnZW9yuVf/rpXTUq/njxIXMmvmEyyvSDn
|
||||
FcFikB8pAoGBAPF77hK4m3/rdGT7X8a/gwvZ2R121aBcdPwEaUhvj/36dx596zvY
|
||||
mEOjrWfZhF083/nYWE2kVquj2wjs+otCLfifEEgXcVPTnEOPO9Zg3uNSL0nNQghj
|
||||
FuD3iGLTUBCtM66oTe0jLSslHe8gLGEQqyMzHOzYxNqibxcOZIe8Qt0NAoGBAO+U
|
||||
I5+XWjWEgDmvyC3TrOSf/KCGjtu0TSv30ipv27bDLMrpvPmD/5lpptTFwcxvVhCs
|
||||
2b+chCjlghFSWFbBULBrfci2FtliClOVMYrlNBdUSJhf3aYSG2Doe6Bgt1n2CpNn
|
||||
/iu37Y3NfemZBJA7hNl4dYe+f+uzM87cdQ214+jrAoGAXA0XxX8ll2+ToOLJsaNT
|
||||
OvNB9h9Uc5qK5X5w+7G7O998BN2PC/MWp8H+2fVqpXgNENpNXttkRm1hk1dych86
|
||||
EunfdPuqsX+as44oCyJGFHVBnWpm33eWQw9YqANRI+pCJzP08I5WK3osnPiwshd+
|
||||
hR54yjgfYhBFNI7B95PmEQkCgYBzFSz7h1+s34Ycr8SvxsOBWxymG5zaCsUbPsL0
|
||||
4aCgLScCHb9J+E86aVbbVFdglYa5Id7DPTL61ixhl7WZjujspeXZGSbmq0Kcnckb
|
||||
mDgqkLECiOJW2NHP/j0McAkDLL4tysF8TLDO8gvuvzNC+WQ6drO2ThrypLVZQ+ry
|
||||
eBIPmwKBgEZxhqa0gVvHQG/7Od69KWj4eJP28kq13RhKay8JOoN0vPmspXJo1HY3
|
||||
CKuHRG+AP579dncdUnOMvfXOtkdM4vk0+hWASBQzM9xzVcztCa+koAugjVaLS9A+
|
||||
9uQoqEeVNTckxx0S2bYevRy7hGQmUJTyQm3j1zEUR5jpdbL83Fbq
|
||||
-----END RSA PRIVATE KEY-----
|
9
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/sample_key.pub
generated
vendored
9
Godeps/_workspace/src/github.com/dgrijalva/jwt-go/test/sample_key.pub
generated
vendored
|
@ -1,9 +0,0 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4f5wg5l2hKsTeNem/V41
|
||||
fGnJm6gOdrj8ym3rFkEU/wT8RDtnSgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7
|
||||
mCpz9Er5qLaMXJwZxzHzAahlfA0icqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBp
|
||||
HssPnpYGIn20ZZuNlX2BrClciHhCPUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2
|
||||
XrHhR+1DcKJzQBSTAGnpYVaqpsARap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3b
|
||||
ODIRe1AuTyHceAbewn8b462yEWKARdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy
|
||||
7wIDAQAB
|
||||
-----END PUBLIC KEY-----
|
22
Godeps/_workspace/src/github.com/thermokarst/jwt/LICENSE
generated
vendored
Normal file
22
Godeps/_workspace/src/github.com/thermokarst/jwt/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Matthew Dillon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
145
Godeps/_workspace/src/github.com/thermokarst/jwt/README.md
generated
vendored
Normal file
145
Godeps/_workspace/src/github.com/thermokarst/jwt/README.md
generated
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
# jwt
|
||||
|
||||
[](https://godoc.org/github.com/thermokarst/jwt)
|
||||
|
||||
A simple, opinionated Go net/http middleware for integrating JSON Web Tokens into
|
||||
your application:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/thermokarst/jwt"
|
||||
)
|
||||
|
||||
func protectMe(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "secured")
|
||||
}
|
||||
|
||||
func main() {
|
||||
var authFunc = func(email string, password string) error {
|
||||
// Hard-code a user --- this could easily be a database call, etc.
|
||||
if email != "test" || password != "test" {
|
||||
return errors.New("invalid credentials")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var claimsFunc = func(userId string) (map[string]interface{}, error) {
|
||||
currentTime := time.Now()
|
||||
return map[string]interface{}{
|
||||
"iat": currentTime.Unix(),
|
||||
"exp": currentTime.Add(time.Minute * 60 * 24).Unix(),
|
||||
"sub": userId,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var verifyClaimsFunc = func(claims []byte) error {
|
||||
currentTime := time.Now()
|
||||
var c struct {
|
||||
Exp int64
|
||||
Iat int64
|
||||
Sub string
|
||||
}
|
||||
err := json.Unmarshal(claims, &c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if currentTime.After(time.Unix(c.Exp, 0)) {
|
||||
return errors.New("this token has expired!")
|
||||
}
|
||||
if c.Sub != "test" {
|
||||
return errors.New("who are you??!")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
config := &jwt.Config{
|
||||
Secret: "password",
|
||||
Auth: authFunc,
|
||||
Claims: claimsFunc,
|
||||
}
|
||||
j, err := jwt.New(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
protect := http.HandlerFunc(protectMe)
|
||||
http.Handle("/authenticate", j.GenerateToken())
|
||||
http.Handle("/secure", j.Secure(protect, verifyClaimsFunc))
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
```
|
||||
|
||||
# Installation
|
||||
|
||||
$ go get github.com/thermokarst/jwt
|
||||
|
||||
# Usage
|
||||
|
||||
**This is a work in progress**
|
||||
|
||||
Create a new instance of the middleware by passing in a configuration for your
|
||||
app. The config includes a shared secret (this middleware only builds HS256
|
||||
tokens), a function for authenticating user, and a function for generating a
|
||||
user's claims. The idea here is to be dead-simple for someone to drop this into
|
||||
a project and hit the ground running.
|
||||
|
||||
```go
|
||||
config := &jwt.Config{
|
||||
Secret: "password",
|
||||
Auth: authFunc, // func(string, string) error
|
||||
Claims: claimsFunc, // func(string) (map[string]interface{})
|
||||
}
|
||||
j, err := jwt.New(config)
|
||||
```
|
||||
|
||||
Once the middleware is instantiated, create a route for users to generate a JWT
|
||||
at.
|
||||
|
||||
```go
|
||||
http.Handle("/authenticate", j.GenerateToken())
|
||||
```
|
||||
|
||||
The auth function takes two arguments (the identity, and the authorization
|
||||
key), POSTed as a JSON-encoded body:
|
||||
|
||||
{"email":"user@example.com","password":"mypassword"}
|
||||
|
||||
These fields are static for now, but will be customizable in a later release.
|
||||
The claims are generated using the claims function provided in the
|
||||
configuration. This function is only run if the auth function verifies the
|
||||
user's identity, then the user's unique identifier (primary key id, UUID,
|
||||
email, whatever you want) is passed as a string to the claims function. Your
|
||||
function should return a `map[string]interface{}` with the desired claimset.
|
||||
|
||||
Routes are "secured" by calling the `Secure(http.Handler, jwt.VerifyClaimsFunc)`
|
||||
handler:
|
||||
|
||||
```go
|
||||
http.Handle("/secureendpoint", j.Secure(someHandler, verifyClaimsFunc))
|
||||
```
|
||||
|
||||
The claims verification function is called after the token has been parsed and
|
||||
validated: this is where you control how your application handles the claims
|
||||
contained within the JWT.
|
||||
|
||||
# Motivation
|
||||
|
||||
This work was prepared for a crypto/security class at the University of Alaska
|
||||
Fairbanks. I hope to use this in some of my projects, but please proceed with
|
||||
caution if you adopt this for your own work. As well, the API is still quite
|
||||
unstable, so be prepared for handling any changes.
|
||||
|
||||
# Tests
|
||||
|
||||
$ go test
|
||||
|
||||
# Contributors
|
||||
|
||||
Matthew Ryan Dillon (matthewrdillon@gmail.com)
|
||||
|
57
Godeps/_workspace/src/github.com/thermokarst/jwt/examples/net-http.go
generated
vendored
Normal file
57
Godeps/_workspace/src/github.com/thermokarst/jwt/examples/net-http.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/thermokarst/jwt"
|
||||
)
|
||||
|
||||
func protectMe(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "secured")
|
||||
}
|
||||
|
||||
func dontProtectMe(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "not secured")
|
||||
}
|
||||
|
||||
func main() {
|
||||
var authFunc = func(email string, password string) error {
|
||||
// Hard-code a user
|
||||
if email != "test" || password != "test" {
|
||||
return errors.New("invalid credentials")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var claimsFunc = func(string) (map[string]interface{}, error) {
|
||||
currentTime := time.Now()
|
||||
return map[string]interface{}{
|
||||
"iat": currentTime.Unix(),
|
||||
"exp": currentTime.Add(time.Minute * 60 * 24).Unix(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
var verifyClaimsFunc = func([]byte) error {
|
||||
// We don't really care about the claims, just approve as-is
|
||||
return nil
|
||||
}
|
||||
|
||||
config := &jwt.Config{
|
||||
Secret: "password",
|
||||
Auth: authFunc,
|
||||
Claims: claimsFunc,
|
||||
}
|
||||
j, err := jwt.New(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
protect := http.HandlerFunc(protectMe)
|
||||
dontProtect := http.HandlerFunc(dontProtectMe)
|
||||
http.Handle("/authenticate", j.GenerateToken())
|
||||
http.Handle("/secure", j.Secure(protect, verifyClaimsFunc))
|
||||
http.Handle("/insecure", dontProtect)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
275
Godeps/_workspace/src/github.com/thermokarst/jwt/jwt.go
generated
vendored
Normal file
275
Godeps/_workspace/src/github.com/thermokarst/jwt/jwt.go
generated
vendored
Normal file
|
@ -0,0 +1,275 @@
|
|||
// Package jwt implements a simple, opinionated net/http-compatible middleware for
|
||||
// integrating JSON Web Tokens (JWT).
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
typ = "JWT"
|
||||
alg = "HS256"
|
||||
)
|
||||
|
||||
// Errors introduced by this package.
|
||||
var (
|
||||
ErrMissingConfig = errors.New("missing configuration")
|
||||
ErrMissingSecret = errors.New("please provide a shared secret")
|
||||
ErrMissingAuthFunc = errors.New("please provide an auth function")
|
||||
ErrMissingClaimsFunc = errors.New("please provide a claims function")
|
||||
ErrEncoding = errors.New("error encoding value")
|
||||
ErrDecoding = errors.New("error decoding value")
|
||||
ErrMissingToken = errors.New("please provide a token")
|
||||
ErrMalformedToken = errors.New("please provide a valid token")
|
||||
ErrInvalidSignature = errors.New("signature could not be verified")
|
||||
ErrParsingCredentials = errors.New("error parsing credentials")
|
||||
)
|
||||
|
||||
// Config is a container for setting up the JWT middleware.
|
||||
type Config struct {
|
||||
Secret string
|
||||
Auth AuthFunc
|
||||
Claims ClaimsFunc
|
||||
}
|
||||
|
||||
// AuthFunc is a type for delegating user authentication to the client-code.
|
||||
type AuthFunc func(string, string) error
|
||||
|
||||
// ClaimsFunc is a type for delegating claims generation to the client-code.
|
||||
type ClaimsFunc func(string) (map[string]interface{}, error)
|
||||
|
||||
// VerifyClaimsFunc is a type for processing and validating JWT claims on one
|
||||
// or more route's in the client-code.
|
||||
type VerifyClaimsFunc func([]byte) error
|
||||
|
||||
// Middleware is where we store all the specifics related to the client's
|
||||
// JWT needs.
|
||||
type Middleware struct {
|
||||
secret string
|
||||
auth AuthFunc
|
||||
claims ClaimsFunc
|
||||
}
|
||||
|
||||
// New creates a new Middleware from a user-specified configuration.
|
||||
func New(c *Config) (*Middleware, error) {
|
||||
if c == nil {
|
||||
return nil, ErrMissingConfig
|
||||
}
|
||||
if c.Secret == "" {
|
||||
return nil, ErrMissingSecret
|
||||
}
|
||||
if c.Auth == nil {
|
||||
return nil, ErrMissingAuthFunc
|
||||
}
|
||||
if c.Claims == nil {
|
||||
return nil, ErrMissingClaimsFunc
|
||||
}
|
||||
m := &Middleware{
|
||||
secret: c.Secret,
|
||||
auth: c.Auth,
|
||||
claims: c.Claims,
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Secure wraps a client-specified http.Handler with a verification function,
|
||||
// as well as-built in parsing of the request's JWT. This allows each handler
|
||||
// to have it's own verification/validation protocol.
|
||||
func (m *Middleware) Secure(h http.Handler, v VerifyClaimsFunc) http.Handler {
|
||||
secureHandler := func(w http.ResponseWriter, r *http.Request) *jwtError {
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
return &jwtError{status: http.StatusUnauthorized, err: ErrMissingToken}
|
||||
}
|
||||
token := strings.Split(authHeader, " ")[1]
|
||||
tokenParts := strings.Split(token, ".")
|
||||
if len(tokenParts) != 3 {
|
||||
return &jwtError{status: http.StatusUnauthorized, err: ErrMalformedToken}
|
||||
}
|
||||
|
||||
// First, verify JOSE header
|
||||
header, err := decode(tokenParts[0])
|
||||
if err != nil {
|
||||
return &jwtError{
|
||||
status: http.StatusInternalServerError,
|
||||
err: err,
|
||||
message: fmt.Sprintf("decoding header (%v)", tokenParts[0]),
|
||||
}
|
||||
}
|
||||
var t struct {
|
||||
Typ string
|
||||
Alg string
|
||||
}
|
||||
err = json.Unmarshal(header, &t)
|
||||
if err != nil {
|
||||
return &jwtError{
|
||||
status: http.StatusInternalServerError,
|
||||
err: ErrMalformedToken,
|
||||
message: fmt.Sprintf("unmarshalling header (%s)", header),
|
||||
}
|
||||
}
|
||||
|
||||
// Then, verify signature
|
||||
mac := hmac.New(sha256.New, []byte(m.secret))
|
||||
message := []byte(strings.Join([]string{tokenParts[0], tokenParts[1]}, "."))
|
||||
mac.Write(message)
|
||||
expectedMac, err := encode(mac.Sum(nil))
|
||||
if err != nil {
|
||||
return &jwtError{status: http.StatusInternalServerError, err: err}
|
||||
}
|
||||
if !hmac.Equal([]byte(tokenParts[2]), []byte(expectedMac)) {
|
||||
return &jwtError{
|
||||
status: http.StatusUnauthorized,
|
||||
err: ErrInvalidSignature,
|
||||
message: fmt.Sprintf("checking signature (%v)", tokenParts[2]),
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, check claims
|
||||
claimSet, err := decode(tokenParts[1])
|
||||
if err != nil {
|
||||
return &jwtError{
|
||||
status: http.StatusInternalServerError,
|
||||
err: ErrDecoding,
|
||||
message: "decoding claims",
|
||||
}
|
||||
}
|
||||
err = v(claimSet)
|
||||
if err != nil {
|
||||
return &jwtError{
|
||||
status: http.StatusUnauthorized,
|
||||
err: err,
|
||||
message: "handling claims callback",
|
||||
}
|
||||
}
|
||||
|
||||
// If we make it this far, process the downstream handler
|
||||
h.ServeHTTP(w, r)
|
||||
return nil
|
||||
}
|
||||
return errorHandler(secureHandler)
|
||||
}
|
||||
|
||||
// GenerateToken returns a middleware that parsing an incoming request for a JWT,
|
||||
// calls the client-supplied auth function, and if successful, returns a JWT to
|
||||
// the requester.
|
||||
func (m *Middleware) GenerateToken() http.Handler {
|
||||
generateHandler := func(w http.ResponseWriter, r *http.Request) *jwtError {
|
||||
var b map[string]string
|
||||
err := json.NewDecoder(r.Body).Decode(&b)
|
||||
if err != nil {
|
||||
return &jwtError{
|
||||
status: http.StatusInternalServerError,
|
||||
err: ErrParsingCredentials,
|
||||
message: "parsing authorization",
|
||||
}
|
||||
}
|
||||
err = m.auth(b["email"], b["password"])
|
||||
if err != nil {
|
||||
return &jwtError{
|
||||
status: http.StatusInternalServerError,
|
||||
err: err,
|
||||
message: "performing authorization",
|
||||
}
|
||||
}
|
||||
|
||||
// For now, the header will be static
|
||||
header, err := encode(fmt.Sprintf(`{"typ":%q,"alg":%q}`, typ, alg))
|
||||
if err != nil {
|
||||
return &jwtError{
|
||||
status: http.StatusInternalServerError,
|
||||
err: ErrEncoding,
|
||||
message: "encoding header",
|
||||
}
|
||||
}
|
||||
|
||||
// Generate claims for user
|
||||
claims, err := m.claims(b["email"])
|
||||
if err != nil {
|
||||
return &jwtError{
|
||||
status: http.StatusInternalServerError,
|
||||
err: err,
|
||||
message: "generating claims",
|
||||
}
|
||||
}
|
||||
|
||||
claimsJSON, err := json.Marshal(claims)
|
||||
if err != nil {
|
||||
return &jwtError{
|
||||
status: http.StatusInternalServerError,
|
||||
err: ErrEncoding,
|
||||
message: "marshalling claims",
|
||||
}
|
||||
}
|
||||
|
||||
claimsSet, err := encode(claimsJSON)
|
||||
if err != nil {
|
||||
return &jwtError{
|
||||
status: http.StatusInternalServerError,
|
||||
err: ErrEncoding,
|
||||
message: "encoding claims",
|
||||
}
|
||||
}
|
||||
|
||||
toSig := strings.Join([]string{header, claimsSet}, ".")
|
||||
|
||||
h := hmac.New(sha256.New, []byte(m.secret))
|
||||
h.Write([]byte(toSig))
|
||||
sig, err := encode(h.Sum(nil))
|
||||
if err != nil {
|
||||
return &jwtError{
|
||||
status: http.StatusInternalServerError,
|
||||
err: ErrEncoding,
|
||||
message: "encoding signature",
|
||||
}
|
||||
}
|
||||
|
||||
response := strings.Join([]string{toSig, sig}, ".")
|
||||
w.Write([]byte(response))
|
||||
return nil
|
||||
}
|
||||
|
||||
return errorHandler(generateHandler)
|
||||
}
|
||||
|
||||
type jwtError struct {
|
||||
status int
|
||||
err error
|
||||
message string
|
||||
}
|
||||
|
||||
type errorHandler func(http.ResponseWriter, *http.Request) *jwtError
|
||||
|
||||
func (e errorHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if err := e(w, r); err != nil {
|
||||
if err.message != "" {
|
||||
log.Printf("error (%v) while %s", err.err, err.message)
|
||||
}
|
||||
http.Error(w, err.err.Error(), err.status)
|
||||
}
|
||||
}
|
||||
|
||||
func encode(s interface{}) (string, error) {
|
||||
var r []byte
|
||||
switch v := s.(type) {
|
||||
case string:
|
||||
r = []byte(v)
|
||||
case []byte:
|
||||
r = v
|
||||
default:
|
||||
return "", ErrEncoding
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(r), nil
|
||||
}
|
||||
|
||||
func decode(s string) ([]byte, error) {
|
||||
return base64.StdEncoding.DecodeString(s)
|
||||
}
|
217
Godeps/_workspace/src/github.com/thermokarst/jwt/jwt_test.go
generated
vendored
Normal file
217
Godeps/_workspace/src/github.com/thermokarst/jwt/jwt_test.go
generated
vendored
Normal file
|
@ -0,0 +1,217 @@
|
|||
package jwt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var testHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("test"))
|
||||
})
|
||||
|
||||
var authFunc = func(email, password string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var claimsFunc = func(id string) (map[string]interface{}, error) {
|
||||
currentTime := time.Now()
|
||||
return map[string]interface{}{
|
||||
"iat": currentTime.Unix(),
|
||||
"exp": currentTime.Add(time.Minute * 60 * 24).Unix(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
var verifyClaimsFunc = func(claims []byte) error {
|
||||
currentTime := time.Now()
|
||||
var c struct {
|
||||
Exp int64
|
||||
Iat int64
|
||||
}
|
||||
err := json.Unmarshal(claims, &c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if currentTime.After(time.Unix(c.Exp, 0)) {
|
||||
return errors.New("expired")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newMiddlewareOrFatal(t *testing.T) *Middleware {
|
||||
config := &Config{
|
||||
Secret: "password",
|
||||
Auth: authFunc,
|
||||
Claims: claimsFunc,
|
||||
}
|
||||
middleware, err := New(config)
|
||||
if err != nil {
|
||||
t.Fatalf("new middleware: %v", err)
|
||||
}
|
||||
return middleware
|
||||
}
|
||||
|
||||
func newToken(t *testing.T) (string, *Middleware) {
|
||||
middleware := newMiddlewareOrFatal(t)
|
||||
authBody := map[string]interface{}{
|
||||
"email": "user@example.com",
|
||||
"password": "password",
|
||||
}
|
||||
body, err := json.Marshal(authBody)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
ts := httptest.NewServer(middleware.GenerateToken())
|
||||
defer ts.Close()
|
||||
|
||||
resp, err := http.Post(ts.URL, "application/json", bytes.NewReader(body))
|
||||
respBody, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
return string(respBody), middleware
|
||||
}
|
||||
|
||||
func TestNewJWTMiddleware(t *testing.T) {
|
||||
middleware := newMiddlewareOrFatal(t)
|
||||
if middleware.secret != "password" {
|
||||
t.Errorf("wanted password, got %v", middleware.secret)
|
||||
}
|
||||
err := middleware.auth("", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
claimsVal, err := middleware.claims("1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := claimsVal["iat"]; !ok {
|
||||
t.Errorf("wanted a claims set, got %v", claimsVal)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewJWTMiddlewareNoConfig(t *testing.T) {
|
||||
cases := map[*Config]error{
|
||||
nil: ErrMissingConfig,
|
||||
&Config{}: ErrMissingSecret,
|
||||
&Config{
|
||||
Auth: authFunc,
|
||||
Claims: claimsFunc,
|
||||
}: ErrMissingSecret,
|
||||
&Config{
|
||||
Secret: "secret",
|
||||
Claims: claimsFunc,
|
||||
}: ErrMissingAuthFunc,
|
||||
&Config{
|
||||
Auth: authFunc,
|
||||
Secret: "secret",
|
||||
}: ErrMissingClaimsFunc,
|
||||
}
|
||||
for config, jwtErr := range cases {
|
||||
_, err := New(config)
|
||||
if err != jwtErr {
|
||||
t.Errorf("wanted error: %v, got error: %v using config: %v", jwtErr, err, config)
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestGenerateTokenHandler(t *testing.T) {
|
||||
token, m := newToken(t)
|
||||
j := strings.Split(token, ".")
|
||||
|
||||
header := base64.StdEncoding.EncodeToString([]byte(`{"typ":"JWT","alg":"HS256"}`))
|
||||
if j[0] != header {
|
||||
t.Errorf("wanted %v, got %v", header, j[0])
|
||||
}
|
||||
|
||||
claims, err := base64.StdEncoding.DecodeString(j[1])
|
||||
var c struct {
|
||||
Exp int
|
||||
Iat int
|
||||
}
|
||||
err = json.Unmarshal(claims, &c)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
duration := time.Duration(c.Exp-c.Iat) * time.Second
|
||||
d := time.Minute * 60 * 24
|
||||
if duration != d {
|
||||
t.Errorf("wanted %v, got %v", d, duration)
|
||||
}
|
||||
mac := hmac.New(sha256.New, []byte(m.secret))
|
||||
message := []byte(strings.Join([]string{j[0], j[1]}, "."))
|
||||
mac.Write(message)
|
||||
expectedMac := base64.StdEncoding.EncodeToString(mac.Sum(nil))
|
||||
if !hmac.Equal([]byte(j[2]), []byte(expectedMac)) {
|
||||
t.Errorf("wanted %v, got %v", expectedMac, j[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecureHandlerNoToken(t *testing.T) {
|
||||
middleware := newMiddlewareOrFatal(t)
|
||||
resp := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
||||
middleware.Secure(testHandler, verifyClaimsFunc).ServeHTTP(resp, req)
|
||||
body := strings.TrimSpace(resp.Body.String())
|
||||
if body != ErrMissingToken.Error() {
|
||||
t.Errorf("wanted %q, got %q", ErrMissingToken.Error(), body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecureHandlerBadToken(t *testing.T) {
|
||||
middleware := newMiddlewareOrFatal(t)
|
||||
resp := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
||||
req.Header.Set("Authorization", "Bearer abcdefg")
|
||||
middleware.Secure(testHandler, verifyClaimsFunc).ServeHTTP(resp, req)
|
||||
body := strings.TrimSpace(resp.Body.String())
|
||||
if body != ErrMalformedToken.Error() {
|
||||
t.Errorf("wanted %q, got %q", ErrMalformedToken.Error(), body)
|
||||
}
|
||||
|
||||
resp = httptest.NewRecorder()
|
||||
req, _ = http.NewRequest("GET", "http://example.com", nil)
|
||||
req.Header.Set("Authorization", "Bearer abcd.abcd.abcd")
|
||||
middleware.Secure(testHandler, verifyClaimsFunc).ServeHTTP(resp, req)
|
||||
body = strings.TrimSpace(resp.Body.String())
|
||||
if body != ErrMalformedToken.Error() {
|
||||
t.Errorf("wanted %q, got %q", ErrMalformedToken.Error(), body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecureHandlerBadSignature(t *testing.T) {
|
||||
token, middleware := newToken(t)
|
||||
parts := strings.Split(token, ".")
|
||||
token = strings.Join([]string{parts[0], parts[1], "abcd"}, ".")
|
||||
resp := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
middleware.Secure(testHandler, verifyClaimsFunc).ServeHTTP(resp, req)
|
||||
body := strings.TrimSpace(resp.Body.String())
|
||||
if body != ErrInvalidSignature.Error() {
|
||||
t.Errorf("wanted %s, got %s", ErrInvalidSignature.Error(), body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecureHandlerGoodToken(t *testing.T) {
|
||||
token, middleware := newToken(t)
|
||||
resp := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "http://example.com", nil)
|
||||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
middleware.Secure(testHandler, verifyClaimsFunc).ServeHTTP(resp, req)
|
||||
body := strings.TrimSpace(resp.Body.String())
|
||||
if body != "test" {
|
||||
t.Errorf("wanted %s, got %s", "test", body)
|
||||
}
|
||||
}
|
38
auth.go
38
auth.go
|
@ -1,38 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
tokenName = "AccessToken"
|
||||
)
|
||||
|
||||
var (
|
||||
verifyKey, signKey []byte
|
||||
errWhileSigningToken = errors.New("error while signing token")
|
||||
errPleaseLogIn = errors.New("please log in")
|
||||
errWhileParsingCookie = errors.New("error while parsing cookie")
|
||||
errTokenExpired = errors.New("token expired")
|
||||
errGenericError = errors.New("generic error")
|
||||
errAccessDenied = errors.New("insufficient privileges")
|
||||
)
|
||||
|
||||
func setupCerts() error {
|
||||
// openssl genrsa -out app.rsa 1024
|
||||
signkey := os.Getenv("PRIVATE_KEY")
|
||||
if signkey == "" {
|
||||
return errors.New("please set PRIVATE_KEY")
|
||||
}
|
||||
signKey = []byte(signkey)
|
||||
|
||||
// openssl rsa -in app.rsa -pubout > app.rsa.pub
|
||||
verifykey := os.Getenv("PUBLIC_KEY")
|
||||
if verifykey == "" {
|
||||
return errors.New("please set PUBLIC_KEY")
|
||||
}
|
||||
verifyKey = []byte(verifykey)
|
||||
|
||||
return nil
|
||||
}
|
135
handlers.go
135
handlers.go
|
@ -1,42 +1,109 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/thermokarst/jwt"
|
||||
)
|
||||
|
||||
func Handler() http.Handler {
|
||||
claimsFunc := func(email string) (map[string]interface{}, error) {
|
||||
currentTime := time.Now()
|
||||
user, err := dbGetUserByEmail(email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"name": user.Name,
|
||||
"iss": "bactdb",
|
||||
"sub": user.Id,
|
||||
"role": user.Role,
|
||||
"iat": currentTime.Unix(),
|
||||
"exp": currentTime.Add(time.Minute * 60 * 24).Unix(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
verifyClaims := func(claims []byte) error {
|
||||
currentTime := time.Now()
|
||||
var c struct {
|
||||
Name string
|
||||
Iss string
|
||||
Sub int64
|
||||
Role string
|
||||
Iat int64
|
||||
Exp int64
|
||||
}
|
||||
err := json.Unmarshal(claims, &c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if currentTime.After(time.Unix(c.Exp, 0)) {
|
||||
return errors.New("this token has expired")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
config := &jwt.Config{
|
||||
Secret: os.Getenv("SECRET"),
|
||||
Auth: dbAuthenticate,
|
||||
Claims: claimsFunc,
|
||||
}
|
||||
|
||||
j, err := jwt.New(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
m := mux.NewRouter()
|
||||
|
||||
// Non-auth routes
|
||||
m.HandleFunc("/authenticate", serveAuthenticateUser).Methods("POST")
|
||||
m.Handle("/authenticate", tokenHandler(j.GenerateToken())).Methods("POST")
|
||||
|
||||
// Auth routes
|
||||
m.Handle("/users", authHandler(serveUsersList)).Methods("GET")
|
||||
m.Handle("/users/{Id:.+}", authHandler(serveUser)).Methods("GET")
|
||||
m.Handle("/users", j.Secure(http.HandlerFunc(serveUsersList), verifyClaims)).Methods("GET")
|
||||
m.Handle("/users/{Id:.+}", j.Secure(http.HandlerFunc(serveUser), verifyClaims)).Methods("GET")
|
||||
|
||||
// Path-based pattern matching subrouter
|
||||
s := m.PathPrefix("/{genus}").Subrouter()
|
||||
|
||||
// Strains
|
||||
s.Handle("/strains", authHandler(serveStrainsList)).Methods("GET")
|
||||
s.Handle("/strains/{Id:.+}", authHandler(serveStrain)).Methods("GET")
|
||||
type r struct {
|
||||
f http.HandlerFunc
|
||||
m string
|
||||
}
|
||||
|
||||
// Measurements
|
||||
s.Handle("/measurements", authHandler(serveMeasurementsList)).Methods("GET")
|
||||
s.Handle("/measurements/{Id:.+}", authHandler(serveMeasurement)).Methods("GET")
|
||||
routes := map[string]r{
|
||||
"/strains": r{serveStrainsList, "GET"},
|
||||
"/strains/{Id:.+}": r{serveStrain, "GET"},
|
||||
"/measurements": r{serveMeasurementsList, "GET"},
|
||||
"/measurements/{Id:.+}": r{serveMeasurement, "GET"},
|
||||
"/characteristics": r{serveCharacteristicsList, "GET"},
|
||||
"/characteristics/{Id:.+}": r{serveCharacteristic, "GET"},
|
||||
}
|
||||
|
||||
// Characteristics
|
||||
s.Handle("/characteristics", authHandler(serveCharacteristicsList)).Methods("GET")
|
||||
s.Handle("/characteristics/{Id:.+}", authHandler(serveCharacteristic)).Methods("GET")
|
||||
for path, route := range routes {
|
||||
s.Handle(path, j.Secure(http.HandlerFunc(route.f), verifyClaims)).Methods(route.m)
|
||||
}
|
||||
|
||||
return corsHandler(m)
|
||||
}
|
||||
|
||||
func tokenHandler(h http.Handler) http.Handler {
|
||||
token := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
// Hackish, but we want the token in a JSON object
|
||||
w.Write([]byte(`{"token":"`))
|
||||
h.ServeHTTP(w, r)
|
||||
w.Write([]byte(`"}`))
|
||||
}
|
||||
return http.HandlerFunc(token)
|
||||
}
|
||||
|
||||
func corsHandler(h http.Handler) http.Handler {
|
||||
cors := func(w http.ResponseWriter, r *http.Request) {
|
||||
domains := os.Getenv("DOMAINS")
|
||||
|
@ -56,45 +123,3 @@ func corsHandler(h http.Handler) http.Handler {
|
|||
}
|
||||
return http.HandlerFunc(cors)
|
||||
}
|
||||
|
||||
// Only accessible with a valid token
|
||||
func authHandler(f func(http.ResponseWriter, *http.Request)) http.Handler {
|
||||
h := http.HandlerFunc(f)
|
||||
auth := func(w http.ResponseWriter, r *http.Request) {
|
||||
authHeader := r.Header.Get("Authorization")
|
||||
if authHeader == "" {
|
||||
http.Error(w, errPleaseLogIn.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
s := strings.Split(authHeader, " ")
|
||||
|
||||
// Validate the token
|
||||
token, err := jwt.Parse(s[1], func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(verifyKey), nil
|
||||
})
|
||||
|
||||
// Branch out into the possible error from signing
|
||||
switch err.(type) {
|
||||
case nil: // No error
|
||||
if !token.Valid { // But may still be invalid
|
||||
http.Error(w, errPleaseLogIn.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
case *jwt.ValidationError: // Something was wrong during the validation
|
||||
vErr := err.(*jwt.ValidationError)
|
||||
switch vErr.Errors {
|
||||
case jwt.ValidationErrorExpired:
|
||||
http.Error(w, errTokenExpired.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
default:
|
||||
http.Error(w, errGenericError.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
default: // Something else went wrong
|
||||
http.Error(w, errGenericError.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
h.ServeHTTP(w, r)
|
||||
}
|
||||
return http.HandlerFunc(auth)
|
||||
}
|
||||
|
|
5
main.go
5
main.go
|
@ -90,11 +90,6 @@ func cmdServe(c *cli.Context) {
|
|||
}
|
||||
httpAddr := fmt.Sprintf(":%v", addr)
|
||||
|
||||
err = setupCerts()
|
||||
if err != nil {
|
||||
log.Fatal("SetupCerts: ", err)
|
||||
}
|
||||
|
||||
m := http.NewServeMux()
|
||||
m.Handle("/api/", http.StripPrefix("/api", Handler()))
|
||||
|
||||
|
|
22
migrate.sh
22
migrate.sh
|
@ -1,26 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXAIBAAKBgQCcjobUzMiX4DUmesqDtIWSZoYc2O2OYN3PwAbSxm+isuCMcXtW
|
||||
YfjTtGmYlcVIJUVEv52z21bIQfKjIV/pKjq0Rd6xoVtsuupcQHilgOVX9t01Y55G
|
||||
VyyUXDz3JgSPrBRM/z6UaU+hwq+xaPAiDn5P3/s419O1WedEyach6Ep4mQIDAQAB
|
||||
AoGAQdiARQhMZfRa5nBGtNY8R7LvPTrPz05WfIZbWFM1qMxrPSaNpWtXaFM9BnwX
|
||||
mZxzYdLl1TuvaFK0ZoAnAr5MKdW/CETjhTt8qfIkaJIEZBWTDv2luJKXNFFpGSmq
|
||||
jvWken3wYM7txPh7p/ApHRVMkiOsaKjZRCy1N5l3hEE9ivECQQDKoZ0mBCreBWB9
|
||||
fEK6L+BxTDDkwo0G0qgnGZZjlbOBLKsZL2vVoHXc4g0+tQUsOdjjFwPi6kUGXYTM
|
||||
I8qTJb3/AkEAxcpXpknAjaLX4s0uJj/eL5KJE7YbiQtJVjA+vd2/JtLTxL8JrLMJ
|
||||
N0Rn1dEJzbVLVZ5vrdOCxYBGHRlDHbL5ZwJBAJRAbCadU/O+wVruKC/qyW57TSaB
|
||||
xQah54875FEV/RBcaw5xKJdS4Ajshr5DWPaDmFCFzT0fI8NFdtyYryS7r2cCQBnT
|
||||
MqCOrqqPoZqGackqu6sAeg9tzqiVJa0wPXDy/Btomaftvaij88cYkmozkhEe48g5
|
||||
GKHcpQ1+kykHfGDrVm8CQD1vzUvgedfKFJHPC+I5GSVNxmgY9zqjVJ15Flmdjx8n
|
||||
pUL6zWr75vw+ZZDQWNOa8gdiE/l9fomcVTUA49fui94=
|
||||
-----END RSA PRIVATE KEY-----" \
|
||||
PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCcjobUzMiX4DUmesqDtIWSZoYc
|
||||
2O2OYN3PwAbSxm+isuCMcXtWYfjTtGmYlcVIJUVEv52z21bIQfKjIV/pKjq0Rd6x
|
||||
oVtsuupcQHilgOVX9t01Y55GVyyUXDz3JgSPrBRM/z6UaU+hwq+xaPAiDn5P3/s4
|
||||
19O1WedEyach6Ep4mQIDAQAB
|
||||
-----END PUBLIC KEY-----" \
|
||||
SECRET=secret \
|
||||
PGDATABASE=bactdbtest \
|
||||
DOMAINS="http://localhost:4200" \
|
||||
bash -c 'go run *.go migrate --drop'
|
||||
|
|
22
serve.sh
22
serve.sh
|
@ -1,26 +1,6 @@
|
|||
#!/bin/sh
|
||||
|
||||
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXAIBAAKBgQCcjobUzMiX4DUmesqDtIWSZoYc2O2OYN3PwAbSxm+isuCMcXtW
|
||||
YfjTtGmYlcVIJUVEv52z21bIQfKjIV/pKjq0Rd6xoVtsuupcQHilgOVX9t01Y55G
|
||||
VyyUXDz3JgSPrBRM/z6UaU+hwq+xaPAiDn5P3/s419O1WedEyach6Ep4mQIDAQAB
|
||||
AoGAQdiARQhMZfRa5nBGtNY8R7LvPTrPz05WfIZbWFM1qMxrPSaNpWtXaFM9BnwX
|
||||
mZxzYdLl1TuvaFK0ZoAnAr5MKdW/CETjhTt8qfIkaJIEZBWTDv2luJKXNFFpGSmq
|
||||
jvWken3wYM7txPh7p/ApHRVMkiOsaKjZRCy1N5l3hEE9ivECQQDKoZ0mBCreBWB9
|
||||
fEK6L+BxTDDkwo0G0qgnGZZjlbOBLKsZL2vVoHXc4g0+tQUsOdjjFwPi6kUGXYTM
|
||||
I8qTJb3/AkEAxcpXpknAjaLX4s0uJj/eL5KJE7YbiQtJVjA+vd2/JtLTxL8JrLMJ
|
||||
N0Rn1dEJzbVLVZ5vrdOCxYBGHRlDHbL5ZwJBAJRAbCadU/O+wVruKC/qyW57TSaB
|
||||
xQah54875FEV/RBcaw5xKJdS4Ajshr5DWPaDmFCFzT0fI8NFdtyYryS7r2cCQBnT
|
||||
MqCOrqqPoZqGackqu6sAeg9tzqiVJa0wPXDy/Btomaftvaij88cYkmozkhEe48g5
|
||||
GKHcpQ1+kykHfGDrVm8CQD1vzUvgedfKFJHPC+I5GSVNxmgY9zqjVJ15Flmdjx8n
|
||||
pUL6zWr75vw+ZZDQWNOa8gdiE/l9fomcVTUA49fui94=
|
||||
-----END RSA PRIVATE KEY-----" \
|
||||
PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
|
||||
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCcjobUzMiX4DUmesqDtIWSZoYc
|
||||
2O2OYN3PwAbSxm+isuCMcXtWYfjTtGmYlcVIJUVEv52z21bIQfKjIV/pKjq0Rd6x
|
||||
oVtsuupcQHilgOVX9t01Y55GVyyUXDz3JgSPrBRM/z6UaU+hwq+xaPAiDn5P3/s4
|
||||
19O1WedEyach6Ep4mQIDAQAB
|
||||
-----END PUBLIC KEY-----" \
|
||||
SECRET=secret \
|
||||
PGDATABASE=bactdbtest \
|
||||
DOMAINS="http://localhost:4200" \
|
||||
bash -c 'go run *.go serve'
|
||||
|
|
81
users.go
81
users.go
|
@ -8,13 +8,13 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/gorilla/mux"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUserNotFound = errors.New("user not found")
|
||||
ErrUserNotFound = errors.New("user not found")
|
||||
ErrInvalidEmailOrPassword = errors.New("Invalid email or password")
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -87,65 +87,16 @@ func serveUser(w http.ResponseWriter, r *http.Request) {
|
|||
w.Write(data)
|
||||
}
|
||||
|
||||
func serveAuthenticateUser(w http.ResponseWriter, r *http.Request) {
|
||||
var a struct {
|
||||
Email string
|
||||
Password string
|
||||
func dbAuthenticate(email string, password string) error {
|
||||
var user User
|
||||
q := `SELECT * FROM users WHERE lower(email)=lower($1);`
|
||||
if err := DBH.SelectOne(&user, q, email); err != nil {
|
||||
return ErrInvalidEmailOrPassword
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&a); err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
|
||||
return ErrInvalidEmailOrPassword
|
||||
}
|
||||
user_session, err := dbAuthenticate(a.Email, a.Password)
|
||||
if err != nil {
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte(`{"error":"Invalid email or password"}`))
|
||||
return
|
||||
}
|
||||
|
||||
currentTime := time.Now()
|
||||
|
||||
t := jwt.New(jwt.SigningMethodRS256)
|
||||
t.Claims["name"] = user_session.Name
|
||||
t.Claims["iss"] = "bactdb"
|
||||
t.Claims["sub"] = user_session.Email
|
||||
t.Claims["role"] = user_session.Role
|
||||
t.Claims["iat"] = currentTime.Unix()
|
||||
t.Claims["exp"] = currentTime.Add(time.Minute * 60 * 24).Unix()
|
||||
tokenString, err := t.SignedString(signKey)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
token := struct {
|
||||
Token string `json:"token"`
|
||||
UserID int64 `json:"user_id"`
|
||||
}{
|
||||
Token: tokenString,
|
||||
UserID: user_session.Id,
|
||||
}
|
||||
data, err := json.Marshal(token)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
|
||||
w.Write(data)
|
||||
}
|
||||
|
||||
func dbAuthenticate(email string, password string) (*User, error) {
|
||||
var users []User
|
||||
if err := DBH.Select(&users, `SELECT * FROM users WHERE lower(email)=lower($1);`, email); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(users) == 0 {
|
||||
return nil, ErrUserNotFound
|
||||
}
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(users[0].Password), []byte(password)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &users[0], nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func dbGetUsers(opt *ListOptions) ([]*User, error) {
|
||||
|
@ -168,3 +119,15 @@ func dbGetUser(id int64) (*User, error) {
|
|||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func dbGetUserByEmail(email string) (*User, error) {
|
||||
var user User
|
||||
q := `SELECT * FROM users WHERE lower(email)=lower($1);`
|
||||
if err := DBH.SelectOne(&user, q, email); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, ErrUserNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
|
Reference in a new issue