Update godeps

This commit is contained in:
Matthew Dillon 2015-05-26 13:26:36 -08:00
parent 1298cffca2
commit 06938a9f2b
7 changed files with 238 additions and 103 deletions

4
Godeps/Godeps.json generated
View file

@ -17,7 +17,7 @@
},
{
"ImportPath": "github.com/gorilla/mux",
"Rev": "8a875a034c69b940914d83ea03d3f1299b4d094b"
"Rev": "94903de8c98a68d8b4483c529b26a5d146e386a2"
},
{
"ImportPath": "github.com/gorilla/schema",
@ -39,7 +39,7 @@
},
{
"ImportPath": "github.com/thermokarst/jwt",
"Rev": "774185ba9ebde2a02689173b736b760a13a6b777"
"Rev": "66ca404d841ed908aa6cc9361107b0c363c94cd8"
},
{
"ImportPath": "golang.org/x/crypto/bcrypt",

View file

@ -135,6 +135,42 @@ func TestHost(t *testing.T) {
path: "",
shouldMatch: false,
},
{
title: "Path route with single pattern with pipe, match",
route: new(Route).Path("/{category:a|b/c}"),
request: newRequest("GET", "http://localhost/a"),
vars: map[string]string{"category": "a"},
host: "",
path: "/a",
shouldMatch: true,
},
{
title: "Path route with single pattern with pipe, match",
route: new(Route).Path("/{category:a|b/c}"),
request: newRequest("GET", "http://localhost/b/c"),
vars: map[string]string{"category": "b/c"},
host: "",
path: "/b/c",
shouldMatch: true,
},
{
title: "Path route with multiple patterns with pipe, match",
route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"),
request: newRequest("GET", "http://localhost/a/product_name/1"),
vars: map[string]string{"category": "a", "product": "product_name", "id": "1"},
host: "",
path: "/a/product_name/1",
shouldMatch: true,
},
{
title: "Path route with multiple patterns with pipe, match",
route: new(Route).Path("/{category:a|b/c}/{product}/{id:[0-9]+}"),
request: newRequest("GET", "http://localhost/b/c/product_name/1"),
vars: map[string]string{"category": "b/c", "product": "product_name", "id": "1"},
host: "",
path: "/b/c/product_name/1",
shouldMatch: true,
},
}
for _, test := range tests {
testRoute(t, test)

View file

@ -9,6 +9,7 @@ your application:
package main
import (
"encoding/json"
"errors"
"fmt"
"net/http"
@ -21,60 +22,90 @@ 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.
func dontProtectMe(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "not secured")
}
func auth(email string, password string) error {
// Hard-code a user
if email != "test" || password != "test" {
return errors.New("invalid credentials")
}
return nil
}
var claimsFunc = func(userId string) (map[string]interface{}, error) {
func setClaims(id 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 {
func verifyClaims(claims []byte, r *http.Request) error {
currentTime := time.Now()
var c struct {
Exp int64
Iat int64
Sub string
}
err := json.Unmarshal(claims, &c)
if err != nil {
return err
Exp int64
}
_ = json.Unmarshal(claims, &c)
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 errors.New("this token has expired")
}
return nil
}
func main() {
config := &jwt.Config{
Secret: "password",
Auth: authFunc,
Claims: claimsFunc,
Auth: auth,
Claims: setClaims,
}
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("/secure", j.Secure(protect, verifyClaims))
http.Handle("/insecure", dontProtect)
http.ListenAndServe(":8080", nil)
}
```
```shell
$ http GET :8080/secure
HTTP/1.1 401 Unauthorized
Content-Length: 23
Content-Type: text/plain; charset=utf-8
Date: Fri, 08 May 2015 06:43:35 GMT
please provide a token
$ http POST :8080/authenticate email=test password=test
HTTP/1.1 200 OK
Content-Length: 130
Content-Type: text/plain; charset=utf-8
Date: Fri, 08 May 2015 06:31:42 GMT
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0MzExNTI0ODAsImlhdCI6MTQzMTA2NjA4MH0=.UbJmLqOF4bTH/8+o6CrZfoi1Fu7zTDfCV0kwMQyzmos=
$ http GET :8080/secure Authorization:"Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0MzExNTI0ODAsImlhdCI6MTQzMTA2NjA4MH0=.UbJmLqOF4bTH/8+o6CrZfoi1Fu7zTDfCV0kwMQyzmos="
HTTP/1.1 200 OK
Content-Length: 7
Content-Type: text/plain; charset=utf-8
Date: Fri, 08 May 2015 06:38:30 GMT
secured
```
# Installation
$ go get github.com/thermokarst/jwt
@ -98,6 +129,10 @@ config := &jwt.Config{
j, err := jwt.New(config)
```
You can also customize the field names by specifying `IdentityField` and
`VerifyField` in the `Config` struct, if you want the credentials to be
something other than `"email"` and `"password"`.
Once the middleware is instantiated, create a route for users to generate a JWT
at.

View file

@ -1,6 +1,7 @@
package main
import (
"encoding/json"
"errors"
"fmt"
"net/http"
@ -17,8 +18,7 @@ func dontProtectMe(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "not secured")
}
func main() {
var authFunc = func(email string, password string) error {
func auth(email string, password string) error {
// Hard-code a user
if email != "test" || password != "test" {
return errors.New("invalid credentials")
@ -26,7 +26,7 @@ func main() {
return nil
}
var claimsFunc = func(string) (map[string]interface{}, error) {
func setClaims(id string) (map[string]interface{}, error) {
currentTime := time.Now()
return map[string]interface{}{
"iat": currentTime.Unix(),
@ -34,24 +34,36 @@ func main() {
}, nil
}
var verifyClaimsFunc = func([]byte) error {
// We don't really care about the claims, just approve as-is
func verifyClaims(claims []byte, r *http.Request) error {
currentTime := time.Now()
var c struct {
Iat int64
Exp int64
}
_ = json.Unmarshal(claims, &c)
if currentTime.After(time.Unix(c.Exp, 0)) {
return errors.New("this token has expired")
}
return nil
}
func main() {
config := &jwt.Config{
Secret: "password",
Auth: authFunc,
Claims: claimsFunc,
Auth: auth,
Claims: setClaims,
}
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("/secure", j.Secure(protect, verifyClaims))
http.Handle("/insecure", dontProtect)
http.ListenAndServe(":8080", nil)
}

View file

@ -31,15 +31,9 @@ var (
ErrMalformedToken = errors.New("please provide a valid token")
ErrInvalidSignature = errors.New("signature could not be verified")
ErrParsingCredentials = errors.New("error parsing credentials")
ErrInvalidMethod = errors.New("invalid request method")
)
// 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
@ -47,8 +41,17 @@ type AuthFunc func(string, string) error
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
// or more routes in the client-code.
type VerifyClaimsFunc func([]byte, *http.Request) error
// Config is a container for setting up the JWT middleware.
type Config struct {
Secret string
Auth AuthFunc
Claims ClaimsFunc
IdentityField string
VerifyField string
}
// Middleware is where we store all the specifics related to the client's
// JWT needs.
@ -56,6 +59,8 @@ type Middleware struct {
secret string
auth AuthFunc
claims ClaimsFunc
identityField string
verifyField string
}
// New creates a new Middleware from a user-specified configuration.
@ -72,10 +77,18 @@ func New(c *Config) (*Middleware, error) {
if c.Claims == nil {
return nil, ErrMissingClaimsFunc
}
if c.IdentityField == "" {
c.IdentityField = "email"
}
if c.VerifyField == "" {
c.VerifyField = "password"
}
m := &Middleware{
secret: c.Secret,
auth: c.Auth,
claims: c.Claims,
identityField: c.IdentityField,
verifyField: c.VerifyField,
}
return m, nil
}
@ -142,7 +155,7 @@ func (m *Middleware) Secure(h http.Handler, v VerifyClaimsFunc) http.Handler {
message: "decoding claims",
}
}
err = v(claimSet)
err = v(claimSet, r)
if err != nil {
return &jwtError{
status: http.StatusUnauthorized,
@ -163,6 +176,13 @@ func (m *Middleware) Secure(h http.Handler, v VerifyClaimsFunc) http.Handler {
// the requester.
func (m *Middleware) GenerateToken() http.Handler {
generateHandler := func(w http.ResponseWriter, r *http.Request) *jwtError {
if r.Method != "POST" {
return &jwtError{
status: http.StatusBadRequest,
err: ErrInvalidMethod,
message: "receiving request",
}
}
var b map[string]string
err := json.NewDecoder(r.Body).Decode(&b)
if err != nil {
@ -172,7 +192,22 @@ func (m *Middleware) GenerateToken() http.Handler {
message: "parsing authorization",
}
}
err = m.auth(b["email"], b["password"])
// Check if required fields are in the body
if _, ok := b[m.identityField]; !ok {
return &jwtError{
status: http.StatusBadRequest,
err: ErrParsingCredentials,
message: "parsing credentials, missing identity field",
}
}
if _, ok := b[m.verifyField]; !ok {
return &jwtError{
status: http.StatusBadRequest,
err: ErrParsingCredentials,
message: "parsing credentials, missing verify field",
}
}
err = m.auth(b[m.identityField], b[m.verifyField])
if err != nil {
return &jwtError{
status: http.StatusInternalServerError,
@ -192,7 +227,7 @@ func (m *Middleware) GenerateToken() http.Handler {
}
// Generate claims for user
claims, err := m.claims(b["email"])
claims, err := m.claims(b[m.identityField])
if err != nil {
return &jwtError{
status: http.StatusInternalServerError,

View file

@ -32,7 +32,7 @@ var claimsFunc = func(id string) (map[string]interface{}, error) {
}, nil
}
var verifyClaimsFunc = func(claims []byte) error {
var verifyClaimsFunc = func(claims []byte, r *http.Request) error {
currentTime := time.Now()
var c struct {
Exp int64
@ -100,6 +100,12 @@ func TestNewJWTMiddleware(t *testing.T) {
if _, ok := claimsVal["iat"]; !ok {
t.Errorf("wanted a claims set, got %v", claimsVal)
}
if middleware.identityField != "email" {
t.Errorf("wanted email, got %v", middleware.identityField)
}
if middleware.verifyField != "password" {
t.Errorf("wanted password, got %v", middleware.verifyField)
}
}
func TestNewJWTMiddlewareNoConfig(t *testing.T) {
@ -215,3 +221,14 @@ func TestSecureHandlerGoodToken(t *testing.T) {
t.Errorf("wanted %s, got %s", "test", body)
}
}
func TestGenerateTokenHandlerNotPOST(t *testing.T) {
middleware := newMiddlewareOrFatal(t)
resp := httptest.NewRecorder()
req, _ := http.NewRequest("PUT", "http://example.com", nil)
middleware.GenerateToken().ServeHTTP(resp, req)
body := strings.TrimSpace(resp.Body.String())
if body != ErrInvalidMethod.Error() {
t.Errorf("wanted %q, got %q", ErrInvalidMethod.Error(), body)
}
}