cyx

– programmer, co-founder openredis

–––––––––––––––––––––––––––––––––––––––

Password Recovery

Often times, when people think of doing password recovery, their immediate instinct is to store a unique token in the database, and to use that as an identifying piece of information.

That’s old school, show me the better way

The following example shows the entire sketch of doing password recovery with Cuba and Nobi, without going into the nitty gritty details of validation, error handling, and good UX messages (all of which are left as an exercise for the reader)

  1. You know cuba.
  2. You have a User model with a fetch method that retrieves via email, and also a User[] method which fetches by ID.
  3. You’ve already read the basics of nobi.
on "forgot-password" do
  on get do
    render("forgot-password")
  end

  on post do
    user = User.fetch(req[:email])

    on user do
      nobi = Nobi::TimestampSigner.new('my secret here')
      signature = nobi.sign(String(user.id))

      # Send an email here containing the token
      puts "Go to http://localhost:9393/otp/%s" % signature

      res.redirect "/login/?recovery=true", 303
    end

    on default do
      session[:error] = "Can't find a user with that email."

      res.redirect("/forgot-password", 303)
    end
  end

  on "otp/:signature" do |signature|
    nobi = Nobi::TimestampSigner.new('my secret here')

    # Let's unsign it, and specify that we want tokens to live
    # for only 2 hours
    user =
      begin
        user_id = nobi.unsign(signature, max_age: 7200)

        User[user_id]
      rescue Nobi::BadData
      end

    on user do
      on get do
        render("otp", user: user)
      end

      on post, param(:password), param(:password_confirmation) do |pass1, pass2|
        # You probably need to do some validation here, left
        # as an exercise for the reader :-p

        user.update(password: pass1)

        res.redirect "/", 303
      end
    end

    on default do
      session[:error] = "Invalid signature found"

      res.redirect("/forgot-password")
    end
  end
end

So that’s basically it, we’re relying on the heavy lifting of Nobi to deal with signature verification – and therefore the needless task of storing the token in the database.