I'm working with the jwt-go library and I've written tests for implementing it in my application. However, no matter what token I create, it is returned as valid. I'm guessing I'm not checking for something. The documentation is out of date because claims no longer support indexing.
This is my application code:
// AuthService - provides authentication
type AuthService struct{}
// CreateToken - signs and encrypts auth token
func (a *AuthService) CreateToken(email, password string) (string, error) {
token := jwt.New(jwt.SigningMethodHS256)
token.Claims = buildClaims(email, password)
tokenString, err := token.SignedString([]byte(os.Getenv("AUTH_SECRET")))
if err != nil {
return "", err
}
return tokenString, nil
}
// VerifyToken - ensures that the token is valid
func (a *AuthService) VerifyToken(email, password, token string) bool {
claims := buildClaims(email, password)
parser := new(jwt.Parser)
parsedClaims, _ := parser.ParseWithClaims(token, claims, getKey)
return parsedClaims.Valid
}
func getKey(t *jwt.Token) (interface{}, error) {
return []byte(os.Getenv("AUTH_SECRET")), nil
}
func buildClaims(email, password string) jwt.Claims {
claims := make(jwt.MapClaims)
claims["email"] = email
claims["password"] = password
claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
claims["iat"] = time.Now().Unix()
return claims
}
And this is the failing test:
func TestAuthToken(t *testing.T) {
// var err error
var valid bool
os.Setenv("AUTH_SECRET", "secret")
email := "[email protected]"
password := "password"
auth := &AuthService{}
token, err := auth.CreateToken(email, password)
if err != nil {
t.Errorf("AuthService failed to create a token: %v", err)
}
valid = auth.VerifyToken(email, password, token)
if !valid {
t.Errorf("AuthService failed to verify token: %v", err)
}
valid = auth.VerifyToken("invalid", "", token)
fmt.Println(valid)
if valid {
t.Error("AuthService verified a bad password")
}
}
What I do not understand is why the final test would return valid. In fact, I can put anything in the password or the email and they will be considered as valid.
Is it possible that this has something to do with the fact that password and email are not standard claims?
Yes, it's that because the email
and password
, are not the part of the standard Claims there is validation method which is used in your example
func (m MapClaims) Valid() error {
vErr := new(ValidationError)
now := TimeFunc().Unix()
if m.VerifyExpiresAt(now, false) == false {
vErr.Inner = errors.New("Token is expired")
vErr.Errors |= ValidationErrorExpired
}
if m.VerifyIssuedAt(now, false) == false {
vErr.Inner = errors.New("Token used before issued")
vErr.Errors |= ValidationErrorIssuedAt
}
if m.VerifyNotBefore(now, false) == false {
vErr.Inner = errors.New("Token is not valid yet")
vErr.Errors |= ValidationErrorNotValidYet
}
if vErr.valid() {
return nil
}
return vErr
}
Do you really need to verifie password and username? Because it's already checked when whole token is passed. But if you anyway wanna check this data you can make some struct
type CustomClaims struct {
jwt.StandardClaims
Email string `json:"email,omitempty"`
Password string `json:"password,omitempty"`
}
which you then can verify in your VerifyToken method
token after parsing calims.
if claims, ok := parsedClaims.Claims.(*CustomClaims); ok {
if claims.Password != password {
parsedClaims.Valid = false
}
if claims.Email != username {
parsedClaims.Valid = false
}
}
Some other things:
Claims
object or pass your own with your custom Valid
method which will satisfying interfaceNewWithClaims
method