Devise: Rails 5 API doesn't work with devise, throws undefined local variable or method `flash'

Created on 26 Aug 2016  路  13Comments  路  Source: heartcombo/devise

The new rails 5 API doesn't work with devise (v4.2.0). To reproduce:

  1. rails new my_api --api
  2. Follow steps of installing devise (adding gem, running rake commands).
  3. Try accessing sign_in with a valid user with something like that:
    curl -X POST -F "user[email][email protected]" -F "user[password]=example" localhost:3000/users/sign_in

It throws an error like this:

NameError (undefined local variable or method `flash' for #<Devise::SessionsController:0x007fb8b8814250>)

Due to this line of code.

Something as simple as this commit, makes it work: https://github.com/johndel/devise/commit/c870213ee57b71ac580784979b46c7aab8863a2a

Update:
Seems that if you call it with .json at the end it works (like this curl -X POST -F "user[email][email protected]" -F "user[password]=example" localhost:3000/users/sign_in.json).

Update 2:
Seems like adding the .json at the end of the sign_in.json url worked, but in other endpoints didn't, like /password/edit.json.

Most helpful comment

Found the solution. For a rails --api application, set "config.navigational_formats = []". Then devise will never attempt to use flash and never throw a 500 error. I suspect this may be a good candidate for adding to the documentation.

All 13 comments

I agree with John that this is an issue. The PasswordsController#edit does not respect navigational formats. (I did not check if this concerns other controllers, too.)

This could be fixed by using set_flash_message! instead of set_flash_message here.

I will chime in that I suspect this is more of an issue that it appears at first blush.

I'm attempting to use devise with a rails --api (5.0.0.1) application, and while it does the correct thing if an end-user submits a request with the correct type, it throws a 500 if a user tries to do a simple html format GET from one of my endpoints without providing authorization, rather than returning a proper 401.

I could limit routes to specific formats (btw, anyone yet figure out how to use route constraints to limit routes to jsonapi format?), which will then throw an unknown route error, but I can't control what formats users may throw requests at my server in; I'd like to respond appropriately. While attempting to do the right thing based on the requested format makes sense, having to override the entirety of devise error handling in order avoid internal server errors if an end-user fails to set the correct type seems broken.

Perhaps addition config flag to never use flash would be appropriate?

Found the solution. For a rails --api application, set "config.navigational_formats = []". Then devise will never attempt to use flash and never throw a 500 error. I suspect this may be a good candidate for adding to the documentation.

This is no definite solution, since not all Devise actions honor this setting.
One such action is PasswordsController#edit. Not sure if there are more.
(See my suggestion for a fix earlier.)

As far as my understanding goes, you shouldn't be using Devise with an API only rails app.
Devise depends on session and cookies, and you should not be using those for an API
An API is not the use case for Devise so these are really not bugs with the gem

At risk of stating the obvious, seems Rails 5 API needs a Devise caliber authentication gem, does anyone know of a solution or if anyone is working on one?

Trying to figure out how to best support both a traditional Rails web application with session and cookies (Devise like) and mobile apps (Token auth like) ?

Don't really want to re-write my web application as a single page web app / Angular or such.

I've gotten devise working fairly well for an API app; I added JWT tokens as a warden strategy, so a user hits a devise login endpoint, receives a JWT, and subsequently uses that JWT to authorize access to the actual API endpoints. Still need to add token blacklisting (possibly a small memcached store) for logouts and token refreshing, but the rough skeleton is working, and behaving fairly well.

If the PasswordsController gets fixed to respect navigational formats, I don't necessarily think a new framework is required. Creating some help documentation with pointers on configuring devise for an API-only app would probably help.

I share @gssbzn 's point of view. However, I have been struggling to find the most concise Authentication implementation for a Rails 5 API.

Some implementations use http_secure_token (or something like that), devise_token_auth, Warden implementations, etc. etc.

Would appreciate any tips on what is the most straightforward way to implement Authentication for Rails 5 API.

@BigChief45 did you checkout devise_token_auth?

Using config.navigational_formats = [] throw an error

There are parts of devise_token_auth that still require configuring an API to have traditional HTML-formatted devise pages. Getting the two to play nicely together has been extremely difficult as I am getting the same problems as @BrunoQuaresma even with all the attempted monkey patches and suggested fixes.

In routes.rb add json format to devise_for: devise_for :users, defaults: { format: :json }

im agree with @Ikariusrb but use of defaults: { format: :json } also solve my problem in this way

` devise_for :users

namespace :eatery_book do
namespace :api , defaults: { format: :json } do
namespace :v1 do

  devise_scope :user do
  post "sign_up", to: "registrations#create"
  post "sign_in" , to: "sessions#create"
  delete "log_out", to: "sessions#destroy"

  end`
Was this page helpful?
0 / 5 - 0 ratings