Coder Social home page Coder Social logo

jwtauth's People

Contributors

c2h5oh avatar dependabot[bot] avatar drakkan avatar e-nikolov avatar ernsheong avatar husseinmkwizu avatar jonhester avatar kvii avatar l2dy avatar lukasmalkmus avatar pkieltyka avatar slax0rr avatar sogko avatar vojtechvitek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jwtauth's Issues

How to use JWT expiration?

Hey, I just started using this awesome libary and I have a "basic" question.

I want to have jwt tokens with an expiration date. My first thought was to handle this through a second claim. Then I had a look at jwtauth.go and saw, that there is a possible error regarding to the expiration date. But I did not find an option to set the expiration date for a jwt token generated with TokenAuth.Encode() function.

I would be glad to get a reply.

Thanks,
Thecrafterja

Multiple keys / kid

The kid claim in the header can be used to specify which key was used to secure the token.
Please add support for this.

Make token search order configureable

I think it would be nice to make the token search order configureable. Currently the middleware searches for a query parameter first. In my case, I would like to search for the token in the Authorization header only! Sure, I could create yet another fork, but maybe others want the same functionality. If this is desired, we could discuss this and I would look into submitting a PR.

jwt.Validate in jwtauth.Authenticator package error

When I try to use jwt.Validate in my application, it's always be error.

http: panic serving [::1]:60976: runtime error: invalid memory address or nil pointer dereference
goroutine 45 [running]:

I tried to use an other custom middleware but still got an error from this function, please help!

 "GET http://localhost:****/track HTTP/1.1" from [::1]:64115 - 000 0B in 297.334µs
2023/06/28 23:54:48 http: panic serving [::1]:64115: runtime error: invalid memory address or nil pointer dereference
goroutine 36 [running]:
net/http.(*conn).serve.func1()
        /usr/local/go/src/net/http/server.go:1850 +0xbf
panic({0x1372980, 0x1675fd0})
        /usr/local/go/src/runtime/panic.go:890 +0x262
github.com/go-chi/jwtauth/v5.(*JWTAuth).parse(0x0, {0xc00024a0b0, 0xac, 0xb0})
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:140 +0xb6
github.com/go-chi/jwtauth/v5.(*JWTAuth).Decode(...)
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:131
github.com/go-chi/jwtauth/v5.VerifyToken(0xc000099838?, {0xc0002340c5?, 0x10?})
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:100 +0x3c
github.com/go-chi/jwtauth/v5.VerifyRequest(0x0?, 0x0?, {0xc0001192d0, 0x2, 0x0?})
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:95 +0xac
github.com/go-chi/jwtauth/v5.Verify.func1.1({0x29d75128, 0xc00021e300}, 0xc000224500)
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:71 +0x89
net/http.HandlerFunc.ServeHTTP(0xc0001d2000?, {0x29d75128?, 0xc00021e300?}, 0xc00023c240?)
        /usr/local/go/src/net/http/server.go:2109 +0x2f
github.com/go-chi/chi/v5.(*ChainHandler).ServeHTTP(0x13707e0?, {0x29d75128?, 0xc00021e300?}, 0xc000226034?)
        ~/go/pkg/mod/github.com/go-chi/chi/[email protected]/chain.go:31 +0x2c
github.com/go-chi/chi/v5.(*Mux).routeHTTP(0xc00012a300, {0x29d75128, 0xc00021e300}, 0xc000224500)
        ~/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:444 +0x216
net/http.HandlerFunc.ServeHTTP(0xc000224400?, {0x29d75128?, 0xc00021e300?}, 0xd0?)
        /usr/local/go/src/net/http/server.go:2109 +0x2f
github.com/go-chi/chi/v5/middleware.RequestLogger.func1.1({0x14814d0, 0xc00022a2a0}, 0xc000224400)
        ~/go/pkg/mod/github.com/go-chi/chi/[email protected]/middleware/logger.go:54 +0x17d
net/http.HandlerFunc.ServeHTTP(0x1481998?, {0x14814d0?, 0xc00022a2a0?}, 0x1675e00?)
        /usr/local/go/src/net/http/server.go:2109 +0x2f
github.com/go-chi/chi/v5.(*Mux).ServeHTTP(0xc00012a300, {0x14814d0, 0xc00022a2a0}, 0xc000224300)
        ~/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:90 +0x310
net/http.serverHandler.ServeHTTP({0xc0002142a0?}, {0x14814d0, 0xc00022a2a0}, 0xc000224300)
        /usr/local/go/src/net/http/server.go:2947 +0x30c
net/http.(*conn).serve(0xc0002180a0, {0x1481a40, 0xc0001d2720})
        /usr/local/go/src/net/http/server.go:1991 +0x607
