switched to ssl and devise tokens are invalid - ruby-on-rails-3

We just switched our rails 3 app over to SSL, and later noticed that password recovery tokens aren't working in production any longer. It says "invalid token" when a user tries to reset their password using the emailed link.
I'm using rails 3.0.0, devise 1.3.4, and our user model has:
devise :database_authenticatable, :invitable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
I'm not using anything like ssl_requirement, because we just did ssl universally across the app. I expired old tokens to make sure it wasn't somehow not expiring old tokens or something. I'm baffled.

This was a problem with our nginx config, and entirely unrelated to Devise. But in case anyone else ever finds themselves in a similar position, here's what went down. We set up nginx to redirect the plain http urls to https.. Specifically we had a double rewrite when someone went from domain.com to www.domain.com to https://www.domain.com, and the reset_code was getting added to the end a second time so that reset_code was coming through to the app as ?reset_code=12345?reset_code=12345.
So we changed our nginx config so:
# rewrite ^ https://$server_name$request_uri permanent;
rewrite ^(.*) https://$host$1 permanent;
and then just an optimization
rewrite ^(.*)$ https://www.domain.com$1 permanent;
and all better now.

The answer provided above is correct. But it is a temporary solution and it works only for devise. However the problem arises when you manually send a confirmation token via email.
You can fix this permanently.Go to environment/production.
and change this line
config.action_mailer.default_url_options = { :host => 'domainname', :protocol => "http"}
to
config.action_mailer.default_url_options = { :host => 'domainname', :protocol => "https"}

Related

Secure session cookies for rails application

I have the following configuration in my session_store.rb
Fuel::Application.config.session_store :cookie_store,
:key => "_secure_session",
:secure => !(Rails.env.development? || Rails.env.test?),
:domain => :all
In application_controller.rb
def default_url_options
return { :only_path => false, :port => 443, :protocol => 'https' }
end
I am using devise and my rails3 server is running behind HAProxy. HAProxy terminates the HTTPS traffic and passes HTTP requests to Rails. My problem is when i turn on :secure => true in session_store.rb, the user is redirected back to the sign in page with the message "Unauthorized". I have tried debugging it a lot, not sure how to get it working.
Its a situation where HAProxy is the reverse proxy terminating all the secure traffic and passing non-secure traffic to rails. When rails sets the cookie to secure, somehow it itself is not able to access it.
For your normal session cookie, your doing this correctly. You should see the '_secure_session' cookie properly set as secure in your browser. For the Devise "remember me" cookie you'll need to set that in the devise config. In config/initializers/devise.rb you'll find a line somewhere around line 133 that looks like
# Options to be passed to the created cookie. For instance, you can set
# :secure => true in order to force SSL only cookies.
# config.cookie_options = {}
I changed that to:
config.rememberable_options = {:secure => Rails.env.production?}
If Set-cookie is not being sent to the browser on initial authentication, then it sounds like a devise problem.
If Set-cookie is going to the browser, but not being sent back on the next https:// request, then it's probably a mismatch on :secure => setting.
If the cookie is sent by the browser, but not passed along by HAProxy, then it's a HAProxy configuration problem.
If the cookie is in the ruby environment, and being ignored due to policy, then it's a problem somewhere in Ruby code - at a guess, around secure/not-secure cookie-matching.

Subdomain constraint (Rails 3) makes local server (thin) SO SLOW

I recently added a subdomain constraint to my Rails routes file
constraints(:subdomain => 'new') do
devise_for :customers do
get "/customers/sign_up" => "registrations#new"
post "/customers" => "registrations#create"
put "/customers/:id" => "registrations#update"
end
match '/' => 'roxy#index'
namespace :roxy, :path => '/' do
resources :customers
resources :surveys
end
end
In order to test the subdomain routing constraint locally, I added this line to my hosts file.
127.0.0.1 new.localhost.local
Now, I test my app in my browser at the URL new.localhost.local:3000. It takes about 10 - 15 seconds to load every page, which is unreasonably slow. If I remove the subdomain constraint and just go to 127.0.0.1:3000, everything is zippy and fast again.
What am I doing wrong? I'm new to Rails, so please tell me if there is a better way to do subdomain routing in rails, or if there is a setting I need to configure.
Figured it out. It's nothing to do with Rails or subdomains or thin. Turns out, unlike other unixy-things, OS X reserves the .local TLD for mDNS functionality. For every page, the DNS resolution was timing out before loading my app. So I just changed my /etc/hosts file to
127.0.0.1 new.localhost.dev
and everything's working great now.
Read more: http://www.justincarmony.com/blog/2011/07/27/mac-os-x-lion-etc-hosts-bugs-and-dns-resolution/

