sec51 / twofactor

Golang two factor authentication library
ISC License
217 stars 66 forks source link

Keep getting "Tokens mismatch" error #1

Closed junxie6 closed 8 years ago

junxie6 commented 8 years ago

Hi There,

I am trying to figure it out how to use this library. I have used ToBytes() function to serialize the totp struct and stored it in Redis database (and restore it by calling the twofactor.TOTPFromBytes() function). However, I keep getting the "Tokens mismatch" error when trying to verify the user provided token. I have verified the values of the byte array are exactly the same before storing into Redis and after retrieving from Redis.

Can someone help to review my code and let me know what I was doing wrong?

package main

import (
        "crypto"
        "fmt"
        "github.com/sec51/twofactor"
        "gopkg.in/redis.v3"
        "log"
        "net/http"
        "strconv"
)

var redisClient = redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // no password set
        DB:       0,  // use default DB
})

func handler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Home: %s!", r.URL.Path[1:])
}

func writeImage(w http.ResponseWriter, r *http.Request) {
        otp, err := twofactor.NewTOTP("info@sec51.com", "Sec51", crypto.SHA1, 6)

        if err != nil {
                log.Println("call NewTOTP error.")
        }

        qrBytes, err := otp.QR()

        if err != nil {
                log.Println("call QR error.")
        }

        if b, err := otp.ToBytes(); err != nil {
                log.Println("call ToBytes error.")
        } else {
                log.Printf("Before: %v\n", b) // show the byte array before storing in Redis

                if err := redisClient.Set("info@sec51.com", b, 0).Err(); err != nil {
                        log.Printf("H: %v\n\n", err)
                }
        }

        w.Header().Set("Content-Type", "image/png")
        w.Header().Set("Content-Length", strconv.Itoa(len(qrBytes)))

        if _, err := w.Write(qrBytes); err != nil {
                log.Println("unable to write image.")
        }
}

func verifyIt(w http.ResponseWriter, r *http.Request) {
        val, err := redisClient.Get("info@sec51.com").Bytes()

        log.Printf("Afterr: %v\n", val) // show the byte array after getting it from Redis

        otp, err := twofactor.TOTPFromBytes(val, "Sec51")

        if err != nil {
                log.Println("call TOTPFromBytes error.")
        }

        if err := otp.Validate(r.URL.Path[3:]); err != nil {
                fmt.Fprintf(w, "Wrong code: %s. Err: %v\n", r.URL.Path[3:], err)
        } else {
                fmt.Fprintf(w, "Good Job: %s.\n", r.URL.Path[3:])
        }
}

func main() {

        http.HandleFunc("/v/", verifyIt)
        http.HandleFunc("/qr/", writeImage)
        http.HandleFunc("/", handler)

        http.ListenAndServe(":80", nil)
}
silenteh commented 8 years ago

@junhsieh Looking into it. Will let you know as soon as possible.

silenteh commented 8 years ago

@junhsieh Thanks for reporting the issue. After taking a look at your code, there was nothing wrong about it. So I kept investigating and it turned out the problem was on my side. Unfortunately, I made the bad decision to blindly change the byte order serialisation during one of the last commits, when I updated a couple of dependencies. This broke the token generation part. I apologise for this.

Now if you execute: go get -u github.com/sec51/twofactor and therefore update your local version the problem should be gone. I have tested your code locally and now, after the fixes I made to the library, it works.

Before testing it, please clear the keys in your local testing redis server. Let me know how it goes. Thanks

junxie6 commented 8 years ago

@silenteh I just tried. It works as expected! Thanks for fixing the issue and providing the go get update command as I am relatively new to Go and the open source projects. :+1: