Clef is secure two-factor auth without passwords. With the wave of their phone, users can log in to your site — it's like ✨ magic ✨!
Get started in three easy steps:
- Download the iOS or Android app on your phone
- Sign up for a Clef developer account at https://www.getclef.com/developer and create an application. That's where you'll get your API credentials (
app_id
andapp_secret
) and manage settings for your Clef integration. - Follow the directions below to integrate Clef into your site's log in flow.
We'll walk you through the full Clef integration with Ruby and Sinatra below. You can also run this sample app locally.
The Clef button is the entry point into the Clef experience. Adding it to your site is as easy as dropping a script
tag wherever you want the button to show up.
Set the data-redirect-url
to the URL in your app where you will complete the OAuth handshake. You'll also want to set data-state
to an unguessable random string.
<script type='text/javascript'
class='clef-button'
src='https://clef.io/v3/clef.js'
data-app-id='<%= settings.clef_app_id %>'
data-redirect-url='<%= url("callback/login") %>'
data-state='<%= state_parameter %>'>
</script>
See the code in action or read more here.
Once you've set up the Clef button, you need to be able to handle the OAuth handshake. This is what lets you retrieve information about a user after they authenticate with Clef. The easiest way to do this is to use the oauth2
gem.
To use it, pass your app_id
and app_secret
to the OAuth2 constructor. Then,
t the route you created for the OAuth callback, access the code
URL parameter and exchange it for user information.
Before exchanging the code
for user information, you first need to verify the state
parameter sent to the callback to make sure it's the same one as the one you set in the button. (You can find implementations of the validate_state!
and state_parameter
functions in in app.rb
.)
get '/callback/login' do
validate_state!
return redirect to('/') if @user
oauth_client = OAuth2::Client.new(
settings.clef_app_id,
settings.clef_app_secret,
site: 'https://clef.io/api/v1',
token_url: 'authorize'
)
begin
access_token = oauth_client.auth_code.get_token(
params[:code],
{}, # don't pass in any params
param_name: 'access_token',
mode: :query
)
data = access_token.get('info').parsed
info = data['info']
unless @user = User.first(clef_id: info['id'])
@user = User.create(
email: info['email'],
clef_id: info['id']
)
end
session[:logged_in_at] = Time.now.to_i
session[:user] = @user.id
redirect to('/')
rescue OAuth2::Error => e
status 500
case e.code
# handle errors
when "Invalid OAuth Code." then
'Invalid OAuth Code. This could happen if the code has already been consumed or has expired.'
when "Invalid App ID." then
'Invalid App ID. This could happen if you are not passing in a valid Clef application ID.'
when "Invalid App Secret." then
'Invalid App Secret. This could happen if you are not passing in a valid Clef application secret or it does not match the application ID you are passing in.'
else
e.to_s
end
end
end
See the code in action or read more here.
Logout with Clef allows users to have complete control over their authentication sessions. Instead of users individually logging out of each site, they log out once with their phone and are automatically logged out of every site they used Clef to log into.
To make this work, you need to set up timestamped logins, handle the logout webhook and compare the two every time you load the user from your database.
Setting up timestamped logins is easy. You just add a timestamp to the session everywhere in your application code that you do the Clef OAuth handshake:
session[:logged_in_at] = Time.now.to_i
See the code in action or read more here
Every time a user logs out of Clef on their phone, Clef will send a POST
to your logout hook with a logout_token
. You can exchange this for a Clef ID:
post '/callback/logout' do
content_type :json
data = {
body: {
logout_token: params[:logout_token],
app_id: settings.clef_app_id,
app_secret: settings.clef_app_secret
}
}
response = HTTParty.post("#{settings.clef_api_base}/logout", data)
if response['success']
user = User.first(clef_id: response['clef_id'].to_s)
user.update(logged_out_at: Time.now.to_i)
{ success: true }.to_json
else
status 500
{ error: response['error'] }.to_json
end
end
See the code in action or read more here.
You'll want to make sure you have a logged_out_at
attribute on your User
model. Also, don't forget to specify this URL as the logout_hook
in your Clef application settings so Clef knows where to notify you.
Every time you load user information from the database, you'll want to compare the logged_in_at
session variable to the user logged_out_at
field. If logged_out_at
is after logged_in_at
, the user's session is no longer valid and they should be logged out of your application.
before do
if session[:user] and @user = User.get(session[:user])
if session[:logged_in_at] and @user.logged_out_at and @user.logged_out_at > session[:logged_in_at]
@user = nil
session.delete(:logged_in_at)
session.delete(:user)
end
end
end
See the code in action here or read more here
To run this sample app, clone the repo:
$ git clone https://github.com/clef/sample-ruby.git
Then install the dependencies and run on localhost:4567.
$ bundle install
$ ruby app.rb
You can find our most up-to-date documentation at http://docs.getclef.com. It covers additional topics like customizing the Clef button and testing your integration.
Have a question or just want to chat? Send an email to [[email protected]](mailto: [email protected]).
We're always around, but we do an official Q&A every Friday from 10am to noon PST :) — would love to see you there!
Clef is an Oakland-based company building a better way to log in online. We power logins on more than 80,000 websites and are building a beautiful experience and inclusive culture. Read more about our values, and if you like what you see, come work with us!