created by net/http.(*Server).Serve
        /usr/local/go/src/net/http/server.go:3102 +0x4db
2023/06/28 23:54:48 "GET http://localhost:****/track HTTP/1.1" from [::1]:64120 - 000 0B in 152.666µs
2023/06/28 23:54:48 http: panic serving [::1]:64120: runtime error: invalid memory address or nil pointer dereference
goroutine 25 [running]:
net/http.(*conn).serve.func1()
        /usr/local/go/src/net/http/server.go:1850 +0xbf
panic({0x1372980, 0x1675fd0})
        /usr/local/go/src/runtime/panic.go:890 +0x262
github.com/go-chi/jwtauth/v5.(*JWTAuth).parse(0x0, {0xc000154210, 0xac, 0xb0})
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:140 +0xb6
github.com/go-chi/jwtauth/v5.(*JWTAuth).Decode(...)
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:131
github.com/go-chi/jwtauth/v5.VerifyToken(0xc000068838?, {0xc0001dc0c5?, 0x10?})
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:100 +0x3c
github.com/go-chi/jwtauth/v5.VerifyRequest(0x0?, 0x0?, {0xc0001192d0, 0x2, 0x0?})
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:95 +0xac
github.com/go-chi/jwtauth/v5.Verify.func1.1({0x29d75128, 0xc0001d64c0}, 0xc00019e700)
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:71 +0x89
net/http.HandlerFunc.ServeHTTP(0xc0001d2000?, {0x29d75128?, 0xc0001d64c0?}, 0xc0001254f0?)
        /usr/local/go/src/net/http/server.go:2109 +0x2f
github.com/go-chi/chi/v5.(*ChainHandler).ServeHTTP(0x13707e0?, {0x29d75128?, 0xc0001d64c0?}, 0xc000132274?)
        ~/go/pkg/mod/github.com/go-chi/chi/[email protected]/chain.go:31 +0x2c
github.com/go-chi/chi/v5.(*Mux).routeHTTP(0xc00012a300, {0x29d75128, 0xc0001d64c0}, 0xc00019e700)
        ~/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:444 +0x216
net/http.HandlerFunc.ServeHTTP(0xc00019e600?, {0x29d75128?, 0xc0001d64c0?}, 0xd0?)
        /usr/local/go/src/net/http/server.go:2109 +0x2f
github.com/go-chi/chi/v5/middleware.RequestLogger.func1.1({0x14814d0, 0xc000192380}, 0xc00019e600)
        ~/go/pkg/mod/github.com/go-chi/chi/[email protected]/middleware/logger.go:54 +0x17d
net/http.HandlerFunc.ServeHTTP(0x1481998?, {0x14814d0?, 0xc000192380?}, 0x1675e00?)
        /usr/local/go/src/net/http/server.go:2109 +0x2f
github.com/go-chi/chi/v5.(*Mux).ServeHTTP(0xc00012a300, {0x14814d0, 0xc000192380}, 0xc00019e500)
        ~/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:90 +0x310
net/http.serverHandler.ServeHTTP({0xc0001d2a80?}, {0x14814d0, 0xc000192380}, 0xc00019e500)
        /usr/local/go/src/net/http/server.go:2947 +0x30c
net/http.(*conn).serve(0xc0001b1220, {0x1481a40, 0xc0001d2720})
        /usr/local/go/src/net/http/server.go:1991 +0x607
created by net/http.(*Server).Serve
        /usr/local/go/src/net/http/server.go:3102 +0x4db
2023/06/28 23:54:48 "GET http://localhost:****/track HTTP/1.1" from [::1]:64121 - 000 0B in 1.013041ms
2023/06/28 23:54:48 http: panic serving [::1]:64121: runtime error: invalid memory address or nil pointer dereference
goroutine 6 [running]:
net/http.(*conn).serve.func1()
        /usr/local/go/src/net/http/server.go:1850 +0xbf
panic({0x1372980, 0x1675fd0})
        /usr/local/go/src/runtime/panic.go:890 +0x262
github.com/go-chi/jwtauth/v5.(*JWTAuth).parse(0x0, {0xc000298160, 0xac, 0xb0})
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:140 +0xb6
github.com/go-chi/jwtauth/v5.(*JWTAuth).Decode(...)
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:131
github.com/go-chi/jwtauth/v5.VerifyToken(0xc00006e838?, {0xc00028e0c5?, 0x10?})
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:100 +0x3c
github.com/go-chi/jwtauth/v5.VerifyRequest(0x0?, 0x0?, {0xc0001192d0, 0x2, 0x0?})
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:95 +0xac
github.com/go-chi/jwtauth/v5.Verify.func1.1({0x29d75128, 0xc00007c3c0}, 0xc000284500)
        ~/go/pkg/mod/github.com/go-chi/jwtauth/[email protected]/jwtauth.go:71 +0x89
