Authenticate devise user from email - ruby-on-rails

I'm using rails 3 and devise. I would like to do build in the following work flow.
Article is submitted on site that requires admin approval.
Site sends the admin of the site an email with a link to the page where the admin can review edit and approve the article
I've got this implemented, however, if the admin is not currently logged into the site the admin is bounced back to the root path for not being authenticated.
Is there away that the link that that is sent in the email can act as an authentication for the specific admin?

You want to look into token authentication for devise. This allows you to use a one time key to authorize user access through a URL parameter.
See more information here. http://zyphmartin.com/blog/simple-auth-token-example-with-devise
Also you could simply create a authentication token per resource and store the authentication value in the database. When you go to the approve URL for the resource pass in the resource id and the authentication token. This would be more secure than logging the user in as administrator.
If you need more help let me know.

You could implement this as a custom controller action.
You can turn off devise authentication for a given action in a controller by adding an :except => :custom_action key to your :before_filter call.
You could include a query string in the link with the article_id and the admin_id. Then in the custom controller action, you would check that this article has not already been moderated, via an extra field in the articles table. If it had, you could bail. If it hadn't then you could allow the moderator to approve or disapprove of it, set the moderated flag to true and thank the moderator for her work.
If you wanted to get really tricky, you could make the URL include an MD5-hashed id which you could use as the key rather than an article id. You could store this in an extra field in the article model. That would make it much less likely to get spoofed during the brief window when it is moderate-able.
ian.

You could add an action to the controller (which is where your URL would point to), and then exclude it from authentication by adding this to your controller:
before_filter :authenticate_user!, :except => [:review_article]
You could include some sort of a key in the URL (e.g. MD5 of the article as Ian suggested), and check that in review_article.

Related

Getting past anti-CSRF to log a user into a site when you know their username and password

This sounds a bit evil, bear with me though. It's also not specifically a Rails question even though the two sites in question use Rails. (Apologies in advance for both these things)
Imagine two websites which both use Ruby on Rails:
mysite.com, on which i'm a developer and have full access in terms of changing code etc, and also have an admin login, so I can manage user accounts.
theirsite.com, on which i have an admin login but no dev access. I know the people who run it but i'd rather not ask them any favours for political reasons. That is an option however.
Using my admin login on each site i've made a user account for the same person. When they're logged into mysite.com, i'd like to be able to provide a button which logs them straight into theirsite.com. I have their username and password for theirsite.com stored in their user record in the mysite.com database, to facilitate this. The button is the submit button for a form which duplicates the form on the theirsite.com login page, with hidden fields for their username and password.
The stumbling block is that theirsite.com handles CSRF with an authenticity_token variable, which is failing validation when the login submits from mysite.com.
My first attempt to get past this was, in the mysite.com controller which loads the page with the form, to scrape the theirsite.com login page to get an authenticity token, and then plug that into my form. But this isn't working.
If i load the theirsite.com login page, and the mysite.com page with the remote login button in two browser tabs, and manually copy the authenticity_token from the theirsite.com form to the mysite.com form, then it works. This is because (i think) the authenticity_token is linked to my session via a cookie, and when i do it all in the same browser the session matches up, but when i get the authenticity token from theirsite.com via scraping (using Nokogiri but i could use curl instead) it's not the same session.
Question A) So, i think that i also need to set a cookie so that the session matches up between the browser and the Nokogiri request that i make. But, this might be impossible, and exactly the sort of thing that the anti-CSRF system was designed to defeat. Is that the case?
Question B) Let's say that i decide that, despite the politics, i need to ask the owner of theirsite.com to make a small change to allow me to log our users into theirsite.com when we know their theirsite.com username and password. What would be the smallest, safest change that i could ask them to make to allow this?
Please feel free to say "Get off SO you evil blackhat", i think that's a valid response. The question is a bit dodgy.
A) No, this is not possible as CSRF Protection is made to protect from actions like these only. So "Get off SO you evil blackhat"
As per the question I'm assuming that theirsite.com is using Rails(v3 or v4)
B) The smallest change that you could ask them to do is to make a special action for you, so that you could pass user credentials from your back-end and the user will be logged in from their on.
That action will work something like this :
You'll have a special code which will be passed along the credentials so that the request is verified on their servers. That code can either be a static predefined code or it can be generated on minute/hour/day basis with the same algorithm on both sites.
The function that you'd be asking to make for you will be like this:
Rails v3 and v4:
This action will be POST only.
#I'm supposing 'protect_from_forgery' is already done in theirsite.com
class ApplicationController < ActionController::Base
protect_from_forgery
end
#changes to be made are here as follows
class SomeController < ApplicationController
skip_before_filter :verify_authenticity_token, only: [:login_outside] #this turns off CSRF protection on specific actions
def login_outside
if(#check special code here)
#Their login logic here
end
end
end
Check this link for further information on skipping CSRF protection in Rails
Rails 4 RequestForgeryProtection
This shouldn't be too hard to do.
You need to send an ajax GET request to their signup page, copy the authenticity_token with javascript, and then send an ajax POST to the actual log in route that creates a session with the right credentials and authenticity_token.
One tricky part is finding out their log in route. Try /sessions/new or perhaps they have the url in the form, so look at the html there. Good luck!
The other tricky part is knowing how the parameters are usually sent. Check out the form's html. If all the input tags have user_ before their name's then you'll need to structure your parameters similarly; i.e. user_email, user_password.
It's entirely possible to fetch the crsf token and submit your own form (because a log-in page is accessible to anyone!). However, it'll be difficult to know the details of their arrangement. The guessing and checking isn't too bad of an options (again, /sessions/new is how I route my log in; you should also try your route to see if they have a similar one.)
If that doesn't work, try taking a look at their github account! It's very possible they haven't paid $7 a month and it's open to the public. You will easily be able to view their routes and parameter parsings that way.
Good luck!
This is impossible. The anti-csrf works like you send cookie to an user, and inject token in form of hidden field into a form; if the token matches with cookie form post is accepted. Now if you run form on your side, you can't set the cookie (as the cookie can be only set in domain of its origin).
If there is just some particular action you want to perform on their site, you can get away with browser automation. (i.e. your run browser on your server-side, script the action and execute it).
As for B) safest and smallest change is contradiction :) Smallest change would be to create handler for POST request on their side where you'll send username and password (this handler HAS TO run over https) and it will create auth cookie on their side.
As for safest - the whole concept of storing encrypted (not hashed) passwords is questionable at best (would you like your site to be listed here http://plaintextoffenders.com/ ?). Also if user changes his password on their side you're screwed. Secure solution would be that you'll store just 3pty UserID on your side, and you'll send asymmetrically encrypted UserID with Timestamp to their side (you'll encrypt it with your private key). They'll decrypt it (they'll have to have public key), validate if timestamp is not to old and if not they'll create auth cookie for given user id. There are also protocols for that (like SAML).
A)
What you are trying to do is really a form of a CSRF attack.
The idea behind a cross-site request forgery attack is that an attacker tricks a browser into performing an action as a user on some site, as the user who is using the site. The user is usually identified by a session identifier stored in a cookie, and cookies are sent along automatically. This means that without protection, an attacker would be able to perform actions on the target site.
To prevent CSRF, a site typically includes an anti-CSRF token in pages, which is tied to the session and is sent along in requests made from the legitimate site.
This works because the token is unpredictable, and the attacker cannot read the token value from the legitimate site's pages.
I could list various ways in which CSRF protection may be bypassed, but these all depend on on an incorrect implementation of the anti-CSRF mechanism. If you manage to do so, you have found a security vulnerability in theirsite.com.
For more background information about CSRF, see https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF).
B)
The smallest change which theirsite.com could do is to disable the CSRF protection check for the login page.
CSRF protection depends on the unpredictability of requests, and for login pages, the secret password itself protects against CSRF. An extra check through an anti-CSRF token is unnecessary.