how should I go about making all Devise paths use https?

Related: Rails 3 SSL routing redirects from https to http (unfortunately didn't work).
Duplicate, but the answer didn't work for me: setting up ssl on devise
I have a web app that's been working fine for a while now but I need to add SSL to the login/edit acct paths. I'm using Devise for authentication. I found an entry in the devise wiki that made the process seem pretty simple, but damn if I can get it to work. The simple part was this:
#in config/environments/production.rb
config.to_prepare { Devise::SessionsController.force_ssl }
config.to_prepare { Devise::RegistrationsController.force_ssl }
And then there's about 25 lines of code in this gist: https://gist.github.com/1040964
I got that to work well enough, but when ever I sign out I get a 301 from the sessions DELETE action that sends me to a GET.
Started DELETE "/users/sign_out" for 98.246.164.160 at 2012-03-02 01:45:42 +0000
[02 Mar 01:45 10886 INFO] Processing by Devise::SessionsController#destroy as HTML
[02 Mar 01:45 10886 INFO] Parameters: {"authenticity_token"=>"fI4VZ4V0Go2Civo3sJz8Dv5/Wtaa90ynaYr+xxx="}
[02 Mar 01:45 10886 DEBUG] Parameters: {"_method"=>"delete", "authenticity_token"=>"fI4VZ4V0Go2Civo3sJz8Dv5/Wtaa90ynaYr+xxxx=", "action"=>"destroy", "controller"=>"devise/sessions"}
[02 Mar 01:45 10886 INFO] Redirected to https://ec2-xx-xx-106-255.us-west-2.compute.amazonaws.com/users/sign_out
[02 Mar 01:45 10886 INFO] Completed 301 Moved Permanently in 3ms
Started GET "/users/sign_out" for xx.xx.164.160 at 2012-03-02 01:45:42 +0000
[02 Mar 01:45 10886 FATAL]
ActionController::RoutingError (No route matches [GET] "/users/sign_out"):
So I think I need to start over from scratch. What's the simplest way to make any Devise path use https, but the rest of the paths in my app use http? I tried this (from the SO post at the top):
#devise routes
scope :protocol => 'https://', :constraints => { :protocol => 'https://' } do
devise_for :users, :controllers => { :registrations => :registrations }
devise_for :admins
end
But no go. I need a better suggestion.
No answers yet, so here's what I concluded:
Once you access a site via https, don't access it via http until the user signs out (firesheep attack). There's a lot of stuff on Devise in the article linked above that discusses only having https on the sign in / out page. Bad idea.
All you really need is this:
#in config/environments/production.rb
config.to_prepare { Devise::SessionsController.force_ssl }
config.to_prepare { Devise::RegistrationsController.force_ssl }
I had a ton of issues surrounding 'after_sign_in_path' from Devise. It turns out that after_sign_out_path_for is expecting a path to be returned -- it's not an event, it is asking where the user should be directed. So I returned root_path :protocol => 'http://' and that took care of it.
Try making your whole app use HTTPS by adding:
#in config/environments/production.rb
config.force_ssl = true
I had quite the same problem. Sometimes I sign out fine sometimes I got 301 from DELETE action and redirect to GET. For me this was the problem.
Make sure you use https in all your Devise links (this avoids the force_ssl redirect).
In your routes.rb (only applied in production environment):
scope defaults: (Rails.env.production? ? { protocol: 'https' } : {}) do
devise_for :users
end
Now in your application use:
destroy_user_session_url # use _url instead of _path so the protocol is added!
Now your logout / sign out link (and other devise links) will point directly to https. The force_ssl rewrite from HTTP DELETE to HTTPS GET is avoided. It all works :)

Phusion Passenger - route exists but is not matched (gives 404 instead)