net/http.HandlerFunc.ServeHTTP(0xc0001d2000?, {0x29d75128?, 0xc00007c3c0?}, 0xc000294240?)
        /usr/local/go/src/net/http/server.go:2109 +0x2f
github.com/go-chi/chi/v5.(*ChainHandler).ServeHTTP(0x13707e0?, {0x29d75128?, 0xc00007c3c0?}, 0xc00002a10c?)
        ~/go/pkg/mod/github.com/go-chi/chi/[email protected]/chain.go:31 +0x2c
github.com/go-chi/chi/v5.(*Mux).routeHTTP(0xc00012a300, {0x29d75128, 0xc00007c3c0}, 0xc000284500)
        ~/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:444 +0x216
net/http.HandlerFunc.ServeHTTP(0xc000284400?, {0x29d75128?, 0xc00007c3c0?}, 0xd0?)
        /usr/local/go/src/net/http/server.go:2109 +0x2f
github.com/go-chi/chi/v5/middleware.RequestLogger.func1.1({0x14814d0, 0xc0002882a0}, 0xc000284400)
        ~/go/pkg/mod/github.com/go-chi/chi/[email protected]/middleware/logger.go:54 +0x17d
net/http.HandlerFunc.ServeHTTP(0x1481998?, {0x14814d0?, 0xc0002882a0?}, 0x1675e00?)
        /usr/local/go/src/net/http/server.go:2109 +0x2f
github.com/go-chi/chi/v5.(*Mux).ServeHTTP(0xc00012a300, {0x14814d0, 0xc0002882a0}, 0xc000284300)
        ~/go/pkg/mod/github.com/go-chi/chi/[email protected]/mux.go:90 +0x310
net/http.serverHandler.ServeHTTP({0xc00007e240?}, {0x14814d0, 0xc0002882a0}, 0xc000284300)
        /usr/local/go/src/net/http/server.go:2947 +0x30c
net/http.(*conn).serve(0xc0000000a0, {0x1481a40, 0xc0001d2720})
        /usr/local/go/src/net/http/server.go:1991 +0x607
created by net/http.(*Server).Serve
        /usr/local/go/src/net/http/server.go:3102 +0x4db

Having a hard time setting up a custom authenticator function

I'm doing the oauth dance with an API (Shopify) that is making it difficult on me to write an authenticator function. Currently it looks like this:

func Authenticator(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		token, _, err := jwtauth.FromContext(r.Context())

		if err != nil {
			ctx := context.WithValue(r.Context(), "shop", r.URL.Query().Get("shop"))
			fmt.Println(r.URL.Query().Get("shop"), "Debug: 1")
			http.Redirect(w, r.WithContext(ctx), "http://localhost:2020/login", 301)
		}

		if token == nil || !token.Valid {
			ctx := context.WithValue(r.Context(), "shop", r.URL.Query().Get("shop"))
			fmt.Println(r.URL.Query().Get("shop"), "Debug: 2")
			http.Redirect(w, r.WithContext(ctx), "http://localhost:2020/login", 301)
		}

		// Token is authenticated, pass it through
		next.ServeHTTP(w, r)
	})
}

If I'm not mistaken, the middleware runs before the actual request right? Which means that my r.URL.Query().Get("shop") will never be available to the middleware before redirecting to http://localhost:2020/login.

But it's necessary because shopify doesn't auth with a generic https://shopify.com/oauth/authorize style endpoint, but rather with a account specific endpoint that looks like this: https://{shop}.myshopify.com/admin/oauth/authorize. The shop query param I'm trying to pass in would replace that {shop} in the URL and is provided to the code when you go to http://localhost:2020/?shop=shopNameHere.myshopify.com. The authenticator function should redirect to /login and pass the 'shop' param through but it doesn't.

So what can I do here?

Missing error handling when set claim fields

func (ja *JWTAuth) Encode(claims map[string]interface{}) (t jwt.Token, tokenString string, err error) {
	t = jwt.New()
	for k, v := range claims {
		t.Set(k, v)
	}
	payload, err := ja.sign(t)
	if err != nil {
		return nil, "", err
	}
	tokenString = string(payload)
	return
}

If t.Set(k,v) returns error, we should handle it and error out earlier, instead of now just ignore it ?
Look at the API t.Set(k, v), depending on the key's name, it may fail if we provide a wrong type for that key, e.g. the sub has to be string.

`null` values in token lead to panic in jwtauth.Authenticator

This is an issue with github.com/lestrrat-go but I'm reporting it here because I suspect others upgrading v1.1.0 and above (and hence moving from github.com/dgrijalva/jwt-go to github.com/lestrrat-go/jwx) may encounter it (and it took me a while to trace the cause). If a token contains a null (e.g. "nullValue": null) then calling jwtauth.Authenticator will result in a panic.

