Automatic Login Links

Scary, I know, but hear me out. Implemented correctly, an automatic login link can be just the ticket to appease those pesky, forgetful, real-world users.

Imagine a Rails application that sells fruit. The app has two kinds of users:

  1. A customer who comes to the site to buy fruit
  2. A seller who registers at the site to sell and ship fruit to customers

If you’re buying your fruit online, you’re probably pretty tech-savvy. Odds are you have accounts at a lot of sites and you do a decent job of remembering your passwords. And when memory fails, there’s the always-helpful “Forget your password?” link.

But what about the sellers? These guys are a bunch of farmers and not necessarily the most adept computer users. But every time a new order comes in, the seller gets an email prompting them to log in and ship the order.

Spoiler alert: The sellers never remember their passwords.

In this case, an automatic login link can go a long way to keep the sellers and the customers happy. However, with this approach, there are two important bases to cover in order to protect yourself from brute force attacks:

  1. The link must be secure (random)
  2. Access to the link must be temporary

In order to make the link temporary, we’ll attach each link to an order rather than to the seller. That way we can expire the link as soon as the order is shipped. Here’s the order model which automatically generates a unique login token when an order is placed and clears the login token when the order ships:

class Order < ActiveRecord::Base
belongs\_to :seller
belongs\_to :customer

before\_create :set\_login\_token

scope :authenticatable, where("orders.login\_token IS NOT NULL")

def ship\_it!
\# TODO Send the fruit off to the customer
update\_attribute(:login\_token, nil)
end

private
def set\_login\_token
self.login\_token = ActiveSupport::SecureRandom.urlsafe\_base64
end
end

Now we need a route for the automatic login link:

ApplesAndOranges::Application.routes.draw do
resources :orders, :only => \[:index, :show, :destroy\] do
member do
get :authenticate
post :ship
end
end
end

And in the orders controller, we can automatically log the seller in when she follows the new route:

class OrdersController < ApplicationController
respond\_to :html

before\_filter :require\_current\_seller, :except => :authenticate
before\_filter :load\_order, :except => \[:index, :authenticate\]

def index
`orders = current_seller.orders
    respond_with `orders
end

def show
respond\_with @order
end

def destroy
`order.destroy
    respond_with `order
end

def authenticate
order = Order.authenticatable.find\_by\_login\_token!(params\[:id\])
\# TODO Force-log-in order.seller
redirect\_to order
end

def ship
`order.ship_it!
    respond_with `order
end

private
def load\_order
@order = current\_seller.orders.find(params\[:id\])
end
end

And that’s it! Now, in the order notification email, we can send the automatic login link to the seller:

Dear <%= @seller.name %>,

Congratulations! You have a new order.
View your order here: <%= authenticate\_order\_url(@order.login\_token) %>

Thank you!

There may be some hesitation with allowing direct authentication like this and in a many cases, it’s justified. This technique certainly doesn’t fit all applications. As always… proceed with extreme caution.

That said, this approach can be extremely useful in certain situations. If you’re already taking advantage of the “Forget your password?” functionality mentioned above, you’re probably sending similar, perishable, password-reset links to your users. Automatic login links are no less secure. Either way, an intruder has to either gain access to your email account or guess the random link.

It may be scary but I hope this helps shed some light on the technique and how it can be done securely. Your farmers will love it!

Update: Jeffrey Paul also wrote a blog post today on this very subject, explaining his own frustration with constant prompts to log in. You can read his post here.

steve@collectiveidea.com

