Simple Auth Devise giving unauthenticated error in Ember Rails - ruby-on-rails

I am setting up an ember app that is backed by ruby on rails. I am running into issues with my sign in action using simple-auth and simple-auth-devise. I successfully retrieve the sessions authentication token and username when I submit a correct username and password, but I am still given a 401 access denied error and I can't figure out why. I suspect that it may have to do with the naming of email versus user_email and token vs user_token business. I am taking this code mostly from dayjot, so you'd think it would be trivial to track down this bug but I am having tons of issues finding the exact issue. Thanks for any help you can give me!
The exact error I get in the rails server is:
Started GET "/users/me" for 127.0.0.1 at 2015-02-17 10:25:31 -0600
Processing by UsersController#me as JSON
Parameters: {"user"=>{}}
Filter chain halted as :authenticate_user! rendered or redirected
Completed 401 Unauthorized in 5ms (Views: 4.1ms | ActiveRecord: 0.0ms)
In rails, this is my application controller:
This is my application controller:
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
# protect_from_forgery with: :null_session
before_action :authenticate_user_from_token!, :handle_html
around_action :user_time_zone, if: :current_user
def index
render file: 'public/index.html'
end
protected
def authenticate_user!
render(json: {}, status: 401) unless current_user
end
private
def authenticate_user_from_token!
authenticate_with_http_token do |token, options|
user_email = options[:user_email].presence
user = user_email && User.find_by_email(user_email)
if user && Devise.secure_compare(user.authentication_token, token)
request.env['devise.skip_trackable'] = true
sign_in user, store: false
end
end
end
def user_time_zone(&block)
Time.use_zone(current_user.time_zone, &block)
end
# If this is a get request for HTML, just render the ember app.
def handle_html
render 'public/index.html' if request.method == 'GET' && request.headers['Accept'].match(/html/)
end
end
My sessions controller looks like this:
class SessionsController < Devise::SessionsController
def create
self.resource = warden.authenticate!(auth_options)
sign_in(resource_name, resource)
data = {
user_token: self.resource.authentication_token,
user_email: self.resource.email
}
render json: data, status: 201
end
def destroy
sign_out :user
render json: {}, status: :accepted
end
end
My serializers are these:
class UserSerializer < ActiveModel::Serializer
attributes :id, :password, :user_email, :email, :user_token, :passwordConfirmation
end
class UserSerializer < ActiveModel::Serializer
attributes :id, :email, :email_times, :last_export_time, :plan,
:plan_started, :plan_canceled, :plan_status, :trial_end,
:time_zone, :status, :created_at, :include_email_memory
end
My route is:
Rails.application.routes.draw do
# PLANS
post 'update_plan' => 'plans#update_plan', as: :update_plan
post 'update_card' => 'plans#update_card', as: :update_card
post 'cancel_plan' => 'plans#cancel_plan', as: :cancel_plan
# PASSWORDS
post 'start_password_reset' => 'users#start_password_reset'
put 'finish_password_reset' => 'users#finish_password_reset'
get 'password-reset' => 'application#index', as: :edit_user_password
# USERS
devise_for :users, controllers: { sessions: 'sessions' }, :skip => [:passwords]
resources :users, only: [:create, :update] do
get 'me' => 'users#me', on: :collection
end
# background processing admin
match "/delayed_job" => DelayedJobWeb, :anchor => false, via: [:get, :post]
# catch-all for ember app
get '*path' => 'application#index', :constraints => { :format => 'html' }
end
In the ember-cli app itself, my login controller is:
import Ember from "ember";
export default Ember.Controller.extend({
authenticator: 'simple-auth-authenticator:devise',
identification: null,
password: null,
error: null,
working: false,
actions: {
authenticate: function() {
var _this = this,
data = this.getProperties('identification', 'password');
this.setProperties({
working: true,
password: null,
error: null
});
this.get('session').authenticate('simple-auth-authenticator:devise', data).then(function() {
// authentication was successful
}, function(data) {
_this.set('working', false);
_this.set('error', data.error);
});
}
}
});
My application route is:
// ember-simple-auth
import Ember from "ember";
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
import Notify from 'ember-notify';
import ENV from 'front-end/config/environment';
export default Ember.Route.extend(ApplicationRouteMixin, {
beforeModel: function(transition) {
this._super(transition);
return this.setCurrentUser();
},
actions: {
sessionAuthenticationFailed: function(data) {
this.controllerFor('login').set('working', false);
this.controllerFor('login').set('loginErrorMessage', data.message);
},
sessionInvalidationSucceeded: function() {
this.transitionTo('index');
},
sessionAuthenticationSucceeded: function() {
var _this = this;
this.controllerFor('login').set('working', false);
this.setCurrentUser().then(function() {
if (_this.get('session.currentUser.mustSubscribe')) {
_this.transitionTo('plans');
} else {
_this.transitionTo('courses');
}
});
},
authorizationFailed: function() {
Notify.error("Could not be authenticated.. signing out.", {closeAfter: 5000});
this.get('session').invalidate();
}
},
setCurrentUser: function() {
var _this = this,
adapter = this.get('store').adapterFor('user');
if (this.get('session.isAuthenticated')) {
return new Ember.RSVP.Promise(function(resolve) {
adapter.ajax(ENV.APP.API_HOST + "/users/me", "GET", {}).then(
function(response){
_this.store.pushPayload(response);
var user = _this.store.find('user', response.user.id);
resolve(user);
},
function(response){
resolve(response);
}
);
}).then(function(user) {
_this.set('session.currentUser', user);
}, function() {
Notify.error("Could not be authenticated.. signing out.", {closeAfter: 5000});
_this.get('session').invalidate();
});
} else {
return new Ember.RSVP.Promise(function(resolve){ resolve(); });
}
}
});
Finally my login route is:
import Ember from "ember";
export default Ember.Route.extend({
activate: function() {
if (this.get('session').isAuthenticated) {
this.transitionTo('courses');
}
}
});
And Template is:
<form {{action 'register' on='submit'}} class='d-auth-form fade-in'>
{{#each errors}}
<div class="d-error">
{{this}}
</div>
{{/each}}
{{input placeholder='Email' type='email' value=email autocomplete='off' autocapitalize="none"}}
{{input placeholder='Password' type='password' value=password autocomplete='off'}}
<button type="submit" class='d-btn d-btn--success' {{bind-attr disabled=working}}>
{{#if working}}
Registering..
{{else}}
Sign up for DayJot for free
{{/if}}
</button>
<ul class='d-links'>
<li>{{#link-to 'login'}}Login to existing account{{/link-to}}</li>
</ul>
</form>
The important parts of environment.js are:
'simple-auth': {
crossOriginWhitelist: ['http://localhost:3000','http://localhost:4202','https://api.dayjot.com'],
authorizer: 'simple-auth-authorizer:devise',
authenticationRoute: 'index'
}
and
ENV['simple-auth-devise'] = {
serverTokenEndpoint: ENV.APP.API_HOST+'/users/sign_in',
identificationAttributeName: 'email'
}

Checkout the README - Ember Simple Auth Devise expects the token to be returned as token, you're using user_token however. Thus, the session will never actually be authenticated in Ember and the token won't be included in requests which leads to the 401 response.

Related

How to successfully logout using a Rails 6 API with devise and JWT

I've been stuck for days and searching but I cannot find a correct solution to logout from a devise session using JWT. I had a front made with react and everything works fine on login and searching, but when I logout the page if I don't make a refresh I can't login. I leave the code from devise session controller along side the application controller, route and my middeware build to use redux with my front (I'm working with React to). Thanks in advance and I you need something else, let me know.
Devise::SessionsController
# frozen_string_literal: true
class Api::SessionsController < Devise::SessionsController
respond_to :json, :html
# GET /resource/sign_in
# def new
# super
# end
# POST /resource/sign_in
# def create
# super
# end
# DELETE /resource/sign_out
# def destroy
# super
# end
# protected
private
def revoke_token(token)
# Decode JWT to get jti and exp values.
begin
secret = Rails.application.credentials.jwt_secret
jti = JWT.decode(token, secret, true, algorithm: 'HS256', verify_jti: true)[0]['jti']
exp = JWT.decode(token, secret, true, algorithm: 'HS256')[0]['exp']
user = User.find(JWT.decode(token, secret, true, algorithm: 'HS256')[0]['sub'])
sign_out user
# Add record to blacklist.
time_now = Time.zone.now.to_s.split(" UTC")[0]
sql_blacklist_jwt = "INSERT INTO jwt_blacklist (jti, exp, created_at, updated_at) VALUES ('#{ jti }', '#{ Time.at(exp) }', '#{time_now}', '#{time_now}');"
ActiveRecord::Base.connection.execute(sql_blacklist_jwt)
rescue JWT::ExpiredSignature, JWT::VerificationError, JWT::DecodeError
head :unauthorized
end
end
def respond_with(resource, _opts = {})
render json: resource
end
def respond_to_on_destroy
token = request.headers['Authorization'].split("Bearer ")[1]
revoke_token(token)
request.delete_header('Authorization')
render json: :ok
end
end
ApplicationController
class ApplicationController < ActionController::API
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :authenticate_user
protected
def configure_permitted_parameters
added_attrs = %i[username email password password_confirmation remember_me]
devise_parameter_sanitizer.permit(:sign_up, keys: added_attrs)
devise_parameter_sanitizer.permit(:account_update, keys: added_attrs)
end
private
def authenticate_user
if request.headers['Authorization'].present?
token = request.headers['Authorization'].split("Bearer ")[1]
begin
jwt_payload = JWT.decode(token, Rails.application.credentials.jwt_secret).first
#current_user_id = jwt_payload['sub']
rescue JWT::ExpiredSignature, JWT::VerificationError, JWT::DecodeError
head :unauthorized
end
end
end
def authenticate_user!(options = {})
head :unauthorized unless signed_in?
end
def current_user
#current_user ||= super || User.find(#current_user_id)
end
def signed_in?
#current_user_id.present?
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users, skip: %i[registrations sessions passwords]
namespace :api do
devise_scope :user do
post 'signup', to: 'registrations#create'
post 'login', to: 'sessions#create'
delete 'logout', to: 'sessions#destroy'
get 'login', to: 'sessions#create'
end
resources :notes
resources :searches
get 'get_places', to: 'searches#get_places'
end
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
middleware.js
import * as constants from './constants';
import axios from 'axios';
import { logoutUser } from './actions/authActionCreators'
export const apiMiddleware = ({ dispatch, getState }) => next => action => {
if (action.type !== constants.API) return next(action);
dispatch({ type: constants.TOGGLE_LOADER });
const BASE_URL = 'http://localhost:3001';
const AUTH_TOKEN = getState().user.token;
if (AUTH_TOKEN)
axios.defaults.headers.common['Authorization'] = `Bearer ${AUTH_TOKEN}`;
const { url, method, success, data, postProcessSuccess, postProcessError } = action.payload;
console.log('AUTH_TOKEN '+AUTH_TOKEN);
console.log('url '+url);
axios({
method,
url: BASE_URL + url,
data: data ? data : null,
headers: {
'Content-Type': 'application/json', 'Accept': '*/*'
}
}).then((response) => {
dispatch({ type: constants.TOGGLE_LOADER });
if (success) dispatch(success(response));
if (postProcessSuccess) postProcessSuccess(response);
}).catch(error => {
dispatch({ type: constants.TOGGLE_LOADER });
if (typeof(error.response) === "undefined") {
console.warn(error);
postProcessError('An error has ocurred');
} else {
if (error.response && error.response.status === 403)
dispatch(logoutUser());
if (error.response.data.message) {
if (postProcessError) postProcessError(error.reponse.data.message);
}
}
})
};

Devise 422 Unprocessable Entity Error when calling the controller from a React front end

EDIT:
bwalshy - helped me solve the issue - I was rendering a static error message, instead of the actual server response. Once I rendered the server response it was obvious that I was trying to create the test account with the password as "123" which is too weak for the gem's standards. Coding is a humbling experience.
I've seen this question posted, but without any solutions that work for me. I have a React front end that is mounted on top of a Rails app. When I try to call my registrations controller I am getting the following error:
Completed 422 Unprocessable Entity in 103ms
I saw this this could be token related, and logging the token shows what appears to be a valid one: v4Ml1tlzkgBKWBcYGP9SGO+YVL7fxcxs3D8MhC3Z/3ZcZOa5rYGSHUABG+vL+yJfoVfO1Ks6RLI60sQDk7Hh8A==
which is being passed in the headers
The server response is:
{"error":"signup error"}
Here is the React component
import React from "react";
class SignUp extends React.Component {
constructor(props) {
super(props);
this.state = {
signupUnsuccessful: false,
email: '',
passwordOne: '',
passwordTwo: '',
error: null,
};
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
};
onChange = event => {
this.setState({ [event.target.name]: event.target.value });
}
//this is erroring and I think it's because I'm not telling devise to deliver json
onSubmit(event) {
event.preventDefault();
const url = "http://localhost:3000/users";
const { email } = this.state;
const password = this.state.passwordOne
const userInfo = {
user: {
email,
password
}
}
const token = document.querySelector('meta[name="csrf-token"]').content;
console.log(token)
fetch(url, {
method: "POST",
headers: {
"X-CSRF-Token": token,
"Content-Type": "application/json"
},
body: JSON.stringify(userInfo)
})
.then(response => {
if (response.ok) {
return response.json();
}
throw new Error("Network response was not ok.");
})
.catch(error => console.log(error.message));
}
render() {
const {
email,
passwordOne,
passwordTwo,
error,
} = this.state;
const isInvalid =
passwordOne !== passwordTwo ||
passwordOne === '' ||
email === '';
return (
<div>
<h2>Signup</h2>
<form onSubmit={this.onSubmit}>
<input name="email"
value={email}
onChange={this.onChange}
type="text"
placeholder="Email Address"/>
<input name="passwordOne"
value={passwordOne}
onChange={this.onChange}
type="password"
placeholder="Password" />
<input name="passwordTwo"
value={passwordTwo}
onChange={this.onChange}
type="password"
placeholder="Confirm Password"/>
<button disabled={isInvalid} type="submit">
Sign Up
</button>
</form>
<button onClick={() => this.props.changePage("login")}>Login!</button>
</div>
);
};
};
export default SignUp;
and the controller:
class RegistrationsController < Devise::RegistrationsController
def create
#user = User.new(user_params)
if #user.save
render json: #user
else
warden.custom_failure!
render json: { error: 'signup error' }, status: :unprocessable_entity
end
end
def update
#user = User.find_by_email(user_params[:email])
if #user.update_attributes(user_params)
render json: #user
else
warden.custom_failure!
render :json=> #user.errors, :status=>422
end
end
def destroy
#user = User.find_by_email(user_params[:email])
if #user.destroy
render :json=> { success: 'user was successfully deleted' }, :status=>201
else
render :json=> { error: 'user could not be deleted' }, :status=>422
end
end
private
def user_params
params.require(:user).permit(:email, :password, :password_confirmation)
end
end
And my routes
Rails.application.routes.draw do
devise_for :users, controllers: { registrations: 'registrations', sessions: 'sessions' }
root 'homepage#index'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end

I'm able to hit my API for every request, but my POST request to create a new user

I'm working on authentication (using Knock) for my rails API and I can't hit my POST route to create a new user using a react client. I am able to hit this route in Postman and it successfully creates a new user. I can see I hit the route in postman from the terminal, but when I try it on my client I don't get any response in the terminal.
Things I've tried
Ensured client and server are running on different ports
I have my client on localhost:3000 and my server on localhost:3001
Made sure I have CORS set up
I am using the gem 'rack-cors' and have a cors initializer setup according to the docs for Rails 5. (I can include this file if you believe its needed)
I also have enabled CORS on my chrome web browser
Try another POST route
I am able to successfully log in on the client side (using a user's email/password I created in postman) and generate a JWT token using a POST route to my api
Made sure I am getting to the action creator from my container
I put a debugger in the action creator to make sure I am hitting it when I submit the form. I hit it and have all the relevant info I need (first name, last name, email, and password) to complete the request.
I think the problem lies somewhere in my userSignUpFetch action creator or in my user Controller.
Action creator that handle process of sending new user object to rails
export const userSignUpFetch = (user) => {
const newUser = user
return dispatch => {
return fetch(`http://localhost:3001/api/users`, {
method: "POST",
headers: {
Accept:"application/json",
"Content-Type":"application/json"
},
body: JSON.stringify({user: user})
})
.then(response => response.json())
.then(jresp => {
dispatch(loginUserFetch({
first_name: newUser.first_name,
last_name: newUser.last_name,
email: newUser.email,
password: newUser.password})
);
})
.catch((errors) => {
dispatch(authFailure(errors))
})
};
}
My current Routes for auth in Rails
api_users GET /api/users(.:format) api/users#index
POST /api/users(.:format) api/users#create
api_user GET /api/users/:id(.:format) api/users#show
PATCH /api/users/:id(.:format) api/users#update
PUT /api/users/:id(.:format) api/users#update
DELETE /api/users/:id(.:format) api/users#destroy
api_user_token POST /api/user_token(.:format) api/user_token#create
api_find_user POST /api/find_user(.:format) api/users#find
My Rails User Controller
class Api::UsersController < ApplicationController
before_action :set_user, only: [:show, :update]
def index
#users = User.all
render json: #users
end
def create
#user = User.create(user_params)
if #user.valid? && #user.save
render json: #user
else
render json: #user.errors, status: 400
end
end
def show
render json: #user
end
def update
if #user.update(user_params)
render json: #user
else
render json: #user.errors, status: 400
end
end
def destroy
#user.destroy
end
def find
#user = User.find_by(email: params[:user][:email])
if #user
render json: #user
else
#errors = #user.errors.full_messages
render json: #errors
end
end
private
def set_user
#user = User.find_by(id: params[:id])
end
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password)
end
end
I'm expecting to see something like
Started POST "/api/users" for ::1 at 2019-05-28 17:56:41 -0500
in my terminal, but when I hit that action creator I don't get any response from my terminal running the server. I'm wondering if anybody has any suggestions on what to look for. Thanks.
Update
I believe it is something with my dispatch.
This below works, up until jresp.loginUserFetch
export const userSignUpFetch = user => {
//Fetch request info
const newUser = JSON.stringify({user: user})
const userAuth = JSON.stringify({user})
const options = {
method: 'post',
headers: {
'Content-type': 'application/json'
},
body: newUser
}
const fetchURL = `${API_URL}/users`
return fetch(fetchURL, options)
.then(resp => resp.json())
.then(jresp => jresp.loginUserFetch({
first_name: userAuth.first_name,
last_name: userAuth.last_name,
email: userAuth.email,
password: userAuth.password}))
.catch( err => {
console.log('Request Failed:', err)
})
}
Update 2- Got it To work
I imported fetch from cross fetch as well as cleaned it up a little, but it works as attended now which makes me believe I needed cross-fetch in the file.
export const userSignUpFetch = user => {
//Fetch request info
const newUser = JSON.stringify({user: user})
const options = {
method: 'post',
headers: {
'Content-type': 'application/json'
},
body: newUser
}
const fetchURL = `${API_URL}/users`
return dispatch => {
return fetch(fetchURL, options)
.then(resp => resp.json())
.then(jresp => { dispatch(loginUserFetch({
first_name: user.first_name,
last_name: user.last_name,
email: user.email,
password: user.password})
)
})
.catch( err => {
console.log('Request Failed:', err)
})
}
}

Rails 5 API + Vue.js frontend: form to invite users with devise_invitable

I am writing an invitation form with Vue.js. The invites should POST to a Rails 5 API. I am using devise_invitable for the invitation logic and email dispatch. However, I am having a problem with intercepting the create method, as I will be sending the invitation to multiple users so I want to perform a User.invite! for each user in the params.
My invite.vue file, which contains the form to post:
<template>
<b-row>
<b-col>
Invite {{form.length}} members
<b-form #submit.prevent="submitStaffInvite" id='staffInvite'>
<div v-for="(row, idx) in form">
<b-row>
<b-col cols="3">
<b-form-group id="firstNameGroup" label="First name">
<b-form-input id="firstNameInput" name="user[first_name][]" type="text" v-model="row.firstName" autofocus></b-form-input>
</b-form-group>
</b-col>
<b-col cols="3">
<b-form-group id="lastNameGroup" label="Last name">
<b-form-input id="lastNameInput" name="user[last_name][]" type="text" v-model="row.lastName"></b-form-input>
</b-form-group>
</b-col>
<b-col cols="3">
<b-form-group id="emailGroup" label="Staff email">
<b-form-input id="emailInput" name="user[email][]" type="text" v-model="row.email"></b-form-input>
</b-form-group>
</b-col>
<b-col cols="3">
<b-button #click='removeRow(idx)'>Remove invitation</b-button>
</b-col>
</b-row>
</div>
<br />
<b-button-group>
<b-button #click.prevent='addRow'>Add invitation</b-button>
</b-button-group>
<br />
<b-button-group>
<b-button type="submit" variant="primary">Submit</b-button>
</b-button-group>
</b-form>
</b-col>
</b-row>
</template>
<script>
export default {
data: () => {
return {
form: [
{
email: '',
firstName: '',
lastName: ''
}
]
}
},
methods: {
addRow: function () {
this.form.push({
email: '',
firstName: '',
lastName: ''
})
},
removeRow: function (idx) {
this.form.splice(idx, 1)
},
submitStaffInvite: function () {
this.$axios.post('http://localhost:3001/auth/invitation', this.form)
.then((res) => {
if (res.status === 200) {
this.$notify({
text: res.data.message,
group: 'alerts',
type: 'success'
})
}
})
.catch(function (error) {
error.response.data.errors.forEach((err) => {
this.$notify({
text: err,
group: 'alerts',
type: 'warning'
})
})
})
}
}
}
</script>
my users/invitations_controller.rb
class Users::InvitationsController < Devise::InvitationsController
before_action :configure_permitted_parameters
def create
# TODO
# Send email to each user in the form.
end
def edit
sign_out send("current_#{resource_name}") if send("#{resource_name}_signed_in?")
set_minimum_password_length
resource.invitation_token = params[:invitation_token]
redirect_to "http://localhost:3001/auth/invitation/accept?invitation_token=#{params[:invitation_token]}"
end
def update
super do |resource|
if resource.errors.empty?
render json: { status: "Invitation Accepted!" }, status: 200
else
render json: resource.errors, status: 401
end
end
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:accept_invitation, keys: [:first_name, :last_name])
end
end
my routes.rb
Rails.application.routes.draw do
# Using devise_token_auth for the API
mount_devise_token_auth_for 'User', at: 'auth',
defaults: { format: :json },
controllers: {
invitations: 'users/invitations'
}
...
end
I can see that the following routes exist:
PUT /auth/invitation(.:format) devise/invitations#update
POST /auth/invitation(.:format) devise/invitations#create
The problem I can see here though is that I'd expect the routes to look like this:
PUT /auth/invitation(.:format) users/invitations#update
POST /auth/invitation(.:format) users/invitations#create
This behavior might be caused by the devise_token_auth gem, which I'm not sure how to correct.
Again, when I submit this form I'd expect to be able to intercept the create method, so to then call User.invite! on all the users that are listed in the form. I've tried adding a byebug or binding.pry inside the create method but the code doesn't appear to execute, which means it's being bypassed.
The error message I see is:
Processing by Devise::InvitationsController#create as HTML
Parameters: {"_json"=>[{"email"=>"asd#ad.com", "firstName"=>"Test", "lastName"=>"Last"}], "invitation"=>{"_json"=>[{"email"=>"asd#ad.com", "firstName"=>"Test", "lastName"=>"Last"}]}}
Completed 500 Internal Server Error in 0ms (ActiveRecord: 0.0ms)
ArgumentError (wrong number of arguments (given 1, expected 0)):
Am I not supposed to pass the form data to the devise_invitable's create method? Any help is much appreciated.
Thanks in advance!
Try changing your routes to
mount_devise_token_auth_for 'User', at: 'auth', skip: [:invitations]
devise_for :users, path: "auth", only: [:invitations],
controllers: { invitations: 'users/invitations' }
For more information you can refer to this article

Ember Simple Auth (Devise) after update, authenticate break

I did an update from 0.6.4 to 0.7.2 with Ember Simple Auth (not Ember CLI version) for devise, now my authentification doesn't work at all :(, do you have an idea ? thank you very much for your help :)
PS : apparently, ApplicationController (application_controller.rb) don't continue after authenticate_with_http_token do |token, options| and authenticate_with_http_token is empty (tested with puts)
login_controller.js
App.LoginController = Ember.Controller.extend(SimpleAuth.LoginControllerMixin, {
authenticator: 'simple-auth-authenticator:devise'
//authenticator: 'authenticator:custom'
});
application.js.coffee
Ember.Application.initializer
name: "authentication"
after: "simple-auth"
initialize: (container, application) ->
applicationRoute = container.lookup("route:application")
session = container.lookup("simple-auth-session:main")
# handle the session events
session.on "sessionAuthenticationSucceeded", ->
applicationRoute.transitionTo "Myspace"
return
return
window.ENV = window.ENV || {}
window.ENV["simple-auth"] = { store: 'simple-auth-session-store:local-storage', authorizer: "simple-auth-authorizer:devise" };
window.ENV['simple-auth-devise'] = {
crossOriginWhitelist: ['*'],
serverTokenEndpoint: 'users/sign_in',
};
login.hbs
<br />
<div class="row">
<div class="large-12 columns">
<form {{action 'authenticate' on='submit'}}>
<label for="identification">Login</label>
{{input id='identification' placeholder='Enter Login' value=identification}}
<label for="password">Password</label>
{{input id='password' placeholder='Enter Password' type='password' value=password}}
<button type="submit">Login</button>
</form>
</div>
</div>
login_route.js.coffee
App.LoginRoute = Ember.Route.extend(
#model: (params) ->
#return #store.find('user', #get('session.user_id'))
setupController: (controller, model) ->
#controller.set "content", model
controller.set "errorMessage", null
return
actions:
sessionAuthenticationFailed: (responseBody) ->
message = responseBody.error
#controller.set "errorMessage", message
console.log "errorMessage : " + message
return )
myspace_route.js.coffee
App.MyspaceRoute = Ember.Route.extend(SimpleAuth.AuthenticatedRouteMixin, ....)
session_controller.rb
class SessionsController < Devise::SessionsController
def create
respond_to do |format|
format.html { super }
format.json do
self.resource = warden.authenticate!(auth_options)
sign_in(resource_name, resource)
data = {
user_token: self.resource.authentication_token,
user_email: self.resource.email
}
render json: data, status: 201
end
end
end
end
application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :null_session,
if: Proc.new { |c| c.request.format =~ %r{application/json} }
before_filter :skip_trackable, :authenticate_user_from_token!
private
def skip_trackable
request.env['warden'].request.env['devise.skip_trackable'] = '1'
end
def authenticate_user_from_token!
puts "authentification"
puts authenticate_with_http_token
authenticate_with_http_token do |token, options|
user_email = options[:user_email].presence
user = user_email && User.find_by_email(user_email)
puts "user.authentication_token"
puts user.authentication_token
puts token
puts "token"
if user && Devise.secure_compare(user.authentication_token, token)
sign_in user, store: false
end
end
end
end
You're setting up the window.ENV object in the initializer that runs after the 'simple-auth' initializer so Ember Simple Auth cannot actually see the values that you set when its initializer runs. Make sure the values are set before the 'simple-auth' initializer runs.
Also you should switch to Ember CLI of course ;)
After a run of debugger, it goes to :
ember-simple-auth.js
authenticate: function() {
var args = Array.prototype.slice.call(arguments);
var authenticator = args.shift();
Ember.assert('Session#authenticate requires the authenticator factory to be specified, was ' + authenticator, !Ember.isEmpty(authenticator));
var _this = this;
var theAuthenticator = this.container.lookup(authenticator);
Ember.assert('No authenticator for factory "' + authenticator + '" could be found', !Ember.isNone(theAuthenticator));
return new Ember.RSVP.Promise(function(resolve, reject) {
theAuthenticator.authenticate.apply(theAuthenticator, args).then(function(content) {
_this.setup(authenticator, content, true);
resolve(); // <- it goes to here
}, function(error) {
_this.clear();
_this.trigger('sessionAuthenticationFailed', error);
reject(error);
});
});
},
The json response with token seems to be ok, and authenticator config seems to be ok also ...
Also i have a "Rejected" in this promise
ember-simple-auth.js
restore: function() {
var _this = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
var restoredContent = _this.store.restore();
var authenticator = restoredContent.authenticator;
if (!!authenticator) {
delete restoredContent.authenticator;
_this.container.lookup(authenticator).restore(restoredContent).then(function(content) {
_this.setup(authenticator, content);
resolve();
}, function() {
_this.store.clear();
reject();
});
} else {
_this.store.clear();
reject();
}
});
},
Trace of the Rejected promise :
VM7522:164 Ember Inspector (Promise Trace):
at new Promise (http://localhost:3000/assets/ember.js?body=1:10174:9)
at __exports__.default.Ember.ObjectProxy.extend.restore (http://localhost:3000/assets/ember-simple-auth.js?body=1:1116:16)
at __exports__.default (http://localhost:3000/assets/ember-simple-auth.js?body=1:1337:15)
at __exports__.default.initialize (http://localhost:3000/assets/ember-simple-auth.js?body=1:447:9)
at http://localhost:3000/assets/ember.js?body=1:43164:11
at visit (http://localhost:3000/assets/ember.js?body=1:43556:7)
at DAG.topsort (http://localhost:3000/assets/ember.js?body=1:43610:11)
at Namespace.extend.runInitializers (http://localhost:3000/assets/ember.js?body=1:43161:15)
at Namespace.extend._initialize (http://localhost:3000/assets/ember.js?body=1:43046:14)
Edit 1: and also this one :
ember-simple-auth-devise.js
restore: function(properties) {
var _this = this;
var propertiesObject = Ember.Object.create(properties);
return new Ember.RSVP.Promise(function(resolve, reject) {
if (!Ember.isEmpty(propertiesObject.get(_this.tokenAttributeName)) && !Ember.isEmpty(propertiesObject.get(_this.identificationAttributeName))) {
resolve(properties);
} else {
reject();
}
});
},
with trace :
Ember Inspector (Promise Trace):
at new Promise (http://localhost:3000/assets/ember.js?body=1:10174:9)
at __exports__.default.Base.extend.restore (http://localhost:3000/assets/ember-simple-auth-devise.js?body=1:156:16)
at apply (http://localhost:3000/assets/ember.js?body=1:7993:27)
at superWrapper [as restore] (http://localhost:3000/assets/ember.js?body=1:7571:15)
at http://localhost:3000/assets/ember-simple-auth.js?body=1:1121:51
at invokeResolver (http://localhost:3000/assets/ember.js?body=1:10192:9)
at new Promise (http://localhost:3000/assets/ember.js?body=1:10178:9)
at __exports__.default.Ember.ObjectProxy.extend.restore (http://localhost:3000/assets/ember-simple-auth.js?body=1:1116:16)
at __exports__.default (http://localhost:3000/assets/ember-simple-auth.js?body=1:1337:15)
With the help of marcoow, just modified https://github.com/simplabs/ember-simple-auth/tree/master/packages/ember-simple-auth-devise#server-side-setup SessionsController like this :
class SessionsController < Devise::SessionsController
def create
respond_to do |format|
format.html { super }
format.json do
self.resource = warden.authenticate!(auth_options)
sign_in(resource_name, resource)
#data = {
user_token: self.resource.authentication_token,
user_email: self.resource.email
}
render json: #data.to_json, status: 201
end
end
end
end
Now it's working
Edit : to_json explanation : http://apidock.com/rails/ActiveRecord/Serialization/to_json

Resources