panic(0xcd2ae0, 0xc00000d8f0)
	runtime/panic.go:965 +0x1b9
reflect.Value.Type(0x0, 0x0, 0x0, 0x1256801, 0xca1940)
	reflect/value.go:1908 +0x189
github.com/lestrrat-go/iter/mapiter.AsMap(0x124a168, 0xc000096000, 0xdbd8a0, 0xc000d508a0, 0xc84f80, 0xc00000e948, 0xdbd8a0, 0x1253048)
	github.com/lestrrat-go/[email protected]/mapiter/mapiter.go:180 +0x5c5
github.com/lestrrat-go/jwx/internal/iter.AsMap(0x124a168, 0xc000096000, 0x123a520, 0xc000d508a0, 0xdbd8a0, 0xdbd801, 0x1253048)
	github.com/lestrrat-go/[email protected]/internal/iter/mapiter.go:31 +0x86
github.com/lestrrat-go/jwx/jwt.(*stdToken).AsMap(0xc000d508a0, 0x124a168, 0xc000096000, 0x1253048, 0xc000d508a0, 0x1)
	github.com/lestrrat-go/[email protected]/jwt/token_gen.go:491 +0x4b
github.com/go-chi/jwtauth/v5.FromContext(0x124a1d8, 0xc0011a1620, 0x7fa161cc75b8, 0x90, 0xc00053aa20, 0xb994de, 0xc000248c00)
	github.com/go-chi/jwtauth/[email protected]/jwtauth.go:195 +0xb5
github.com/go-chi/jwtauth/v5.Authenticator.func1(0x1246738, 0xc0005fe460, 0xc000984700)
	github.com/go-chi/jwtauth/[email protected]/jwtauth.go:165 +0x5d

This issue in the upstream project is: lestrrat-go/iter#1 (I have posted my work around there).

The following demonstrates how this can be replicated within github.com/go-chi/jwtauth:

package main

import (
	"context"
	"crypto/rand"
	"crypto/rsa"
	"fmt"
	"net/http"

	"github.com/go-chi/jwtauth"
	"github.com/lestrrat-go/jwx/jwa"
	"github.com/lestrrat-go/jwx/jwk"
	"github.com/lestrrat-go/jwx/jwt"
)

func main() {
	// Generate a new private key so we can sign a test token
	privKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		fmt.Printf("failed to generate private key: %s\n", err)
		return
	}
	realKey, err := jwk.New(privKey)
	if err != nil {
		fmt.Printf("failed to create JWK: %s\n", err)
		return
	}
	realKey.Set(jwk.KeyIDKey, `mykey`)

	// Generate the test token with a nil value
	token := jwt.New()
	token.Set("nullValue", nil)

	// Sign the token and generate a payload
	signed, err := jwt.Sign(token, jwa.RS256, realKey)
	if err != nil {
		panic(fmt.Sprintf("failed to generate signed payload: %s\n", err))
	}
	fmt.Printf("token generated: %s\n", string(signed))

       // Now we use jwtauth to process the token (simulating extraction from a cookie)
	var r *http.Request // Will not actually be used..
	tokenAuth := jwtauth.New("RS256", nil, privKey.Public())
	token, err = jwtauth.VerifyRequest(tokenAuth, r, func(r *http.Request) string { return string(signed) })
	if err != nil {
		panic(fmt.Sprintf("Failed to process cookie: %s", err))
	}

	// Add the token to the context
	ctx := jwtauth.NewContext(context.Background(), token, nil)

	// Attempt to retrieve from the context (Authenticator makes this call)
	jwtauth.FromContext(ctx) // panic here
}

RSA signing method

Is it possible to use jwtauth with the RSA signing method?

From my understanding of the jwt-go library, its SignedString method takes the key as interface{}

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
}

And each different signing method implementation is free to choose the type of the key. However in go-chi/jwtauth, the keys are only allowed to be []byte. This works fine for the HMAC implementation, which expects the key to be a []byte, but the RSA implementation expects the key to be of type *rsa.PrivateKey

func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) {
	var rsaKey *rsa.PrivateKey
	var ok bool

	// Validate type of key
	if rsaKey, ok = key.(*rsa.PrivateKey); !ok {
		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
	}
}

This results in a "key is invalid type" error when using the RSA signing method. Check this issue for more info dgrijalva/jwt-go#65.

Altering Validation Options to jwt.Validate()

The new underlying JWT library supports options for validation via jwt.Validate(t Token, options ...ValidateOption), unfortunately this isn't exposed in jwtauth.

