Tozny ♥ Magic Links

Example Tozny Magic Link email

The average online user maintains 90 distinct online accounts. At the same time, users only maintain about 19 different passwords for those various accounts. Password reuse is a major issue among consumers, and many don’t realize just how critical of an issue it has become.

Think of it this way -- as secure as your platform might be, chances are high that your users are using the same password for your system as they are for another site or service. Chances are also good that these other services implement weak security and are prone to leaks and data loss.

In short, even if your system doesn’t leak a password, your users could still be hacked when their credentials are leaked by someone else. It’s your job to keep your system’s data safe; the weakest component of your security is entirely out of your control.

So why use passwords at all?

A Better Way

Many online users either use or have heard of Slack’s messaging platform. It’s free for many organizations, easy to use, and has brought another amazing practice to the forefront: password-free authentication.

Slack provides you with the opportunity to either type a password or send a “magic link” to the email account with which you’ve registered. The link will automatically log you in to Slack, and means you never have to remember your Slack password at all.

Cutting down on the number of password users need to remember is a simple way to combat password reuse and keep your service safe and secure.

The Tozny API

The Tozny API presents a similar functionality to help either verify email addresses or promote password-free authentication for your users. Once you’ve signed up for a free beta account, you’ll be given authentication credentials and instructions for using the API to authenticate your users.

Tozny also publishes public SDKs for Java, Ruby, JavaScript, and PHP to help make it easier for developers on any platform to integrate with the service and get started with secure authentication.

Workflow

This walkthrough uses Tozny’s PHP SDK and is based on a longer tutorial presented in the February issue of net Magazine, delivered live at Sunshine PHP, and available on GitHub.

Create a Magic Link

The first step to setting up password-free authentication is to invoke the Tozny API to actually send a magic link to your customer via email.

$ToznyRealm = new Tozny_Remote_Realm_API(
  "...realm key id...", 
  "...realm secret..."
);
$sent = $ToznyRealm->realmLinkChallenge(
  $customer_email_address,     // Destination
  $link_verification_endpoint, // Callback URL
  null,                        // Unused ...
  'authenticate',              // Context
  true,                        // Send the link
  json_encode( ['username' => $customer_username])
);

This method will automatically dispatch a unique, one-time use code to the specified customer’s email address. The link will reference the $link_verification_endpoint you specify above (meaning your application will be responsible for completing the verification itself).

We’re also adding the customer’s username to the request as custom meta information. This data could be anything (the email, a user ID, their username, etc) and will be stored temporarily by Tozny in the session that identifies the one-time password. When the link is “completed” during verification, Tozny will return this information to you so you can use it to identify the user.

Tozny will not show the magic link to you. The return value of $sent will be an array with either an element “return” with the value “ok” or an element “errors” with some error information.

Verify a Magic Link

When your customer receives their magic link from Tozny, the URL will point back to the predefined endpoint on your own site. This keeps the flow of user data entirely within your control and allows you to both validate the link and immediately authenticate the user.

Note: When the link was created, it was done so with a context of “authenticate.” The other two valid contexts are “enroll” and “verify.” These are each used, respectively, to specify if a link is intended to log a user in, register a new user, or merely verify the ownership of an email address. It’s important to keep these contexts in mind when both creating and validating links to ensure that customers aren’t attempting to use a “verify” link to register or authenticate (or otherwise mix contexts).

The magic link itself will contain two query parameters -- toznyo and toznyr. The first parameter is the unique, one-time password issued to your user. The second is your Realm’s ID (which can be used to switch contexts in the event you’re using multiple Tozny Realms within the same application). The only component you need to complete the link workflow is the toznyo parameter:

$ToznyUserRealm = new Tozny_Remote_User_API( "...realm key id..." );
$validated = $ToznyUser->userLinkResult( $toznyo );

The $validated variable will at this point contain either an error response from the Tozny API indicating what went wrong with the link, or an array with signed_data and signature. Assuming a successful response, these two parameters will define a base64-encoded associative array that represents the authentication session just completed and the HMAC signature of that data.

After verifying the signature with your realm’s secret key, the signed_data can be decoded to extract the data originally sent to Tozny to identify the user’s account:

$data = $validated['signed_data'];
$decoded = Tozny_Remote_Realm_API::base64UrlDecode( $data );
$deserialized = json_decode( $decoded, true );

if ( isset( $deserialized['data'] ) ) {
  $realm_data = json_decode( $deserialized['data'], true );

  if ( isset( $realm_data['username'] ) ) {
    $username = $realm_data['username'];
    // Now authenticate the user!
  }
}

// Redirect to an error state if no data was set

At this point, you can use the data returned from Tozny to authenticate the user with your platform. They’ve already identified themselves (by providing their email or username) and have verified their identity by clicking the magic link sent to their inbox. No password was ever needed!

Next Steps

First and foremost, authenticating without a password is removing a component of your users’ security system. Most systems have two components -- something you are (email or username) and something you know (password). With only one component in place, it’s arguably easier for social engineering or phishing attacks to take place.

As with any system, adding a second factor (i.e. something you have) for authentication can help make logins more secure:

  • Mobile apps like Google Authenticator can generate time-based one time passwords to further verify user accounts
  • Tools like Yubikeys provide a physical element of authentication that can be tied to individual logins
  • Tozny’s Secure Authentication product enables the use of strong cryptographic keys on a mobile device to verify authentication

Your service’s or website’s security is only as strong as your users’ ability to keep their own data secure. Moving away from passwords means you’ve lessened the security burden on your customers, but it also means your site is only as secure as your customers’ email. Adding a secure, yet simple, second factor that doesn’t depend on your customer’s memory is a solid way to keep password-free authentication secure.