Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/jwtproxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
_ "github.com/coreos/jwtproxy/jwt/keyserver/keyregistry/keycache/memory"
_ "github.com/coreos/jwtproxy/jwt/keyserver/preshared"
_ "github.com/coreos/jwtproxy/jwt/noncestorage/local"
_ "github.com/coreos/jwtproxy/jwt/noncestorage/none"
_ "github.com/coreos/jwtproxy/jwt/privatekey/autogenerated"
_ "github.com/coreos/jwtproxy/jwt/privatekey/preshared"
)
Expand Down
10 changes: 5 additions & 5 deletions jwt/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ func Verify(req *http.Request, keyServer keyserver.Reader, nonceVerifier noncest
// Extract token from request.
token, err := oidc.ExtractBearerToken(req)
if err != nil {
username, password, ok := req.BasicAuth()
if !ok && username == "oauth2" {
return nil, errors.New("No JWT found")
}
username, password, ok := req.BasicAuth()
if !ok && username == "oauth2" {
return nil, errors.New("No JWT found")
}
token = password
}

Expand Down Expand Up @@ -120,7 +120,7 @@ func Verify(req *http.Request, keyServer keyserver.Reader, nonceVerifier noncest
return nil, errors.New("Invalid 'exp' claim (too long)")
}
jti, exists, err := claims.StringClaim("jti")
if !exists || err != nil || !nonceVerifier.Verify(jti, exp) {
if err != nil || !nonceVerifier.Verify(jti, exp) {
return nil, errors.New("Missing or invalid 'jti' claim")
}

Expand Down
70 changes: 65 additions & 5 deletions jwt/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ import (
"github.com/coreos/go-oidc/key"
"github.com/coreos/go-oidc/oidc"
"github.com/coreos/jwtproxy/config"
"github.com/coreos/jwtproxy/jwt/noncestorage"
_ "github.com/coreos/jwtproxy/jwt/noncestorage/local"
_ "github.com/coreos/jwtproxy/jwt/noncestorage/none"
"github.com/coreos/jwtproxy/stop"
"github.com/stretchr/testify/assert"
)
Expand Down Expand Up @@ -87,10 +90,7 @@ func (ts *testService) Stop() <-chan struct{} {
return stop.AlreadyDone
}

func TestJWT(t *testing.T) {
// Create a request to sign.
req, _ := http.NewRequest("GET", "http://foo.bar:6666/ez", nil)

func setup(t *testing.T) *signAndVerifyParams {
// Create a public/private key pair used to sign/verify.
pkb, _ := pem.Decode([]byte(privateKey))
pkr, _ := x509.ParsePKCS1PrivateKey(pkb.Bytes)
Expand Down Expand Up @@ -123,6 +123,15 @@ func TestJWT(t *testing.T) {
maxTTL: 5 * time.Minute,
}

return defaultConfig
}

func TestJWT(t *testing.T) {
// Create a request to sign.
req, _ := http.NewRequest("GET", "http://foo.bar:6666/ez", nil)

defaultConfig := setup(t)

// Basic sign / verify.
assert.Nil(t, signAndVerify(t, req, *defaultConfig, nil))

Expand Down Expand Up @@ -201,6 +210,53 @@ func TestJWT(t *testing.T) {
assert.Error(t, signAndVerify(t, req, cfg, nil))
}

func TestValidateJWT(t *testing.T) {
cfg := setup(t)

req, _ := http.NewRequest("GET", "http://foo.bar:6666/ez", nil)

assert.Nil(t, signAndVerify(t, req, *cfg, nil))
}

func TestLocalValidateJTITwice(t *testing.T) {
cfg := setup(t)

local, err := noncestorage.New(config.RegistrableComponentConfig{
Type: "local",
Options: map[string]interface{}{
"PurgeInterval": 1 * time.Minute,
},
})
assert.NoError(t, err)

req, _ := http.NewRequest("GET", "http://foo.bar:6666/ez", nil)
sign(t, req, *cfg, nil)

_, err = Verify(req, cfg.services, local, cfg.aud, cfg.maxSkew, cfg.maxTTL)
assert.Nil(t, err)

//Validating same JTI twice should have error
_, err = Verify(req, cfg.services, local, cfg.aud, cfg.maxSkew, cfg.maxTTL)
assert.Error(t, err)
}

func TestNoValidateJTITwice(t *testing.T) {
cfg := setup(t)

local, err := noncestorage.New(config.RegistrableComponentConfig{
Type: "none",
})
assert.NoError(t, err)

req, _ := http.NewRequest("GET", "http://foo.bar:6666/ez", nil)
sign(t, req, *cfg, nil)

_, err = Verify(req, cfg.services, local, cfg.aud, cfg.maxSkew, cfg.maxTTL)
assert.Nil(t, err)
_, err = Verify(req, cfg.services, local, cfg.aud, cfg.maxSkew, cfg.maxTTL)
assert.Nil(t, err)
}

type signAndVerifyParams struct {
services *testService

Expand All @@ -215,7 +271,7 @@ type signAndVerifyParams struct {

type requestModifier func(req *http.Request)

func signAndVerify(t *testing.T, req *http.Request, p signAndVerifyParams, modify requestModifier) error {
func sign(t *testing.T, req *http.Request, p signAndVerifyParams, modify requestModifier) {
// Sign.
pk, _ := p.services.GetPrivateKey()
assert.Nil(t, Sign(req, pk, p.signerParams))
Expand All @@ -224,6 +280,10 @@ func signAndVerify(t *testing.T, req *http.Request, p signAndVerifyParams, modif
if modify != nil {
modify(req)
}
}

func signAndVerify(t *testing.T, req *http.Request, p signAndVerifyParams, modify requestModifier) error {
sign(t, req, p, modify)

// Verify.
_, err := Verify(req, p.services, p.services, p.aud, p.maxSkew, p.maxTTL)
Expand Down
4 changes: 4 additions & 0 deletions jwt/noncestorage/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func constructor(registrableComponentConfig config.RegistrableComponentConfig) (
}

func (ln *Local) Verify(nonce string, expiration time.Time) bool {
if nonce == "" {
return false
}

if _, found := ln.Get(nonce); found {
return false
}
Expand Down
45 changes: 45 additions & 0 deletions jwt/noncestorage/none/none.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright 2016 CoreOS, Inc
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package none

import (
"time"

"github.com/coreos/jwtproxy/config"
"github.com/coreos/jwtproxy/jwt/noncestorage"
"github.com/coreos/jwtproxy/stop"
)

func init() {
noncestorage.Register("none", constructor)
}

// None no jti validation
type None struct {
}

func constructor(registrableComponentConfig config.RegistrableComponentConfig) (noncestorage.NonceStorage, error) {
return &None{}, nil
}

// Verify Always returns true
func (ln *None) Verify(nonce string, expiration time.Time) bool {
return true
}

// Stop already done
func (ln *None) Stop() <-chan struct{} {
return stop.AlreadyDone
}