Comments

  1. rgoytacaz@gmail.com
    Rodrigo Dellacqua
    June 14, 2011 at 16:26 PM

    current_seller would be nil, wouldn’t it?

  2. June 14, 2011 at 16:37 PM

    Nice catch! Fixed. Thank you.

  3. June 14, 2011 at 16:42 PM

    At Kontagent, we have a system where we are able to “morph” into user accounts, thereby receiving all the same permissions and abilities of that user.  This helps us debug for support stuff, mostly.  But it also includes the ability to send someone else a link to that account and hence the ability to also morph. 

    It’s a similar system, and I bet more things will start utilizing this paradigm.

  4. June 14, 2011 at 17:13 PM

    This kind of URL is called a “capabilities URL” in security circles.  It’s a common pattern, and often considered more secure than username/password pairs, though its security depends on how the user protects it.

    You can find more details here: http://en.wikipedia.org/wiki/Capability-based_security

  5. June 14, 2011 at 20:34 PM

    This makes me sad :(

    @Phillip, no, it is not more secure. It is equivalent to storing your username/password in an email that will be forward on to someone else.

    That means if you forward the email to anyone within the [insert expiry period here] they have full access you your account.

    Please don’t be a Sony.

    If you really want to do something like this, PLEASE implement some form of out of band 2FA. For example, SMS a PIN to a mobile number once the link has been clicked. This gives the benefit of not needing to remember your password, but requiring more than just a forwarded email to sign in.

  6. June 14, 2011 at 20:36 PM

    You dont even need the column if you use url_store https://github.com/grosser/url_store

  7. June 14, 2011 at 21:32 PM

    @Dan or you could disable the link after being clicked once?  Not as efficient for the consumer, but more secure.

  8. June 14, 2011 at 22:04 PM

    @Brad. That helps, but do you click every link you receive via email? I know I don’t.

    The other assumption that is bad in terms of comparing it to the forget password link is in frequency.

    If someone is intercepting traffic at the MTA, they will have a much lower strike rate than if you were sending auto login links in every email, as opposed to only when the user specifically requests it. Auto-login links = Guaranteed compromise vs Forgot password link = Potential Compromise.

    PS: I’m not advocating the forgot password links either, though :)

  9. tgrisfal@yahoo.com
    Tyr Grisfal
    June 14, 2011 at 23:51 PM

    Multi-factor is always better…and not that hard today. Go, go, gadget SMS API.

    Main gotchas with URI tokens are that it must expire and that you should use the same practices as for other login attempts. It shouldn’t be happy hour on brute force attempts simply because there’s not a web form involved.

  10. test@example.com
    Mysterious
    June 15, 2011 at 0:12 AM

    Yea, I am not sure I like this.

    If you need to login with username / password then the bad guy has to have access to your computer to keylog.

    With the above method all he needs is access to your email account. And with things like auto-forwarding on gmail…

  11. June 15, 2011 at 1:12 AM

    @Dan actually that’s exactly the point of capabilities URLs, anyone who has the URL has access to the page.  You’d email it to anyone who you wanted to give access to the site.  Another example of this in the wild is flickr’s secret URLs to a private photo set, or skitch’s secret URLs to a photo.

  12. June 15, 2011 at 7:44 AM

    @Philip, no argument on that.

    The topic here, however, is slightly different. We are talking about account sign in and the level of security auto-login links provides.

    Unless you are arguing that it is a common use case to have someone want to forward on access to their account (as opposed to access to view stuff they set up through their account), my point still stands.

  13. June 16, 2011 at 15:43 PM

    it is ablicble for bloger and wordpress also?

  14. June 21, 2011 at 2:49 AM

    @Dan I agree with Philip. In real life it is equivalent to giving the keys of your pad to someone else, possibly someone you trust.

    Now the someone you trust is free to abuse it. Either by doing something nasty in your house, or worse by copying the key; or maybe even giving it to someone else without your knowledge. Yes. The practice is inherently insecure. But we do it all the time with the people we trust or when the damages of a breach affect both the parties.

    That said, if one doesn’t want the user to do the sign in, forget password and confirmation dance for a getting a small action done, I don’t see a comparable easy-to-use solution than using single-use links. In such cases, sharing the link is really a “feature”; and a much better solution than sharing actual account passwords.

    Also, application designers rely on certain assumptions - like the user would follow instructions in English (or some other language), the screen-resolution would be a certain minimum and the likes… The assumption that a user is able to secure his/her email account is central in this scheme of things.

    At the end of the day, all the application designer has done is to give the user the keys to his own account. If the user can’t secure his email account, or doesn’t know better about whom to trust with the links - I don’t think it is a mistake on the application designer’s part at all.

    From a security standpoint, what an application designer should do must really depend on the value at risk. Best practices aren’t for everyone…

  15. jonblock@alumni.princeton.edu
    jonblock
    June 21, 2011 at 12:14 PM

    @Amol, it’s probably not a good idea to trust the user to secure his/her email account.  In addition to the possibility that at least one (and probably most, in this scenario) seller will not take the appropriate steps to secure the account itself, the global email infrastructure is not designed to keep messages secure in transit.  At least one hop along the way from sending system to receiving account is likely to allow for the possibility of snooping.

    Reading these comments brings to mind another possibility that might better balance the account security concerns with the desire for user convenience.  With a bit more developer effort, you could implement a two-tier access system.  Automatic login links would give you access to the specific task at hand, and possibly other related tasks.  But attempting to access core account functions (bank info, seller contact information and password, full customer list, etc.) would prompt for the account’s password first.

    It’s also worth considering that, even if you are willing to accept the reduced security of automatic login links, some of your customers may not.  You can address this with a user preference setting (“Allow automatic login from email links”).

  16. January 26, 2013 at 8:05 AM

    @Dan, Adding one point to your post, You have mentioned the case of forwarded mail, My point is if suppose seller1 do not have enough fruits and wants to forward that email to  seller2, in that case none of the above solution works. In that case can we ask something like Not Seller1, Log out. But I am biased if the user needs did not get conflicted by doing this? After all security is very important concern for me.

    Well currently,I am using Capability URLs.