-
Notifications
You must be signed in to change notification settings - Fork 605
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
flesh out server-side setup instructions in README #155
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,100 @@ This is an extension to the Ember.SimpleAuth library that provides an | |
authenticator and an authorizer that are compatible with customized | ||
installations of [Devise](https:/plataformatec/devise). | ||
|
||
## Server-side setup | ||
|
||
As token authentication is not actually part of Devise anymore, there are some | ||
customizations necessary on the server side. Most of this walk-through is | ||
adapted from [José Valim's gist on token authentication] | ||
(https://gist.github.com/josevalim/fb706b1e933ef01e4fb6). | ||
|
||
First, you must add a colunm to your database in which to store the | ||
authentication token: | ||
|
||
```ruby | ||
class AddAuthenticationTokenToUser < ActiveRecord::Migration | ||
def change | ||
add_column :users, :authentication_token, :string | ||
end | ||
end | ||
``` | ||
|
||
Then, you must set up your model to generate the token: | ||
|
||
```ruby | ||
class User < ActiveRecord::Base | ||
... | ||
before_save :ensure_authentication_token | ||
|
||
def ensure_authentication_token | ||
if authentication_token.blank? | ||
self.authentication_token = generate_authentication_token | ||
end | ||
end | ||
|
||
private | ||
|
||
def generate_authentication_token | ||
loop do | ||
token = Devise.friendly_token | ||
break token unless User.where(authentication_token: token).first | ||
end | ||
end | ||
end | ||
``` | ||
|
||
When a user signs in using Ember, requests are made with a JSON API. | ||
Unfortunately, Devise does not give the responses we need in JSON, so we must | ||
make our own controller: | ||
|
||
```ruby | ||
class SessionsController < Devise::SessionsController | ||
def create | ||
self.resource = warden.authenticate!(auth_options) | ||
sign_in(resource_name, resource) | ||
yield resource if block_given? | ||
response = { | ||
auth_token: resource.authentication_token, | ||
auth_email: resource.email | ||
} | ||
render json: response, status: 201 | ||
end | ||
end | ||
``` | ||
|
||
Then, we need to tell Devise to use this controller instead of its default: | ||
|
||
```ruby | ||
MyRailsApp::Application.routes.draw do | ||
devise_for :users, controllers: {sessions: "sessions"} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't that the default? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When you override a Devise controller, you need to tell the router where to find your controller. On second thought, maybe that's only if you don't monkey-patch the class as we're doing here... unfortunately the code I was playing around with is on my other computer. I'll check tonight. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, turns out you can't just monkey-patch Devise's class in any straightforward way. So you have to create your own SessionsController class that inherits from Devise's, and then tell Devise where to find it in your routes. |
||
... | ||
end | ||
``` | ||
|
||
Finally, we have to authenticate each subsequent request: | ||
|
||
```ruby | ||
class ApplicationController < ActionController::Base | ||
before_filter :authenticate_user_from_token! | ||
|
||
private | ||
|
||
def authenticate_user_from_token! | ||
token = request.headers['auth-token'].to_s | ||
email = request.headers['auth-email'].to_s | ||
return unless token && email | ||
|
||
user = User.find_by_email(email) | ||
|
||
if user && Devise.secure_compare(user.authentication_token, token) | ||
sign_in user, store: false | ||
end | ||
end | ||
end | ||
``` | ||
|
||
Now, we can get on to configuring the Ember side of things. | ||
|
||
## The Authenticator | ||
|
||
In order to use the Devise authenticator (see the | ||
|
@@ -40,24 +134,6 @@ App.LoginController = Ember.Controller.extend(Ember.SimpleAuth.LoginControllerMi | |
{ authenticatorFactory: "authenticator:devise" }); | ||
``` | ||
|
||
As token authentication is not actually part of Devise anymore, there are some | ||
customizations necessary on the server side. In order for the authentication to | ||
work it has to include the user's auth token and email in the JSON response for | ||
session creation: | ||
|
||
```ruby | ||
class SessionsController < Devise::SessionsController | ||
def create | ||
resource = resource_from_credentials | ||
data = { | ||
auth_token: resource.authentication_token, | ||
auth_email: resource.email | ||
} | ||
render json: data, status: 201 | ||
end | ||
end | ||
``` | ||
|
||
## The Authorizer | ||
|
||
The authorizer (see the | ||
|
@@ -75,28 +151,3 @@ Ember.Application.initializer({ | |
} | ||
}); | ||
``` | ||
|
||
As token authentication is not actually part of Devise anymore, the server | ||
needs to implement a custom authentication method that uses the provided email | ||
and token to look up the user (see | ||
[discussion here](https://gist.github.com/josevalim/fb706b1e933ef01e4fb6)): | ||
|
||
```ruby | ||
class ApplicationController < ActionController::API | ||
before_filter :authenticate_user_from_token! | ||
|
||
private | ||
|
||
def authenticate_user_from_token! | ||
token = request.headers['auth-token'].to_s | ||
email = request.headers['auth-email'].to_s | ||
return unless token && email | ||
|
||
user = User.find_by_email(email) | ||
|
||
if user && Devise.secure_compare(user.authentication_token, token) | ||
sign_in user, store: false | ||
end | ||
end | ||
end | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's only necessary in Devise's implementation of the
#create
method - you'd never get a block here.