jwtauth.Verifier(ja *JWTAuth) calls down the verification stack until jwtauth.VerifyToken(ja *JWTAuth, tokenString string) where in the body there is a call to jwt.Validate(t Token, options ...ValidateOption), unfortunately I can't see a way to alter these options from the jwtauth API though?

Meanwhile the jwtauth.Authenticator(next http.Handler) example also calls jwt.Validate(...) (i.e. a second call to this function in the request sequence) where obviously it's possible to add the validation options using a custom Authenticator.

Is the expectation that a second call to jwt.Validate(...) with or without options parameters will be needed in any custom Authenticator? It looks like if I could pass the options in I could avoid calling the function again.

README and Comments Corrections After JWT Lib Change

This is misleading, the current code won't check if the token has expired unless the user explicitly sets dates:

jwtauth/README.md

Lines 13 to 14 in 9448513

request, decode it, verify it and then validate that its correctly signed and hasn't
expired - the `jwtauth.Verifier` middleware handler takes care of all of that. The

The default no longer looks for the token in the query string:

// 1. 'jwt' URI query parameter

No longer using this library:

// or cookie header is then decoded by the `jwt-go` library and a *jwt.Token

How to determine token type on Verify?

I have regular and refresh token. At the moment user can pass verification with both tokens.
I want check token's type on Verify and allow refresh token only for Refresh procedure.
Is it real?

go get github.com/go-chi/jwtauth fails

I tried this on a couple of different machines (Linux and macOS) and both fail with this error:

go get github.com/go-chi/jwtauth
cannot find package "github.com/decred/dcrd/dcrec/secp256k1/v3" in any of:
	/usr/local/Cellar/go/1.15.8/libexec/src/github.com/decred/dcrd/dcrec/secp256k1/v3 (from $GOROOT)
	/Users/saeedzab/go/src/github.com/decred/dcrd/dcrec/secp256k1/v3 (from $GOPATH)

Is this a known issue?

interface conversion: interface {} is *jwt.Token, not *jwt.Token

I'm getting a very strange error using the sample code from jwtauth. The error is at this line:

token := ctx.Value("jwt").(*jwt.Token)

Solved this issue after remove the vendor folder inside jwtauth project.

Keeping the vendor in a library project isn't a good idea, its better to remove it, the front page of the project has a very big warning about the dependency, its better to handle the dependencies at the application level.

I seem to be having issues and errors with NBF

The JWT look like this:

{
  "iss": "...",
  "dest": "...",
  "aud": "...",
  "sub": "...",
  "exp": 1609818402, //-> 01/05/2021 @ 3:46:42am (UTC)
  "nbf": 1609818342, //-> 01/05/2021 @ 3:45:42am (UTC)
  "iat": 1609818342, //-> 01/05/2021 @ 3:45:42am (UTC)
  "jti": "...",
  "sid": "..."
}

I dev apps for a service (Shopify) that lets you request a JWT from their servers, and then you can use that JWT to make authenticated calls to your backend (since the token is signed with your API secret, which both you and Shopify have on hand). My backend uses this JWT middleware to handle that part.

The issue I'm having is that it seems if I request a JWT and immediately use it to make a call to my backend (we're talking sub 50 milliseconds between getting the JWT and making a request to my backend), the JWT middleware returns the following error: token nbf validation failed

If I then use a setTimeout() function on my frontend to add a 750-1000ms delay after requesting the JWT, the issue does not happen. So it seems to me like there's something going on with the underlying library/implementation that causes the backend to think it hasn't reached the correct NBF time yet?

Not sure how I would go about resolving this other than manually adding delays before making calls to my backend. Suggestions?

VerifyRequest returns incorrect error when error is due to ValidationErrorNotValidYet

In VerifyRequest the conditional statement checking the error returned by JWTAuth.Decode is performed against the wrong constant (assuming the comments in the code are correct)

// lines 113-115 of jwtauth.go
} else if verr.Errors&jwt.ValidationErrorIssuedAt > 0 {
  return token, ErrNBFInvalid
}

should be

} else if verr.Errors&jwt.ValidationErrorNotValidYet > 0 {
  return token, ErrNBFInvalid
}

