4
votes

I am using Rails-api to make a test authentication app which uses Devise_token_auth gem. The User.rb model looks like

class User < ActiveRecord::Base
    before_save :set_auth_token

  # Include default devise modules.
  devise :database_authenticatable, :registerable,
          :recoverable, :rememberable, :trackable, :validatable,
          :confirmable, :omniauthable
  include DeviseTokenAuth::Concerns::User

  private
  def set_auth_token
    if self.authentication_token.blank?
      self.authentication_token = generate_authentication_token
    end
  end

  def generate_authentication_token
    loop do
      token = Devise.friendly_token
      break token unless User.where(authentication_token: token).first
    end
  end

end

routes.rb contains

mount_devise_token_auth_for 'User', at: 'auth'

Also I am using the default SessionsController and RegistrationsController defined by DeviseTokenAuth gem

My frontend is made in Ember-cli where I have made a login form which uses Ember-simple-auth-devise, Devise authorizer to call the /sign_in url of rails api. The Ember simple auth wraps the parameters like

{"user"=>{"password"=>"[FILTERED]", "email"=>"[email protected]"}}

while the rails DeviseTokenAuth expects the request parameters like

{"password"=>"[FILTERED]", "email"=>"[email protected]"}

The error produced is

Processing by DeviseTokenAuth::RegistrationsController#create as JSON
   Parameters: {"user"=>{"password"=>"[FILTERED]", "email"=>"[email protected]"}} 
 Unpermitted parameter: user

The problem can be solved if either Rails DeviseTokenAuth gem accepts parameters wrapped in "user" OR Ember-simple-auth sends the parameters unwrapped, but unfortunately documentation for both of them doesn't clearly mention the way to implement the same. I tried changing the resourceName for Ember-simple-auth to null but it didn't work

    ENV['simple-auth-devise'] = {
     resourceName: null,
    serverTokenEndpoint: 'http://localhost:3000/auth/sign_in'
  };

Is there a way to send unwrapped parameters in Ember-simple-auth-devise? Or is it possible to permit the parameters contained in "user" for all the controllers generated using DeviseTokenAuth gem?

Versions used are:

devise_token_auth (0.1.36)
  devise (~> 3.5.2)
  rails (~> 4.2)
"ember-simple-auth": "0.8.0"
1
I think you are going to have to use ember-simple-auth without the devise plugin and roll it yourself. It looks like the devise plugin in specific to vanilla devise.penner

1 Answers

1
votes

A solution is to extend ember-simple-auth/addon/authenticators/devise.js as done here.


In app/authenticators/devise.js, replace:

import Devise from 'ember-simple-auth/authenticators/devise';

export default Devise.extend({});

by:

import Ember from 'ember';
import Devise from 'ember-simple-auth/authenticators/devise';

const { RSVP: { Promise }, isEmpty, getProperties, run, get } = Ember;

export default Devise.extend({
  loginEndpoint: '/auth/sign_in',
  logoutEndpoint: '/auth/sign_out',

  authenticate(identification, password) {
    return new Promise((resolve, reject) => {
      let { identificationAttributeName } = getProperties(this, 'identificationAttributeName');
      let data = { password };
      data[identificationAttributeName] = identification;

      let requestOptions = { url: get(this, 'loginEndpoint') };

      this.makeRequest(data, requestOptions).then((response) => {
        if (response.ok) {
          response.json().then((json) => {
            let data = {
              account: json,
              accessToken: response.headers.get('access-token'),
              expiry: response.headers.get('expiry'),
              tokenType: response.headers.get('token-type'),
              uid: response.headers.get('uid'),
              client: response.headers.get('client')
            };

            if (this._validate(data)) {
              run(null, resolve, data);
            } else {
              run(null, reject, 'Check that server response header includes data token and valid.');
            }
          });
        } else {
          response.json().then((json) => run(null, reject, json));
        }
      }).catch((error) => run(null, reject, error));
    });
  },

  invalidate(data) {
    return new Promise((resolve, reject) => {
      let headers = {
        'access-token': data.accessToken,
        'expiry': data.expiry,
        'token-type': data.tokenType,
        'uid': data.uid,
        'client': data.client
      };

      let requestOptions = {
        url: get(this, 'logoutEndpoint'),
        method: 'DELETE',
        headers
      };

      this.makeRequest({}, requestOptions).then((response) => {
        response.json().then((json) => {
          if (response.ok) {
            run(null, resolve, json);
          } else {
            run(null, reject, json);
          }
        });
      }).catch((error) => run(null, reject, error));
    });
  },

  _validate(data) {
    let now = (new Date()).getTime();

    return !isEmpty(data.accessToken) && !isEmpty(data.expiry) && (data.expiry * 1000 > now) &&
      !isEmpty(data.tokenType) && !isEmpty(data.uid) && !isEmpty(data.client);
  }
});