Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
fukun committed May 10, 2017
0 parents commit 7505560
Show file tree
Hide file tree
Showing 21 changed files with 1,069 additions and 0 deletions.
26 changes: 26 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Contributing
Enhancements or fixes are welcome

## Issues
Check if a ticket for your issue already exists in GitHub issues. If you don't
find a ticket submit a new one.

## Pull Requests
1. Fork the repo
1. Check out a new topic branch from `dev`.
1. Make your changes.
1. Commit and push the topic branch.
1. Extra credit if you squash your commits first.
1. Submit a pull request back to `dev`.

###Style
- Your code should pass golint.
- Follow the existing conventions.

###Tests
- If you add any functionality be sure to also add a test for it.
- All regressions need to pass before I can accept your pull

## License
By contributing to this project you agree that your contributions will be
licensed under its MIT license.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
The MIT License (MIT)

Copyright (c) 2016 Clifton Kaznocha

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.
90 changes: 90 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# bearerware

[![Build Status](http://img.shields.io/travis/ckaznocha/go-JWTBearerware.svg?style=flat)](https://travis-ci.org/ckaznocha/go-JWTBearerware)
[![Coverage Status](https://coveralls.io/repos/github/ckaznocha/go-JWTBearerware/badge.svg?branch=master)](https://coveralls.io/github/ckaznocha/go-JWTBearerware?branch=master)
[![License](http://img.shields.io/:license-mit-blue.svg)](http://ckaznocha.mit-license.org)
[![GoDoc](https://godoc.org/github.com/ckaznocha/go-JWTBearerware?status.svg)](https://godoc.org/github.com/ckaznocha/go-JWTBearerware)
[![Go Report Card](https://goreportcard.com/badge/ckaznocha/go-JWTBearerware)](https://goreportcard.com/report/ckaznocha/go-JWTBearerware)

Package bearerware provides a library and middleware to make using [JSON Web Tokens](https://jwt.io/) in gRPC and HTTP requests more convenient. Middleware functions and examples for popular routers are in the `midleware` directory.

This project was inspire by [auth0/go-jwt-middleware](https://github.com/auth0/go-jwt-middleware).

Requires go1.7 or newer.

For more info see the example files and the [GoDoc](https://godoc.org/github.com/ckaznocha/go-JWTBearerware) page.

--
import "github.com/ckaznocha/go-JWTBearerware"


## Usage

#### func JWTFromContext

```go
func JWTFromContext(
ctx context.Context,
keyFunc jwt.Keyfunc,
signingMethod jwt.SigningMethod,
) (*jwt.Token, error)
```
JWTFromContext **deprecated** use `JWTFromIncomingContext`

#### func JWTFromHeader

```go
func JWTFromHeader(
r *http.Request,
keyFunc jwt.Keyfunc,
signingMethod jwt.SigningMethod,
) (*jwt.Token, error)
```
JWTFromHeader extracts a valid JWT from an http.Request or returns and error

#### func JWTFromIncomingContext

```go
func JWTFromIncomingContext(
ctx context.Context,
keyFunc jwt.Keyfunc,
signingMethod jwt.SigningMethod,
) (*jwt.Token, error)
```
JWTFromIncomingContext extracts a valid JWT from a context.Contexts or returns
and error

#### func NewJWTAccessFromJWT

```go
func NewJWTAccessFromJWT(jsonKey string) (credentials.PerRPCCredentials, error)
```
NewJWTAccessFromJWT creates a JWT credentials.PerRPCCredentials for use in gRPC requests.

#### func WriteAuthError

```go
func WriteAuthError(w http.ResponseWriter, err error)
```
WriteAuthError is a convenience function for setting the WWW-Authenticate header
and sending an http.Error()

#### type JWTContexter

```go
type JWTContexter interface {
WriteJWT(*http.Request, *jwt.Token)
ReadJWT(*http.Request) (*jwt.Token, bool)
DeleteJWT(*http.Request)
}
```

JWTContexter provides and interface for safe access to a shared map to get a jwt
for the current request scope when using net/http.

#### func NewJWTContext

```go
func NewJWTContext() JWTContexter
```
NewJWTContext creates a new JWTContexter
91 changes: 91 additions & 0 deletions bearerware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package bearerware

import (
"errors"
"fmt"
"strings"

jwt "github.com/dgrijalva/jwt-go"
)

const (
authHeader = "authorization"
bearer = "bearer "
bearerLen = len(bearer)

invalidRequest = "invalid_request"
invalidToken = "invalid_token"
//insufficientScope = "insufficient_scope"

errSigningMethod = `Expected signing method %s but token is signed using %s`
)

var (
errRestricted = errors.New("Bearer realm=Restricted")
errBearerFormat = errors.New(
"Authorization header format must be Bearer {token}",
)
errTokenInvalid = errors.New("Token is invalid")
)

type jwtError struct {
err error
code string
}

func (j *jwtError) Error() string {
s := errRestricted.Error()
if len(j.code) > 0 {
s += fmt.Sprintf(`,error="%s",error_description="%s"`, j.code, j.err)
}
return s
}

func isBearerToken(s string) bool {
return len(s) > bearerLen && strings.ToLower(s)[:bearerLen] == bearer
}

func tokenFromBearer(s string) (string, bool) {
if isBearerToken(s) {
return s[bearerLen:], true
}
return "", false
}

//CheckJWT checks if the JWT was issued by this app
func validJWTFromString(
token string,
keyFunc jwt.Keyfunc,
signingMethod jwt.SigningMethod,
) (*jwt.Token, error) {
var (
err error
parsedToken *jwt.Token
)
if len(token) == 0 {
return nil, &jwtError{
err: errBearerFormat,
code: invalidRequest,
}
}
parsedToken, err = jwt.Parse(token, keyFunc)
if err != nil {
return nil, &jwtError{
err: fmt.Errorf("Error parsing token: %v", err),
code: invalidToken,
}
}
if alg := parsedToken.Header["alg"]; alg != signingMethod.Alg() {
return nil, &jwtError{
err: fmt.Errorf(errSigningMethod, signingMethod.Alg(), alg),
code: invalidToken,
}
}
if !parsedToken.Valid {
return nil, &jwtError{
err: errTokenInvalid,
code: invalidToken,
}
}
return parsedToken, nil
}
90 changes: 90 additions & 0 deletions bearerware_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package bearerware

import (
"errors"
"fmt"
"testing"

"github.com/dgrijalva/jwt-go"
)

func Test_validJWTFromString(t *testing.T) {
var (
jwtKeyFunc = func(token *jwt.Token) (interface{}, error) {
return []byte(" "), nil
}
tests = []struct {
jwt string
signingMethod jwt.SigningMethod
isValidJWT bool
err error
}{
{
jwt: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIiLCJpYXQiOm51bGwsImV4cCI6bnVsbCwiYXVkIjoiIiwic3ViIjoiIn0.IlffGJz3IyFX1ADQ6-jOTQ_0D-K0kuKq5SpB_oirCrk",
signingMethod: jwt.SigningMethodHS256,
isValidJWT: true,
err: nil,
},
{
jwt: "",
signingMethod: jwt.SigningMethodHS256,
isValidJWT: false,
err: errors.New(`Bearer realm=Restricted,error="invalid_request",error_description="Authorization header format must be Bearer {token}"`),
},
{
jwt: "foo",
signingMethod: jwt.SigningMethodHS256,
isValidJWT: false,
err: errors.New(`Bearer realm=Restricted,error="invalid_token",error_description="Error parsing token: token contains an invalid number of segments"`),
},
{
jwt: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIiLCJpYXQiOm51bGwsImV4cCI6bnVsbCwiYXVkIjoiIiwic3ViIjoiIn0.IlffGJz3IyFX1ADQ6-jOTQ_0D-K0kuKq5SpB_oirCrk",
signingMethod: jwt.SigningMethodES256,
isValidJWT: false,
err: errors.New(`Bearer realm=Restricted,error="invalid_token",error_description="Expected signing method ES256 but token is signed using HS256"`),
},
{
jwt: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIiLCJpYXQiOm51bGwsImV4cCI6bnVsbCwiYXVkIjoiIiwic3ViIjoiIn0.EK492gkeFLTSWLrQlu2hTNbFw3scKJqU5CA3sIsLf68",
signingMethod: jwt.SigningMethodES256,
isValidJWT: false,
err: errors.New(`Bearer realm=Restricted,error="invalid_token",error_description="Error parsing token: signature is invalid"`),
},
}
)
for _, test := range tests {
_, err := validJWTFromString(test.jwt, jwtKeyFunc, test.signingMethod)
switch {
case test.isValidJWT && err != nil:
t.Error(err)
case !test.isValidJWT && err.Error() != test.err.Error():
t.Errorf("Expected Err: %s, Got: %s", test.err, err)

}
}
}

func Test_isBearerToken(t *testing.T) {
var (
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIiLCJpYXQiOm51bGwsImV4cCI6bnVsbCwiYXVkIjoiIiwic3ViIjoiIn0.IlffGJz3IyFX1ADQ6-jOTQ_0D-K0kuKq5SpB_oirCrk"
tests = []struct {
s string
valid bool
}{
{s: fmt.Sprintf("Bearer %s", token), valid: true},
{s: fmt.Sprintf("bearer %s", token), valid: true},
{s: fmt.Sprintf("beaRer %s", token), valid: true},
{s: fmt.Sprintf("Bearer%s", token), valid: false},
{s: fmt.Sprintf("%s", token), valid: false},
{s: fmt.Sprintf("MAC %s", token), valid: false},
}
)
for _, test := range tests {
got, valid := tokenFromBearer(test.s)
if valid != test.valid {
t.Errorf("Expected %t, got %t", test.valid, valid)
}
if test.valid && got != token {
t.Errorf("Expected %s, got %s", token, got)
}
}
}
9 changes: 9 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
Package bearerware provides a library and middleware to make using JSON Web
Tokens in gRPC and HTTP requests more convenient. Middleware functions and
examples for popular routers are in the `midleware` directory.
This project was inspire by github.com/auth0/go-jwt-middleware.
*/
package bearerware
Loading

0 comments on commit 7505560

Please sign in to comment.