If this is not a bug (I haven't read the spec to know the difference between these errors) then my apologies for opening. I can send a PR over to address if interested.

Thanks

Middleware never validates claims despite claiming to

The middleare specifically states: We explicitly toggle SkipClaimsValidation in the jwt-go parser so that we can control when the claims are validated - in our case, by the Verifier http middleware handler.
https://github.com/go-chi/jwtauth/blob/master/jwtauth.go#L42-L46

Then, in the Verifier middleware, the code doesn't ever actually validate the claims at all:
https://github.com/go-chi/jwtauth/blob/master/jwtauth.go#L146-L150
https://github.com/go-chi/jwtauth/blob/master/jwtauth.go#L109-L130

The best it does it check the exp field manually, using code that doesn't need to exist since jwt-go already has this functionality built in.

My suggestion is to stop skipping claims validation, and stop checking the exp claim manually, and instead let jwt-go verify the exp claim along with the nbf and iat claims.

If you agree, I can make a pull request.

Token expiry

Let's check the conventional "exp" claim field for the value to check if the current time past the present time. If so, we should return a 401.

Unable to retrieve token from context

Casting token pointer value retrieved from request context fails for no obvious reason:


func accCtx(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		ctx := r.Context()
		val := ctx.Value("jwt")
		log.Printf("%#v\n", val)
		t, ok := val.(*jwt.Token)
		if !ok {
			errBadRequest(w)
			return
		}
		newctx := context.WithValue(ctx, ctxAccountID, t.Claims["account_id"])
		next.ServeHTTP(w, r.WithContext(newctx))
	})
}

The Printf call above displays something that looks like valid jwt.Token reference:


&jwt.Token{Raw:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY2NvdW50X2lkIjoiYTIxZjE4YjQtMGI0Mi00ZDdhLWJkNDAtMmI0ZTZkNGZlYTdjIiwidXNlcl9pZCI6ImQxMWQwOTZkLTc0N2ItNGYyNS05NjcxLWJhNzJjNjM3NTY0YSJ9.ERX3v2mVL_AQu3_nYj7e4xkeXse6VNKR_FcZm8qvQ-A", Method:(*jwt.SigningMethodHMAC)(0xc4200ce760), Header:map[string]interface {}{"alg":"HS256", "typ":"JWT"}, Claims:map[string]interface {}{"account_id":"a21f18b4-0b42-4d7a-bd40-2b4e6d4fea7c", "user_id":"d11d096d-747b-4f25-9671-ba72c637564a"}, Signature:"ERX3v2mVL_AQu3_nYj7e4xkeXse6VNKR_FcZm8qvQ-A", Valid:true}

However, casting that value to *jwt.Token fails.

Getting 401 unauthorized when passing token on multiple services

Currently I have 2 services:

  • One handles auth logic and generates the jwt using postgres
  • The other handles app data logic and is protected using the same token secret (currently uses mongo)

Somehow I end up getting error 401 unauthorized when passing the tokens from one service to another, why is this happening?

I'm just wondering why it behaves like this. Although the easiest way is to implement an api gateway to solve this, I'd like to know if theres something wrong with my approach.

Usage with Auth0 with ID Token

Hello,

I want to know if it is possible to use this library to verify an ID Token originating from Auth0.
I only need the identity of the caller, the authorization is done inside the application.

The readme says I need to pass in a secret. However, I don't know what secret is meant. I have tried it with the "Client Secret". It will error with "token is unauthorized".

tokenAuth = jwtauth.New("HS256", []byte("my client secret"), nil)

Thank you very much for this package and for any help!

Getting same token for every request

module.go

func (oM *OracleModule) Init(ctx context.Context, serverConfig serverConfig.ServerConfig) {
	// token init
	oM.tokenAuth = jwtauth.New("HS256", []byte(oM.config.JwtSecretKey), nil)
}

user.go

tokenString := oM.GenerateJwtToken(respBody["email"].(string), respBody["name"].(string))

Here the tokenString is giving same token everytime. Is this expected?

OIDC Discovery functionality

Hi!

I've been working on an extension for echo's JWT middleware to enable discovery through OpenID Connect. Everything using the jwx library.

Would you be interested in a PR for the same thing here?

You can see the PR here: labstack/echo-contrib#59

If you are interested, I'd love some feedback on how you would prefer the API to be.

Thanks!

Regards,
Simon

int turns float64 after decode

Hello.

I'm trying to get data from claims, after the decode. But it seems that the original value (an int) turns a float64, after the decode.

Example:

package main

import (
	"context"
	"fmt"

	"github.com/go-chi/jwtauth/v5"
)

func main() {
	tokenAuth := jwtauth.New("HS256", []byte("secret"), nil)
	_, tokenString, _ := tokenAuth.Encode(map[string]interface{}{"user_id": 123})

	myToken, _ := tokenAuth.Decode(tokenString)
	claims, _ := myToken.AsMap(context.Background())
	userID := claims["user_id"]

	_, ok := userID.(int)
	if ok {
		fmt.Printf("userID %v is int\n", userID)
	}

	_, ok = userID.(float64)
	if ok {
		fmt.Printf("userID %v is float\n", userID)
	}
}

When I run this, I got "userID 123 is float".

I'm using go 1.16.6, on ubuntu 21.04.

I am doing something wrong?

Thanks

`jwt` key not being retrieved from context

It's entirely possible I'm doing something wrong, but I can't see it. The parsed jwt data is there when I dump out the context with %+v, but it's not coming back from ctx.Value. Building and running this example, then hitting localhost:8080 in the browser will dump the context to stderr with %+v, and write the specific error to the browser.

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
    "time"

    "github.com/dgrijalva/jwt-go"
    "github.com/goware/jwtauth"
    "github.com/pressly/chi"
)