Devise: How to better ask for more oauth permissions?

I use Devise to do the oauth with Github for my users. On login we ask for two basic scopes (for regular users) but if a user wants to perform some action (like setting up a new github organization in our product) we need to require some other scopes in addition to the ones we asked already.
I got it working for the most part the question is about how to better put everything together. This is the timeline of events:
User login (scope email)
User wants to perform setup action (POST /organizations)
in POST /organizations we check for the scopes of the current user and redirect to github to ask for the extra permissions if needed
User accepts the permissions and we get the oauth callback in the callbacks controller.
What I want is to continue with the process have at POST /organizations but the callback is in a different controller (obviously).
I've looked into omniauth.origin but that will only work for a GET redirect and this is a POST.
Any ideas how to better structure a solution for this?
Thank you!

rails authentication for an API

I'm currently working on an application that in addition to the usual visual web application goop also will expose a few RESTful API services for use by outside applications. I am using Devise to manage user authentication but I'm struggling with how to "manually" authenticate a user given certain input.
The case I have is that I want a user of the API to log in w/o actually going to the visual log in screen etc. I want them to submit a username and password and then authenticate and sign them in in my API services.
I know that you can sign a user in using the sign_in method that Devise provides, but that seems to ignore authentication entirely. here's what I wanted to do explained in a bit more detail:
Assume a GET route called connect in the user controller. the controller is replacing entirely the Devise registrations controller, but not the session one. The URL to my service would be:
<server>/users/connect
and it would expect 'email', 'password' parameters in addition to some service specific and unimportant to my question goop.
What I want to know is how to implement something that is equivalent to the following pseudocode:
def connect
user = User.find_by_email(params[:email])
password = params[:password]
# here is the part I'm pseudo coding out
if user.is_valid_password(password)
...do my stuff...
end
render :json ...etc...
end
I have been unable to find a method in the Devise source to do this--it's so generalized in so many ways that I'm likely just missing it.
Anyone have any ideas? I'm hoping not to a) have to implement my own thing and b) not have to move away from Devise. It provides me with so much for the non-API services...
thanks!
I've left out th
Devise's token_authenticatable is the way to go for this. We've successfully used it many times to do api-based logins.
In config/initializers/devise.rb
config.token_authentication_key = :nameofyourapikeyhere
In user.rb:
devise … token_authenticatable, ...
In the above, you can name the api key anything and then have your route as /users/connect?apikey=whatever (using apikey as an example). In the database, it'll be authentication_token, but it'll work fine.
To clarify, if the user has an authentication_token and it's sent in the params (or it's alias- in the above example: apikey), they'll login.

