dgrijalva / jwt-go Goto Github PK
View Code? Open in Web Editor NEWARCHIVE - Golang implementation of JSON Web Tokens (JWT). This project is now maintained at:
Home Page: https://github.com/golang-jwt/jwt
License: MIT License
ARCHIVE - Golang implementation of JSON Web Tokens (JWT). This project is now maintained at:
Home Page: https://github.com/golang-jwt/jwt
License: MIT License
I was reading through the code, trying to get a feel for it as I'm fairly new with JWT and got slightly confused when I came to check out the Verify func on the SigningMethodHMAC (Haven't checked others yet)
https://github.com/dgrijalva/jwt-go/blob/master/hmac.go#L47-L67
As the err variable is declared on line 50 and we return that nil error on line 63 instead of just returning nil, it took a little longer than it should have to see that it was a successful verification at that point.
Can we remove line 50, use good old := on line 51 and then change line 60 to simply return ErrSignatureInvalid and 63 to return nil?
There have been several questions around keys and key formats. Currently, jwt.KeyFunc
expects the key as []byte
. This has a few negative consequences:
KeyFunc
I'm wondering if we shouldn't change the return type of KeyFunc
to (interface{}, error)
. If we make public the RSA parsePublicKey
and parsePrivateKey
, it won't be terribly difficult to keep existing behavior by calling these and returning the result. This will allow users of this library to decide how their keys will be serialized/stored and it will allow in-memory caching of already parsed keys.
The negative consequence I see are:
interface{}
is not very descriptive about what object type each signing method expects. Unfortunately, I don't think there's a lot we can do about this. It's not any worse than the current case.For ease of integration, we could have the RSA library attempt to parse a []byte
value if it's passed in.
Thoughts?
The JWT MUST contain an "exp" (expiration time) claim that limits the time window during which the JWT can be used. The authorization server MUST reject any JWT with an expiration time that has passed, subject to allowable clock skew between systems. - Source
if exp, ok := token.Claims["exp"].(float64); ok {
if now > int64(exp) {
vErr.err = "token is expired"
vErr.Errors |= ValidationErrorExpired
}
}
// No error if exp could not be asserted to float64
Only nbf is optional.
Hi folks,
Looking at your example:
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.")
}
What exactly is myLookupKey
?
I have:
package services
import (
"bitbucket.org/bandzest/applications/core/api/drivers"
"bitbucket.org/bandzest/applications/core/api/models"
jwt "github.com/dgrijalva/jwt-go"
"time"
)
var (
jwtKey = "testing123"
)
func CreateToken(user *models.User) (string, error) {
token := jwt.New(jwt.SigningMethodHS256)
// Set some claims
token.Claims["id"] = user.Id
token.Claims["username"] = user.Name
token.Claims["email"] = user.Email
token.Claims["iat"] = time.Now().Unix()
token.Claims["exp"] = time.Now().Add(time.Second * 3600 * 24).Unix()
// Sign and get the complete encoded token as a string
tokenString, err := token.SignedString([]byte(jwtKey))
return tokenString, err
}
func ValidateToken(tokenString string) (bool, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte("jwtKey"), nil
})
if err != nil {
panic(err)
}
if !token.Valid {
return false, nil
}
return true, nil
}
But I'm getting the error Panic recovery -> signature is invalid
.
Thanks in advance!
Hi there. Thanks for the library.
Per https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-3.1 the spec supports PSS and ECDSA. It looks like the go crypto library supports this as well. Do you have any plans to implement this as a signing mechanism?
If not I can take a crack at the implementation
+--------------+-----------------------------------+----------------+
| alg Param | Digital Signature or MAC | Implementation |
| Value | Algorithm | Requirements |
+--------------+-----------------------------------+----------------+
| HS256 | HMAC using SHA-256 | Required |
| HS384 | HMAC using SHA-384 | Optional |
| HS512 | HMAC using SHA-512 | Optional |
| RS256 | RSASSA-PKCS-v1_5 using SHA-256 | Recommended |
| RS384 | RSASSA-PKCS-v1_5 using SHA-384 | Optional |
| RS512 | RSASSA-PKCS-v1_5 using SHA-512 | Optional |
| ES256 | ECDSA using P-256 and SHA-256 | Recommended+ |
| ES384 | ECDSA using P-384 and SHA-384 | Optional |
| ES512 | ECDSA using P-521 and SHA-512 | Optional |
| PS256 | RSASSA-PSS using SHA-256 and MGF1 | Optional |
| | with SHA-256 | |
| PS384 | RSASSA-PSS using SHA-384 and MGF1 | Optional |
| | with SHA-384 | |
| PS512 | RSASSA-PSS using SHA-512 and MGF1 | Optional |
| | with SHA-512 | |
| none | No digital signature or MAC | Optional |
| | performed | |
+--------------+-----------------------------------+----------------+
https://github.com/dgrijalva/jwt-go/blob/master/hmac.go#L70 tries to do a type assertion on interface{} to []byte but this will not work on strings. See http://play.golang.org/p/mYMjdz8vQi for a simple example.
What I do not understand is why all the examples and tests pass a string
into token.SignedString() when you should really pass in []byte
.
For example https://github.com/dgrijalva/jwt-go/blob/master/example_test.go#L28 (You can see that .Sign() is directly invoked https://github.com/dgrijalva/jwt-go/blob/master/jwt.go#L60)
In general, I do not understand why you are relying on interface{}
in methods instead of explicitly expecting []byte
. (t *Token) SignedString(key interface{}
and (m *SigningMethodHMAC) Sign(signingString string, key interface{})
for two examples.
I think you need to modify the type from []byte to interface{} ... on line 151
token, err := jwt.Parse(tokenCookie.Value, func(token *jwt.Token) (interface{}, error) {...
Currently there is no way for callers of the func Parse to handle error types returned by the extension point KeyFunc. In other words, in my implementation of KeyFunc I return my own error type with my error code in it. Parse does not surface this information to its caller and therefore I cannot easily handle my own error type and code. My suggestion to tackle this is to include an exported field on the type ValidationError, something like "InnerError error", here you can place the whole error returned by KeyFunc. Currently when KeyFunc fails I see that you place the .Error() of that failure in the ValidationError.err but this is not quite the same as keeping the whole KeyFunc error type since that can contain more information. Another option is to provide another extension point so that KeyFunc errors can be handled on a custom fashion.
Hi, in our project the claims part of the JWT token is getting really long. I was wondering if it would be possible to compress the data in the claims part.
It would look pretty straightforward,
I would add an 'compression' : 'gzip' key and value to the header map. This can be done by adding a new argument to the jwt.New() function.
The rest would be transparent. Just add compression between JSON encoding and Base64 encoding,
and decompression symmetrically.
I would rather contribute to the main repo instead of forking it or doing the same thing in our code.
I can't find where the field Token.Signature is populated while parsing.
A comment in line 29 in jwt.go says: "Populated when you Parse a token"
It doesn't seem to be necessary to populate it while Parsing, because the token verification works either way, but I thought I'd point it out anyway since the comment clearly says it is. It might confuse some people... like me :(
While testing I accidentally used a token without the last character in the signature and I kept getting an "invalid signature error" because of it. So I tried printing the token's fields before and after calling token.Parse and the Signature field was always empty. It made me think there was something wrong with my code or the library and it took me quiet a long time to figure out my token was missing a character :/
I implemented Googles Oauth2 for websites.
https://developers.google.com/identity/sign-in/web/backend-auth
ParseRSAPublicKeyFromPEM() seems to expect a full PEM encoded public key,
such as one of these: https://www.googleapis.com/oauth2/v1/certs
But the JavaScript creates a PWT with only the key to the PEM above, i.e.
9015759ea37707cb6d325cca00e6299231b7f72f
The PWT is created by https://apis.google.com/js/platform.js.
How is that supposed to work?
Is there a "PEM Key Lookup" stage, that I overlooked?
Is my use case off base?
I changed the code.
I replaced the key with the full PEM.
Then it works perfectly.
Regards
Peter
Hi Dave,
I found 2 incorrect comments in rsa_pss.go
(line 72 and 101):
// Implements the Verify method from SigningMethod
// For this verify method, key must be an *rsa.PrivateKey(should be rsa.PublicKey?) struct
func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error {
// Implements the Sign method from SigningMethod
// For this signing method, key must be an *rsa.PublicKey(should be rsa.PrivateKey?) struct
func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) (string, error) {
Your jwt-go is cool and I love it:)
The example on jwt.io has the following example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
encoded with secret
In jwt-go/cmd/jwt
, I created a file named key
with content secret
and ran the following command but encountered an error:
ยป echo eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ | go run app.go -key="./key" -verify -
Error: Couldn't parse token: signature is invalid
exit status 1
Any ideas on what I'm doing wrong?
I use this method in creating token:
token := jwt.New(jwt.SigningMethodHS256)
token.Claims["id"] = user.UserID
token.Claims["iat"] = time.Now().Unix()
token.Claims["exp"] = time.Now().Add(time.Second * 3600 * 24).Unix()
jwtString, err := token.SignedString([]byte("mysupersecretkey"))
fmt.Println(jwtString);
Then I take that string i get echoed out an try to do a simple call to the my root route which has this inside it:
func IndexHandler(env *db.Env, w http.ResponseWriter, r *http.Request) error {
jwtString := r.Header.Get("Authorization")
token, err := jwt.ParseFromRequest(jwtString, keyFn)
fmt.Println(token)
fmt.Println(err)
if err == nil && token.Valid {
// token parsed, exp/nbf checks out, signature verified, Valid is true
fmt.Fprint(w, "Welcome!\n")
return nil;
}
fmt.Println("index");
fmt.Fprint(w, "NOT AUTHORIZED")
return nil
}
func keyFn(token *jwt.Token) (interface{}, error) {
return []byte("mysupersecretkey"), nil
}
But I keep getting error illegal base64 data at input byte 6 or illegal base64 data at input byte 4.
What am i missing? I have read that i need to return an interface from the keyFn, ref this http://stackoverflow.com/questions/25848215/go-and-parsing-token-with-jwt-go.
But I dont understand what that should be? Any help here? Cant seem to find any examples that work for me. Also tried this example, https://sendgrid.com/blog/tokens-tokens-intro-json-web-tokens-jwt-go/, but its pretty the same thing.
Would be great if the key has an interface with a Byte()
func instead of interface{}
type
What do you think? Implicit is better than explicit :), btw this would introduce a huge BC...
cfr: gin-gonic/contrib#19
Would there be any chance we can get support for encrypted tokens added? Or would this be something we should look for another package to do?
I got the following error:
key is invalid or of invalid type
by the following code. I'm not sure if I'm missing anything.
package main
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"time"
)
func main() {
tokenString, err := CreateJwtToken()
if err != nil {
fmt.Println(err)
}
fmt.Println(tokenString)
}
func CreateJwtToken() (string, error) {
token := jwt.New(jwt.SigningMethodHS256)
token.Claims["foo"] = "bar"
token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
tokenString, err := token.SignedString("netdata.io")
return tokenString, err
}
I have code like this
token := jwt.New(jwt.GetSigningMethod("HS256"))
token.Claims["ID"] = "Christopher"
token.Claims["exp"] = time.Now().Add(time.Hour * 1).Unix()
s, e := token.SignedString([]byte("unicornsAreAwesome"))
if e != nil {
panic(e.Error())
}
fmt.Println(s)
when I'm running I get
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJJRCI6IkNocmlzdG9waGVyIiwiZXhwIjoxNDM5ODE2Njg2fQ.OU2K92mLwEy9hrL-gwiNEm1Rd_vVVCZ0o3mYxdT2e-I
but when I want to parse it with this code
myToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9leGFtcGxlLm9yZyIsImF1ZCI6Imh0dHA6XC9cL2V4YW1wbGUuY29tIiwiaWF0IjoxMzU2OTk5NTI0LCJuYmYiOjEzNTcwMDAwMDB9.KcNaeDRMPwkRHDHsuIh1L2B01VApiu3aTOkWplFjoYI"
token, err := jwt.Parse(myToken, nil)
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.")
}
I have This token is terrible! I cannot accept this.
Hi,
I believe some of the vulnerabilities mentioned in this article are also applicable to this library:
Critical vulnerabilities in JSON Web Token libraries
Think it would be good to add an argument to the Parse
function which tells it which algorithm to use.
Hi there, just a note, not sure if this would be a bug, or just a normal behaviour that's to be expected with signing tokens via RSxxx.
On my system, where I need to sign one or many token at once. When I sign a token, it may take roughly 200-250ms for jwt.SignedString()
. Of course, depending on what machine I use, it may be faster or slower due to CPU.
But when I tried signing 3 different tokens at the same time, using the same privateKey, the results are almost 3x longer.
The source for my implementation in psuedocode-ish looks like this:
for item := range stuff {
i := doSomethingWith(item)
go func() {
doBasicClaims()
AddPayloadClaim(i)
s := jwt.SignedString(privateKey)
sendOffToken(s)
getBackData()
}
}
So for this, all the other stuff took only a few ms, but the goroutines will take a total of 600 to 900 ms on a request that produces 3 iterations.
Here's what the verbose log looks like
Started POST /settings for localhost:10000
Processing all stuff 915.559ยตs
Iterating through 3 settings request 933.57ยตs
Finished processing all stuff, and waiting for return 954.803ยตs (for range:)
Processing AAAA 989.179ยตs
Before token signing with RSA256 1.175348ms
Processing BBBB 11.132295ms
Before token signing with RSA256 11.341492ms
Processing CCCC 22.01571ms
Before token signing with RSA256 22.350366ms
Token signing for BBBB took 613.113249ms current time @ 624.455326ms
Sent Request for BBBB 624.532368ms
Token signing for AAAA took 658.879584ms current time @ 660.055904ms
Sent Request for AAAA 660.175747ms
Token signing for CCCC took 656.992226ms current time @ 679.343004ms
Sent Request for CCCC 679.402761ms
Got back reply from AAAA 684.077767ms travel time took 23.902779ms
Finished Processing AAAA 684.188976ms
Got back reply from CCCC 686.740498ms travel time took 7.338188ms
Finished Processing CCCC 686.815321ms
Got back reply from BBBB 767.306613ms travel time took 142.774982ms
Finished Processing BBBB 767.355212ms
Completed 200 OK in 767.547319ms: served 1511 bytes
So, the question is, is this expected behaviour that I'm observing? I would be afraid to do this on hundreds of simultaneous token creations with RSA.
Just a note, the getBackData()
parts are actually tokens signed with HS256 over a network, and the slowest one that took 142ms (BBBB), is actually from a Raspberry Pi. Which means, it took less time to verify an RSA signature, and generate a SignedString() with HS256 than to actually sign with RSA on a faster machine.
In the README this example is given:
token, err := jwt.Parse(myToken, func(token *jwt.Token) ([]byte, error) {
return myLookupKey(token.Header["kid"])
})
if err == nil && token.Valid {
deliverGoodness("!")
} else {
deliverUtterRejection(":(")
}
Is there any case where err == nil
but token.Valid == false
or can we just check err
?
When verifying the token in the gist example "func (m *SigningMethodHS256) Verify" in sha256 throws "Signature is invalid" error. But in the example you ignore it and just check for type of error. I don't understand it. Can you explain ?
Opening a ticket for discussion. There are several proposed changes that will not be backwards compatible. It makes sense to land them together so we don't have to break integration multiple times. These are the changes I'm thinking of for 3.x:
[]byte
to the RSA signing methods. Instead, use the helper methods to deserialize your keys first. See #59Claims
. See #66 and #73json.Number
, though I haven't fully grokked that yet. I need to re-read the json library documentation. #68ParseFromRequest
to a sub package and adding some options. This is a very popular request.Things that look like they can land in 2.x:
I'm still on the fence about supporting none out of the box, though, if we did it would look something like #34 (comment)
Thoughts? Ideas? Did I miss something?
The example given in the readme shows something like:
token.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
And this makes sense, feels like idiomatic Go. However, the actual implementation that checks for expired tokens expects a float64
. What is the proper methodology here? I'm guessing float64
as the tests were written this way, so then maybe the README.md needs to be updated accordingly?
I'd be interested in thoughts on this!
Hey!
I'm trying out the 3.0.0 release, works perfectly fine except one thing, I can't read the claims.
Panic: jwt.Claims is *jwt.StandardClaims, not jwt.StandardClaims
Code is pretty much the same as it is in the readme.
token, err := jwt.ParseWithClaims(t, keyFunc, &jwt.StandardClaims{})
claims := token.Claims.(jwt.StandardClaims)
fmt.Println(claims.Subject)
Any idea how to fix it? Or should I just go back to 2.0 for the time being?
I was trying to use the ParseFromRequest method and seem to have hit a problem when I send in a bad token in my Authorization header. If I delete a character or two from the token I send in I'll get something like:
Error while verifying token: *jwt.ValidationError:invalid character ''' looking for beginning of value
I've added a test case at mojotalantikite/jwt-go@7de7ec4ef43cbe0b3ec5f76a7a01b2285f4a7de7 where I'm just removing the 'ey' from the front of the token as an example.
http://stackoverflow.com/questions/25848215/go-and-parsing-token-with-jwt-go
I think you need to update the readme if I am not mistaken?
token, err := jwt.Parse(t1, func(token *jwt.Token) (interface{}, error) {
var b bytes.Buffer
b.Write(publicKey)
return b, nil
})
I am trying to parse a auth0.com JWT token that contains the CLIENT_ID in the payload. Are audiences supported? The token gets parsed successfully but Token.Valid is set to false. Where can I pass the CLIENT_ID? Thanks for your help
token, err := jwt.Parse("MY_JWT_TOKEN", func(token *jwt.Token) (interface{}, error) {
return []byte("CLIENT_SECRET"), nil
})
"Claims": {
"aud": "CLIENT_ID",
}
All of the examples I've found are for validating a token using HS256
using a []byte()
for the secret. I'm wondering if there are any examples for validation when using keys?
Hi,
I noticed that the library will panic due to invalid memory address or nil pointer dereference if there is is a space in a token.
I just noticed this while playing around with PostMan Rest Client. If I send something like:
Authorization: asdasd asd asd asd
There is no problem, but if I send:
Authorization: bearer asd asd asd asd
Then the server panics. I guess this isn't a big issue really, I just thought you should be aware of it.
Hey,
i just tested the new version go1.3rc1 and sadly it makes the test fail (for me at least, please verify)
--- FAIL: TestJWT (0.29 seconds)
jwt_test.go:93: [basic expired] Claims mismatch. Expecting: map[foo:bar exp:1.401757378e+09] Got: map[]
jwt_test.go:108: [basic expired] Errors don't match expectation
jwt_test.go:93: [basic nbf] Claims mismatch. Expecting: map[foo:bar nbf:1.401757578e+09] Got: map[]
jwt_test.go:108: [basic nbf] Errors don't match expectation
--- FAIL: TestParseRequest (0.29 seconds)
jwt_test.go:134: [basic expired] Claims mismatch. Expecting: map[foo:bar exp:1.401757378e+09] Got: map[]
jwt_test.go:134: [basic nbf] Claims mismatch. Expecting: map[foo:bar nbf:1.401757578e+09] Got: map[]
--- FAIL: TestHS256Verify (0.00 seconds)
sha256_test.go:43: [web sample] Error while verifying key: illegal base64 data at input byte 44
FAIL
exit status 1
FAIL github.com/dgrijalva/jwt-go 0.678s
A t.Log(err)
before the DeepEqual
in TestJWT
reviled this:
illegal base64 data at input byte 48
The error is returned by the 2nd DecodeSegment
in jwt.go L103
Googles OAuth2 JWT seems to have a similar issue. I adapted their workaround, which is really similar to the switch len(seg) %4 {}
that was in DecodeSegment
before.
In some scenarios getting access to an in memory structure representing the claims can be really useful, for example to integrate with https://github.com/xeipuuv/gojsonschema.
The feature is mostly already there, just would need to be exposed separately.
Thoughts?
Hi all,
I was working on something off of the advanced-ish gist from Cryptixfor this lib: https://gist.github.com/cryptix/45c33ecf0ae54828e63b
I ran the
openssl genrsa -out app.rsa 2048
openssl rsa -in app.rsa -pubout > app.rsa.pub
commands and saved the files into a keys/ dir. I got back two PEM format type files (PUBLIC KEY and PRIVATE KEY).
my go code is throwing the error, "key is invalid or of invalid type" when I try to use the token.SignedString() method as shown in the gist. I checked the source but this error is thrown in many places and I don't know how to resolve it.
package xyz
const privKeyPath string = "keys/app.rsa"
const pubKeyPath string = "keys/app.rsa.pub"
var signKey *rsa.PrivateKey
var verifyKey *rsa.PublicKey
func init() {
var signBytes []byte
var verifyBytes []byte
var err error
signBytes, err = ioutil.ReadFile(privKeyPath)
if err != nil {
log.Print(err)
}
signKey, err = jwt.ParseRSAPrivateKeyFromPEM(signBytes)
if err != nil {
log.Print(err)
}
verifyBytes, err = ioutil.ReadFile(pubKeyPath)
if err != nil {
log.Print(err)
}
verifyKey, err = jwt.ParseRSAPublicKeyFromPEM(verifyBytes)
if err != nil {
log.Print(err)
}
}
func LoginAccount(username string, password string) (string, error) {
var token_str string
// get the account info from db
combination := string(salt) + string(password)
passwordHash := sha1.New()
io.WriteString(passwordHash, combination)
match := bytes.Equal(passwordHash.Sum(nil), hashedPassword)
if !match {
return token_str, errors.New("invalid password")
}
// create the jwt token
token := jwt.New(jwt.SigningMethodHS256)
token.Claims["username"] = username
token.Claims["iat"] = time.Now()
token_str, err = token.SignedString(signKey)
if err != nil {
return token_str, err
}
return token_str, nil
}
According to jwt.io, jwt-go is missing the following checks:
*iss check
*sub check
*aud check
*iat check
*jti check
I am not sure what exactly they entail, but does their lack create any security concerns?
I'd like to pass in a custom type that I can have the JWT claims unmarshaled into rather than going into the default [map]{interface}. I don't know if it's easier to pass in a UDF that runs on the claim or if we can just pass in a type and implement the unmarshal interface for custom processing in an external package. Is there a technical reason that wouldn't work?
I've try to implement this gist example but unfortunatly this step return an error:
crypto/rsa: message too long for RSA public key size
indeed I've generate rsa key pair using:
$ openssl genrsa -out my_key.rsa 256
$ openssl rsa -in my_key.rsa -pubout > my_key.rsa.pub
do you know how can I fix that?
Hi,
Just curious about which draft version is implemented by this library.
For instance the ruby one listed on http://jwt.io clearly states it's implementing draft 6.
Putting it in the README as they do might help.
Thanks!
The original ParseFromRequest
method was just a helper I inserted based on how I was using this library. It is useful as an example, but far more specific than I'm comfortable with for this library. It's also hard to change it's behavior without introducing risk to users of the library.
I'd like to migrate, for version 3.0, all the request parsing behavior into a sub-package. In doing so, we should also modify it to be flexible, but have well defined behavior. Adding functionality should not introduce unexpected behavior for existing users.
I'm struggling to get the example from README
to work, and keep getting error Signature is invalid
My code is as follows:
package main
import (
"fmt"
jwt "github.com/dgrijalva/jwt-go"
"io/ioutil"
"time"
)
func main() {
privateKey, err := ioutil.ReadFile("keys/app.rsa")
if err != nil {
fmt.Println("Error reading private key")
return
}
t := jwt.New(jwt.GetSigningMethod("HS256"))
t.Claims["AccessToken"] = "bar"
t.Claims["exp"] = time.Now().Add(time.Hour * 72).Unix()
tokenString, err := t.SignedString(privateKey)
fmt.Println("tokenString: " + tokenString)
token, err := jwt.Parse(tokenString, func(token *jwt.Token) ([]byte, error) {
fmt.Printf("parsed token obj: %v\n", token)
publicKey, err := ioutil.ReadFile("keys/app.rsa.pub")
if err != nil {
return nil, fmt.Errorf("Error reading public key")
}
return publicKey, nil
})
if err == nil && token.Valid {
//Carry on
} else {
fmt.Printf("Token parse error: %v\n", err)
}
}
Running this generates the following output:
tokenString: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBY2Nlc3NUb2tlbiI6ImJhciIsImV4cCI6MTM5MzEzODM5NH0.Bc15fCosx8K4PD2gavPIGoYTrxEvslhcw8sIa8GgYJc
parsed token obj: &{eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBY2Nlc3NUb2tlbiI6ImJhciIsImV4cCI6MTM5MzEzODM5NH0.Bc15fCosx8K4PD2gavPIGoYTrxEvslhcw8sIa8GgYJc map[alg:HS256 typ:JWT] map[AccessToken:bar exp:1.393138394e+09] 0x771300 false}
Token parse error: Signature is invalid
What am I missing?
Hi,
I installed the jwt tool with go get github.com/dgrijalva/jwt-go/cmd/jwt and ran it using your example:
echo '{"foo":"bar"}' | jwt -key mykey -alg RS256 -sign - | jwt -key mykey.pub -verify -
It crashed with a "slice bounds out of range" Error in
main.verifyToken.func1 -> app.go:123
github.com/dgrijalva/jwt-go.Parse -> jwt-go/jwt.go:127
main.verifyToken -> jwt/app.go:127
main.start -> jwt/app.go:57 +0x67
main.main -> jwt/app.go:46
Hope that helps.
Can you add an example for checking if a token is valid? For some reason if there is a validation error it uses a bit instead of a specific err variable. Do you do this because you can set multiple errors this way(can you even do this with bits?)? I see that you use |=
which I couldn't find much info about. However I made a playground to test it and it seems to work the same as =
.
This is what I'm currently using to check for a validation error but I don't fully understand if there can be multiple validation errors because of how you set them with bits. Also I don't know if this will work if there are multiple errors.
if vErr,ok := err.(jwt.ValidationError); ok {
if vErr.Errors == jwt.ValidationErrorExpired {
//handle the error
}
}
Would you mind explaining how this works a bit more or perhaps switching the validation errors to regular error variables in a future version. Thanks
Is it possible to marshal an object from a t.Claims
value (which is a map[string]interface{}
) back to its original type?
Typically this would just be a type assertion away - e.g.
type User struct {
ID string
Data string
Admin bool
}
// Generation
user = &User{ID: "1313193933391913", Data: "[email protected]", Admin: false}
t.Claims["user"] = user
// Parsing after calling jwt.Parse(...)
val, ok := t.Claims["user"].(*User)
if !ok {
return nil, ErrInvalidUser
}
// val is of type *User.
return val, nil
My assumption here is that the boolean field in the struct is being marshalled to a string and cannot be un-marshalled back to the struct based on the below:
fmt.Printf("Type: %t\nValue: %v\n", t.Claims["user"], t.Claims["user"])
// Outputs:
Type: map[%!t(string=ID):%!t(string=1313193933391913) %!t(string=Data):%!t([email protected]) %!t(string=Admin):false]
Value: map[ID:1313193933391913 Data:[email protected] Admin:false]
Is there a way to resolve this without writing a ton of un-marshalling logic?
Is there an easy way to invalidate tokens from the server side?
What do you think about returning an error instead panicking ?
I'm unable to recreate this occurrence but I was able to produce a JWT that contained 5 segments. I've built a test for it but have hit padding issues similar to what was resolved in: commit: 679bf0f .
JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOiIyMDE1LTAxLTIyVDEzOjM5OjM2Ljc1MjcyNDA0Ny0wNTowMCIsImlkIjoxLCJpc3MiOiJsb2NhbGhvc3QiLCJpdCI6ImlaY0hjZG9JdlluSnBiRXBEaElQWWc9PSJ9.DRFip65fLbqOomUT82q1cin9s2_znpbiBcnHMU2-Vcg.eyJpYXQiOiIyMDE1LTAxLTIyVDEzOjIxOjI1LjQyMTAxMzczOC0wNTowMCIsImlkIjoxLCJpc3MiOiJsb2NhbGhvc3QiLCJpdCI6IkFETG9Bc3h1VklUdlc2MmpYd2lUYnc9PSJ9.frBtab8-eul5kle2iLwF0krmG0Cor0SeGs-3EKx6TL0
This results in 5 segments with header and claims correctly formatted but the third segment seems to be corrupted:
// Header
{
"alg": "HS256",
"typ": "JWT"
}
// Claims
{
"iat": "2015-01-22T13:39:36.752724047-05:00",
"id": 1,
"iss": "localhost",
"it": "iZcHcdoIvYnJpbEpDhIPYg=="
}
The crypto segment is very odd, but the second "sub-segment" in the crypto is the claims:
DRFip65fLbqOomUT82q1cin9s2_znpbiBcnHMU2-Vcg.eyJpYXQiOiIyMDE1LTAxLTIyVDEzOjIxOjI1LjQyMTAxMzczOC0wNTowMCIsImlkIjoxLCJpc3MiOiJsb2NhbGhvc3QiLCJpdCI6IkFETG9Bc3h1VklUdlc2MmpYd2lUYnc9PSJ9.frBtab8-eul5kle2iLwF0krmG0Cor0SeGs-3EKx6TL0
I made a hack to accept the multiple parts and just string it back together in Parse(): https://github.com/DisruptiveMind/jwt-go/blob/parse-multiple-segments/jwt_test.go#L180
The JWT seems to validate using jwt.io if that helps anything....
HS256
is)The example from the README fails with a panic when attempting to use RS256
(the example is working if I switch to HS256
):
// Create the token
token := jwt.New(jwt.GetSigningMethod("RS256"))
// 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)
Panic:
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x20 pc=0x438ef7]
goroutine 1 [running]:
runtime.panic(0x60a960, 0x8a4b28)
/usr/local/go/src/pkg/runtime/panic.c:266 +0xb6
github.com/dgrijalva/jwt-go.New(0x0, 0x0, 0x0)
/home/kmp/go/src/github.com/dgrijalva/jwt-go/jwt.go:39 +0x27
main.main()
/home/kmp/projects/playground/googleapi.go:25 +0x54
exit status 2
I tested this with the jwt debugger at http://jwt.io. This key encoded with ruby-jwt
and will not decode with go.
secret: supersecretkey
token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAvbWFuaWZlc3QuanNvbiJ9.a8DtiNS6v-n-OqLeVw2HTPDICGRVey3b-PUYBXlQkNk
code:
func parseToken(tokenString string) *jwt.Token {
token, err := jwt.Parse(tokenString, keyFn)
if err != nil {
panic(err)
}
return token
}
func keyFn(token *jwt.Token) (interface{}, error) {
println(token.Method.Alg())
return vars.ManifestKey, nil
}
error:
key is invalid or of invalid type
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.