var TokenAuth *jwtauth.JwtAuth

func init() {
    TokenAuth = jwtauth.New("HS256", []byte("fooo"), nil)
}

func main() {
    r := chi.NewRouter()
    r.Get("/", Authenticate)
    r.Group(func(r chi.Router) {
        r.Use(TokenAuth.Verifier)
        r.Use(jwtauth.Authenticator)
        r.Route("/foo", func(r chi.Router) {
            r.Get("/", FooIndex)
        })
    })

    log.Fatal(http.ListenAndServe(":8080", r))
}

func Authenticate(w http.ResponseWriter, r *http.Request) {
    cl := jwtauth.Claims{}.
        SetExpiryIn(time.Hour*24).
        SetIssuedNow().
        Set("uid", 13)
    _, str, err := TokenAuth.Encode(cl)

    if err != nil {
        http.Error(w, http.StatusText(500), 500)
    }
    c := &http.Cookie{Name: "jwt", Value: str}
    http.SetCookie(w, c)
    w.Header().Set("Location", "/foo")
    w.WriteHeader(http.StatusFound)
}

func FooIndex(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    fmt.Fprintf(os.Stderr, "%+v\n", ctx)
    token, ok := ctx.Value("jwt").(*jwt.Token)
    if !ok {
        fmt.Fprintln(os.Stderr, "no auth context available!")
        http.Error(w, "no auth context available", 404)
        return
    } else if token == nil {
        http.Error(w, "no token available", 404)
        return
    } else if !token.Valid {
        http.Error(w, "invalid token", 403)
        return
    }

    fmt.Fprintf(os.Stderr, "%+v\n", token)
    // this cast & check is no longer necessary after vendoring jwt-go 2.7.0
    claims, ok := token.Claims.(jwt.MapClaims)
    if !ok {
        http.Error(w, "can't cast claims", 422)
        return
    }

    // claims => token.Claims after vendoring jwt-go 2.7.0
    uid, ok := claims["uid"].(float64)
    if !ok {
        http.Error(w, "no user available", 404)
        return
    }

    fmt.Fprintf(w, "user found (%d)", uid)
}

Customizing Authenticator

When I use jwtauth.Authenticator, it works.
But when I copy the same Authenticator to my own package, an error occur at

		if !ok || jwtToken == nil || !jwtToken.Valid {
			http.Error(w, http.StatusText(401), 401)
			return
		}

Any clues?

Problem with the Verifier

package routes

import (
	"alexapp.pck.com/handler"
	"fmt"
	"github.com/go-chi/chi"
	"github.com/go-chi/jwtauth"
	"net/http"
)
var tokenAuth *jwtauth.JWTAuth

//InitIndexRoutes function
func InitRestrictedRoutes(c *chi.Mux, h *handler.Handler) {
	// Protected routes
	c.Group(func(r chi.Router) {

		// Seek, verify and validate JWT tokens
		r.Use(jwtauth.Verifier(tokenAuth))

		// Handle valid / invalid tokens. In this example, we use
		// the provided authenticator middleware, but you can write your
		// own very easily, look at the Authenticator method in jwtauth.go
		// and tweak it, its not scary.
		r.Use(jwtauth.Authenticator)

		r.Get("/restricted", func(w http.ResponseWriter, r *http.Request) {
			_, claims, _ := jwtauth.FromContext(r.Context())

			w.Write([]byte(fmt.Sprintf("protected area. hi %v", claims["user_id"])))
		})
	})
}

Screenshot 2019-09-10 at 21 52 54

It seems that for a reason the var tokenAuth *jwtauth.JWTAuth is of value nil in the actual jwtauth.Verifier(tokenAuth) code.
So this gives me the error :

2019/09/10 21:39:07 http: panic serving [::1]:53185: runtime error: invalid memory address or nil pointer dereference
goroutine 103 [running]:
net/http.(*conn).serve.func1(0xc00029c320)
        /usr/local/go/src/net/http/server.go:1769 +0x139
panic(0x1392660, 0x16ea470)
        /usr/local/go/src/runtime/panic.go:522 +0x1b5
github.com/go-chi/jwtauth.(*JWTAuth).Decode(0x0, 0xc0001cc547, 0x65, 0x10, 0x10, 0xc00005ba88)
       /src/github.com/go-chi/jwtauth/jwtauth.go:143 +0x26