How to use Grails Spring Security Plugin to require logging in before access an action?

I know that I can use annotation or Request mapping to restrict access to an ACTION by some specific ROLES. But now I have a different circumstance.
My scenario is: every user of my site can create posts, and they can make their own post public, private, or only share to some other users. I implement sharing post by a database table PERMISSION, which specify if a user have the right to view a post or not.
The problem arises here is that when a customer access a post through a direct link, how can I determine he/she have the privilege to view it? There's 3 circumstances:
The post is public, so it can be viewed by anyone (include not-login
user)
The post is private, so only the login-owner can view it
The post is sharing, it means only the login-user that is shared and the
owner can view it.
I want to process like this:
If the requested post is public: ok.
If the requested post is private/sharing: I want to redirect
the customer to the login page; after
logging in, the user will be re-direct
to the page he wants to see.
The problem here is that I can redirect the user to login controller/ auth action, but after that I don't know how to redirect it back. The link to every post is different by post_id, so I can't use SpringSecurityUtils.securityConfig.successHandler.defaultTargetUrl
Could anyone know a way to do this?
Dunno about grails, but spring security has a spring-security-redirect parameter which can be used to redirect the user to the specified url on successful authentication.
I think you could add your own filter that will be executed before the action is called and do the verification of the post permissions there. You can find more information about Grails Filters here.
Have you looked at the Grails Spring Security ACL plugin? I don't know it very well, but it's designed to restrict access to particular instances:
http://grails.org/plugin/spring-security-acl
I have found a quick workaround for this problem:
If the user is logged in: check the user's privilege, and return the appropriate result.
If the user is not logged in: At view action, set the post_id by:
session.post_id = 8
Redirect the user to the Login Controller/ Auth action.
At checkrole action(which is my grails.plugins.springsecurity.successHandler.defaultTargetUrl in Config.groovy), if session.post_id exists, use it to build the link for re-directing to the view action. Before redirecting, clear the session.post_id.

How to create password protected RSS feed in rails

Creating RSS feed in rails is easy. I need a simple way to password protect the RSS feed. I am thinking http basic authentication.
I googled but could not find any article about creating password protected RSS.
I have this in my ApplicationController
def xml_authorize
if request.format == Mime::XML
authenticate_or_request_with_http_basic do |username, password|
username == 'foo' && password == 'bar'
end
end
end
Then I just apply a before_filter :xml_authorize to the actions that I want to password protect for XML requests only, but still want to serve normally with html.
Here's where I got the inspiration from.
Just use whatever auth system you use on your regular controllers. If the user is logged, and session is alive he will be able to download the feed.
How is HTTP authentication any different on RSS feeds than other controller actions? (This is not necessarily a rhetorical question - is it actually different?)
Have you tried just using the Rails HTTP Basic Authentication methods?
Feeds are meant to be fetched in regular intervals without user interaction.
RSS-Feeds can be consumed by something different than a browser. For example,
I have a phone where I can create a widget for the start screen from a rss-feed-link. Great function. Unfortunately authentication does not work for this widget.
And there is no user interface to put in username and password. So the authentication need to be part of the url, with all the bad security implications...
On the other hand you don't need a session to answer a feed-request.
So the solution is a create a special key for the user, and store it in the user table.
Then use it when you display the link to the rss-feed. In the feed-method, you use this key to retrieve the user, but you don't create a session. This way, even when a hacker somehow got a valid key, the hacker can only view the rss-feed, but not access the rest of your application.
If you already use some library for authentication, there may already some solution implemented for this. In Authlogic, is is the class SingleAccessToken, and you need to add a column 'single_access_token' of type string to your user table. Then authlogic creates some cryptic key when are saving the user record. You than add this key as the GET-Parameter 'user_credentials' to the url of the private rss-feed
Like knoopx said, If you use an authentication system like authlogic you should be able to specify the authentication type in the controller. You can then specify http basic authenication. Then you can, if you choose, include the authentication in the URL for the RSS Feed.
e.g:
http://username:password#example.com/rss
(sorry to break the URI up like that, but I don't have enough reputation points to post multiple links :( )

Resources