Last active
December 15, 2015 20:59
-
-
Save clody69/5322309 to your computer and use it in GitHub Desktop.
Password-less Authentication by email.
Inspired by some of Luke's writings on login and password usability issues (http://www.lukew.com/ff/entry.asp?1487, http://alistapart.com/article/signupforms), I have decided to test an authentication mechanism without passwords. In simple terms this can be seen as a combination of the "remember me" and "fo…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'sinatra' | |
require 'haml' | |
$users = {'john' => {:roles => [:user] }, 'mike' => {:roles => [:user, :admin] } } | |
$tokens = {'123' => {:username => 'john', :expires_at => Time.now+60}} | |
helpers do | |
def authenticate_user! | |
@auth_token = auth_token | |
if $tokens.has_key?(@auth_token) && !$tokens[@auth_token][:expires_at].nil? && $tokens[@auth_token][:expires_at] > Time.now | |
@current_user = $tokens[@auth_token][:username] | |
else | |
@current_user = nil | |
end | |
end | |
def current_user | |
@current_user | |
end | |
def login? | |
!@current_user.nil? | |
end | |
def activate!(token) | |
if $tokens.has_key?(token) && $tokens[token][:expires_at].nil? | |
$tokens[token][:expires_at] = Time.now + 600 | |
return true | |
end | |
end | |
def logout | |
$tokens.delete(@auth_token) | |
end | |
def roles?(roles) | |
return true if roles.include?(:any) | |
!(roles & $users[@current_user][:roles]).empty? | |
end | |
def auth_token | |
if params.has_key?("auth_token") | |
return params["auth_token"] | |
elsif request.env.has_key?('HTTP_AUTHENTICATION') | |
return request.env["HTTP_AUTHENTICATION"][/Token token="?(\w+)"?/,1] | |
end | |
end | |
def generate_auth_token | |
begin | |
token = SecureRandom.hex(20) | |
end while $tokens.has_key?(token) | |
token | |
end | |
end | |
set(:auth) do |*roles| | |
condition do | |
halt 401, 'Unauthorized' unless login? | |
halt 403, 'Forbidden' unless roles?(roles.to_a) | |
end | |
end | |
before do | |
authenticate_user! | |
end | |
get '/' do | |
haml :index | |
end | |
get "/resource", :auth => [:user, :admin] do | |
"You can access the resource for authorized users" | |
end | |
get "/secret", :auth => [:admin] do | |
"You can accesss the resource only for admins" | |
end | |
get "/login" do | |
haml :login | |
end | |
get "/logout", :auth => [:any] do | |
logout | |
haml :logout | |
end | |
post "/login" do | |
haml :not_authorized unless $users.include?(params[:email]) | |
@token = generate_auth_token | |
$tokens[@token] = {:username => params[:email], :expires_at => nil} | |
haml :authentication_email | |
end | |
get "/activate/:token" do | |
@token = params[:token] | |
if activate!(params[:token]) | |
haml :authenticated | |
else | |
haml :not_authenticated | |
end | |
end | |
__END__ | |
@@layout | |
!!! 5 | |
%html | |
%head | |
%title Sinatra Instant Email Authentication | |
%body | |
=yield | |
@@index | |
:javascript | |
fetch = function(url,id) { | |
var xhr = new XMLHttpRequest(); | |
xhr.open('GET', url); | |
xhr.setRequestHeader('Authentication', 'Token token=' + localStorage.getItem('auth_token') ); | |
xhr.onload = function(e) { | |
document.getElementById(id).innerHTML = (this.status == 200) ? this.responseText : this.status + ' ' + this.statusText; | |
}; | |
xhr.send(); | |
} | |
logout = function() { | |
window.location.href = "/logout?auth_token=" + localStorage.getItem('auth_token'); | |
} | |
fetch('/resource', 'resource'); | |
fetch('/secret', 'secret'); | |
%a(href="/login") Login | |
%a(href="#" onclick="logout();" ) Logout | |
%p | |
XHR Repsonse on authorized users: | |
%span#resource | |
%p | |
XHR Response on a secret resource only for admin: | |
%span#secret | |
@@login | |
%form(action="/login" method="post") | |
%div | |
%label(for="email")Enter your email: | |
%input#email(type="text" name="email") | |
%div | |
%input(type="submit" value="Login") | |
@@authentication_email | |
:javascript | |
localStorage.setItem('auth_token', '#{@token}'); | |
%p Here is your email for authenticating. | |
%p In order to login into the system, please follow this link: | |
%a(href="/activate/#{@token}") Log me in | |
@@logout | |
:javascript | |
localStorage.removeItem('auth_token'); | |
%p You have logged out | |
%a(href="/") Home | |
@@authenticated | |
%p | |
You have logged in and now you can access your | |
%a(href="/") home | |
@@not_authenticated | |
%p | |
You activation failed. | |
@@not_authorized | |
%p You are not authorized. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice, been looking for this. Seems simple enough and I might adapt it to my needs so it can replace a silly full fledged auth system.