2

I'm new to rails and are have a pretty basic understanding of the Devise Gem. Besides the CRUD and views I'm not clear on what it provides that could help me for a AngularJs app talking to a Rails Json Api.

At the moment I'm hand rolling things ie. for security I have I exchange a HTTP Header token between client (js) and server. I'm also using the Railscast #250 for user authentication - but as I don't see how to apply the SessionController for a remote client.

Are there any strategies I could employ for authentication and managing session via a remote json API?

Thanks!

MikeW
  • 4,749
  • 9
  • 42
  • 83

1 Answers1

0

I personally wouldn't use devise for something like this because there's only a small part of it you'd be using anyways

Dont

You pretty much just don't use a session. All you need to do is pass in basic authentication each time, and in the application controller you determine if its valid, if not just send them back an auth error.

Example request: http://username:password@example.com/api/endpoint

class ApplicationController
  before_filter :check_auth!

  private
  def check_auth!
    username, password = ActionController::HttpAuthentication::Basic::user_name_and_password(request)
    user = User.find_by(username: username)
    if user && user.encrypted_password == SomeEncryptFunction(params[:password])
      @current_user = user
    else
      raise "error"
    end
  end
end

But if you want to...

Then what you can do is update a DateTime field on the user when they first auth (which starts their session), then on subsequent calls they can just pass a token you give them that you you check for each time they sign in. You also check that only a certain amount of time has passed since they first authed, otherwise their session is invalid.

class SessionsController < ApplicationController
  skip_before_filter :check_auth!
  before_filter :login!

  private
  # Note: I don't remember the actual devise method for validating username + password
  def login!
    user = User.find_by(username: params[:username])
    if user && user.valid_password(params[:password])
      current_user = user
      current_user.update_attributes(
        authenticated_at: DateTime.now,
        authentication_token: Devise.friendly_token
      )
    else
      raise "error"
    end
  end
end

class ApplicationController
  before_filter :check_auth!

  private
  def check_auth!
    if valid_token(params[:token])
      current_user = User.find_by(authentication_token: params[:token])
    else
      raise "error"
    end
  end

  # Returns true if token belongs to a user and is recent enough
  def valid_token(token)
    user = User.find_by(authentication_token: params[:token])
    user && user.authenticated_at < DateTime.now - 1.day
  end
end
Tom Prats
  • 7,364
  • 9
  • 47
  • 77
  • Great thanks for that - gave me some useful ideas on token authenticity too. I noticed you use Devise tokens here, I've used BCrypt and SecureRandom - in this case I could do without Devise completely do you think? – MikeW Mar 05 '14 at 05:33
  • yeah, definitely go with it! SecureRandom is great for tokens and BCrypt is great for passwords, so you're all set – Tom Prats Mar 05 '14 at 06:15
  • 1
    `User.find_by(authentication_token: params[:token])` is susceptible to [timing attacks](http://codahale.com/a-lesson-in-timing-attacks/) and is [the reason why TokenAuthenticatable was removed from Devise](http://blog.plataformatec.com.br/2013/08/devise-3-1-now-with-more-secure-defaults/). Consider doing something like [this](https://gist.github.com/josevalim/fb706b1e933ef01e4fb6https://gist.github.com/josevalim/fb706b1e933ef01e4fb6) instead. – Ashitaka Mar 05 '14 at 12:44
  • @Ashitaka's link didn't work for me -- try https://gist.github.com/josevalim/fb706b1e933ef01e4fb6 – Tyler Mar 05 '14 at 16:57
  • Pasted the link twice, it seems. I wish comments were editable. Thanks, tyler. – Ashitaka Mar 05 '14 at 22:59