github.com/go-chi/jwtauth.VerifyRequest(0x0, 0xc000169000, 0xc000246660, 0x3, 0x3, 0x0, 0xc0001e5f20, 0x0)
        /src/github.com/go-chi/jwtauth/jwtauth.go:106 +0x94
github.com/go-chi/jwtauth.Verify.func1.1(0x1483080, 0xc00025cfc0, 0xc000169000)
        /src/github.com/go-chi/jwtauth/jwtauth.go:80 +0x98

In the actual example, you have:
var tokenAuth *jwtauth.JWTAuth

//And you assign a value to it in the init() function :
// tokenAuth = jwtauth.New("HS256", []byte("secret"), nil)

In my case, I already have the token generated and saved somewhere else and all I want to do is use that token.

So when the code arrives in my initRestrictedFunction , what should the value for : tokenAuth be ?
On the line: r.Use(jwtauth.Verifier(tokenAuth))

Thanks

How set expires time in MapClaims

Hi all,

I am using chi for tests purposes, but I did not found any example that validates a jwt.MapClaims with expiration time.

I am using this:

var TokenAuth = jwtauth.New(JWTAlgorithm, JWTSecret, nil)

// Transaction paths
r.Group(func(r chi.Router) {
	r.Use(jwtauth.Verifier(TokenAuth))
	r.Use(jwtauth.Authenticator)
	_transactionHttpHandler.NewTransactionHandler(r, transactionUsecase)
})

And generate JWT with this:


func generateJWT(c *model.Client) (token string, err error) {
	_, token, err = TokenAuth.Encode(jwt.MapClaims{"clientId": c.ID.String()})
	if err != nil {
		return "", err
	}

	return token, nil
}

But my token has no expires time and I do not know to do this with this package. Someone can help?

Thanks :)

Interface conversion error when running example:

Hi. I'm trying to run the example:

r.Get("/secret", func(w http.ResponseWriter, r *http.Request) {
			ctx := r.Context()
			token := ctx.Value("jwt").(*jwt.Token)

			render.JSON(w, r, token)
})

and get the error panic: interface conversion: interface {} is *jwt.Token, not *jwt.Token
I'm a little bit confused, because if I try to run reflect.typeOf(token) - token has type *jwt.Token.
Could you say please, is there a way to solve this?

P.S.
Also, I'm tried to vendor dgrijalva/jwt-go 2.7.0 package, but got the same error

Repository Dead

looks like this repository is dead... no maintenance since jan.

Error with using Claims type as Map

Running the example gives the following error:

# github.com/goware/jwtauth
../github.com/goware/jwtauth/jwtauth.go:151: cannot use claims (type Claims) as type jwt.Claims in assignment:
        Claims does not implement jwt.Claims (missing Valid method)
../github.com/goware/jwtauth/jwtauth.go:173: invalid operation: t.Claims["exp"] (type jwt.Claims does not support indexing)
../github.com/goware/jwtauth/jwtauth.go:175: use of .(type) outside type switch

Incompatible with jwt-go 3.0.0

vendor/github.com/go-chi/jwtauth/jwtauth.go:44: parser.SkipClaimsValidation undefined (type *jwt.Parser has no field or method SkipClaimsValidation)

Looks like SkipClaimsValidation is no longer part of the Parser struct in 3.0.0.
https://github.com/dgrijalva/jwt-go/blob/release_3_0_0/parser.go

Using dep to install dependencies it tries to pull in version 3.0.0 because that is what is listed in the Gopkg.toml file.

[[constraint]]
  name = "github.com/dgrijalva/jwt-go"
  version = "^3.0.0"

TokenFromQuery is missing in Verifier

return Verify(ja, TokenFromHeader, TokenFromCookie)

According to the doc of the function, the verifier should search through URI query parameters.

However, the TokenFromQuery function is not called in the verifier

// Verifier will search for a JWT token in a http request, in the order:
//   1. 'jwt' URI query parameter
//   2. 'Authorization: BEARER T' request header
//   3. Cookie 'jwt' value

Cannot install using Go modules

At least with Go 1.13, I am not able to use 4.0.x versions. It's the same problem that chi v4 has, but there are changes in v4 here that I'm relying on. It looks like chi v4 was just an experiment in moduling.

Allow users to retrieve JWT from a query and cookie with a specified name

Describe the feature or problem you’d like to solve

Currently, if the name of the query param or cookie is other than jwt, it is necessary to implement logic to obtain jwt from the query or cookie on the user side(that's a bit cumbersome).

TokenFromCookie function
TokenFromQuery function

Proposed solution

Solve the above problem by providing a function that can retrieve jwt from a query or cookie with the name passed as an argument.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.