I'm running a rails 3 app at the root level in a phusion passenger environment (CentOS, apache) and having difficulty getting passenger to find some routes, although rake routes shows the routes correctly. Everything works fine in development (i.e. using rails server instead of phusion passenger in apache).
I have an admin section to my app with a login page. The main part of the app works, but everything under the admin section is inaccessible because I get a 404 instead of the login page (when I disable login I can access the admin pages). My apache config is
<VirtualHost *:80>
ServerName foo.bar.com
DocumentRoot /var/www/apps/myapp/current/public
<Directory /var/www/apps/myapp/current/public>
Allow from all
Options -MultiViews
</Directory>
</VirtualHost>
My login process is implemented as a before_filter in an admin controller:
class Admin::AdminController < ApplicationController
# login disabled for testing
before_filter :require_login
def require_login
#current_user ||= User.find_by_id(session[:user_id])
redirect_to admin_login_path unless #current_user
end
end
My routes file has
Mpf::Application.routes.draw do
secure_protocol = "https://"
...
namespace "admin" do
...
match "login" => "user_sessions#new", :as => :login, :constraints => { :protocol => secure_protocol }
...
end
...
end
and when I run rake routes I get
admin_login /admin/login(.:format) {:protocol=>"http://", :action=>"new", :controller=>"admin/user_sessions"}
BUT when I try to access http://foo.bar.com/admin I get a 404 and the log shows
Started GET "/admin/login" for iii.iii.iii.iii at 2011-07-13 07:20:41 -0400
ActionController::RoutingError (No route matches "/admin/login"):
As far as I can tell it should be working... except for the fact that it's not. Any help would be greatly appreciated!
Have you tried accessing with https://? It looks like you provided constraints to prevent access from http:// and the link you posted references http://.

Heroku subdomain duplicate content? How to redirect to domain?

Google has indexed my Heroku app subdomain: myapp.heroku.com
Is it duplicate content?
How should I redirect myapp.heroku.com to mydomain.com?
According to Heroku docs for custom domains, you could do it like so:
class ApplicationController
before_filter :ensure_domain
APP_DOMAIN = 'myapp.mydomain.com'
def ensure_domain
if request.env['HTTP_HOST'] != APP_DOMAIN
# HTTP 301 is a "permanent" redirect
redirect_to "http://#{APP_DOMAIN}", :status => 301
end
end
end
I use this method and it works fine. Note that since the redirect returns a 301 http status (a permanent redirect) your site won't be penalized for duplicate content.
The 301 status is the only point missing in Markus' solution, but I think it is an important one if your concern is with SEO.
Edit: Something that's not on the docs and I forgot to mention - you should exclude the environments you don't want the redirect applied to. You could change the if statement to something like:
if request.env['HTTP_HOST'] != APP_DOMAIN && ENV["RAILS_ENV"] != 'development'
Use the Heroku add-on custom domains:
heroku addons:add custom_domains:basic
heroku domains:add www.myapp.com
heroku domains:add myapp.com
In addition, you have to take some configuration steps at the admin interface of your domain provider. You need a CNAME to proxy.heroku.com and three A-RECORDs to the Heroku IPs. You find this in the Heroku Docs.
Edit to respond to another answer below. You can redirect myapp.com to www.myapp.com in your routes.rb:
constraints(:host => /^communityguides.eu/) do
root :to => redirect("http://www.communityguides.eu")
match '/*path', :to => redirect {|params| "http://www.communityguides.eu/#{params[:path]}"}
end
I suggest using rack-canonical-host to redirect Heroku's subdomain to your custom domain.
rel canonical is one possibility
just put <link rel="canonical" href="http://mydomain.com"/>, <link rel="canonical" href="http://mydomain.com/page"/>, ... on your app pages.
see http://www.google.com/support/webmasters/bin/answer.py?answer=139394
google will treat the URL in the canonical element as the right ressource for that specific page.
The first answer goes part way to solve the problem but introduces a new problem.
If you add www.myapp.com and myapp.com you will then need to take care of redirecting one of these to the other inside your application - so if you choose www.myapp.com as your primary you want to check if the requested URL IS NOT www.myapp.com and redirect the request to www.myapp.com - this will then cover redirects requests coming to myapp.com and myapp.heroku.com correctly. There's an example by Heroku on their docs here.
Also, you need to get rid of the content that Google has already indexed on the Heroku domain. You'll need to use Google WebMaster tools to change the domain to www.myapp.com - it's a relatively simple process once you're logged into webmaster tools
first of all if you do not want your myapp.heroku.com to be indexed simply by adding robot meta tag in your header and give the value to "nofollow".
and for redirection just add another meta tag refresh:
<meta http-equiv="refresh" content="2;url=http://www.heroku.com/">
the content value is in seconds, the example above will direct visiotrs in 2 seconds to your main page.
hope it helps
Use the hide_heroku gem, it uses X-Robots-Tag HTTP headers to prevent search engines from indexing anything under *.herokuapp.com