Hosted authentication

Let EmailEngine handle the email setup flow.

While you can always register new accounts using the EmailEngine's API, you could also let EmailEngine handle the entire email setup flow.

In that case, you would redirect your users to your EmailEngine installation, where they'd see an email server setup form. Once they have completed that form and EmailEngine has registered their settings, that user will be redirected back to your application.

How does it work?

Your application generates a signed URL against your EmailEngine's installation that includes the general account information (account id, name) about the user to be added.

Then you redirect your user to that URL, and EmailEngine takes over by showing the forms you can see above.

Once the user has finished setting up their email account, EmailEngine redirects that user back to your application. You can start making API requests against the user's email account.

NB! For the Outlook and Gmail options to show up, you must first configure these. See tutorials for Outlook and Gmail.

Used internally

EmailEngine uses hosted authentication internally when registering new accounts from the dashboard.

Customization

You can customize the form (eg. to add a logo) by editing the template header for public pages.

Code examples

1. Node.js implementation

The following Node.js example script generates a URL for the authentication form.

const crypto = require("crypto");

// The "Service secret" value from the Service configuration page
const serviceSecret = "a23da152f5b88543f52420a0de0e0eb6";

// URL to EmailEngine
const emailEngineBaseUrl = "http://127.0.0.1:3000";

const accountData = {
  // optional account ID, autogenerated if not set
  account: "account_id",
  // optional name of the user, can be changed by the user
  name: "Full Name",
  // optional email address of the user, can be changed by the user
  email: "user@example.com",
  // required URL where to redirect once account has been added
  redirectUrl: "https://my.app.example.com/account/added",
};

/**
 * Sign a value
 * @param {Buffer} value Binary data to be signed
 * @param {String} secret Service secret
 * @returns {String} url-safe base64 encoded signature
 */
function signRequest(value, secret) {
  // Sign the payload
  let hmac = crypto.createHmac("sha256", serviceSecret);
  hmac.update(value);
  return hmac.digest("base64url");
}

/**
 * Build the hosted authentication form URL
 * @param {String} baseUrl Base URL for EmailEngine
 * @param {Object} data Account data to be signed
 * @param {String} secret Service secret
 * @returns {String} URL to hosted authentication page
 */
function getAuthenticationUrl(baseUrl, data, secret) {
  const dataJson = JSON.stringify(data);
  const dataBuf = Buffer.from(dataJson);
  const signature = signRequest(dataBuf, secret);

  const url = new URL(`accounts/new`, baseUrl);
  url.searchParams.append("data", dataBuf.toString("base64url"));
  url.searchParams.append("sig", signature);

  return url.href;
}

// Form URL is now ready to be used
console.log(getAuthenticationUrl(emailEngineBaseUrl, accountData, serviceSecret));

NB! Note that the example script uses 'base64url' encoding defined in RFC4648. This is a valid encoding supported by Node 14 and 16. Older versions of Node do not support it.

2. PHP implementation

This is a self contained example. You can also use the postalsys/emailengine-php Composer library to generate the authentication URL (look for the get_authentication_url method).

<?php

// "Service secret" value from the Service configuration page
$service_secret = 'a23da152f5b88543f52420a0de0e0eb6';

// URL to EmailEngine
$email_engine_base_url = 'http://127.0.0.1:3000/';

$account_data = array(
    // optional account ID, autogenerated if not set
    'account' => 'account_id',
    // optional name of the user, can be changed by the user
    'name' => 'Full Name',
    // optional email address of the user, can be changed by the user
    'email' => 'user@example.com',
    // required URL where to redirect once account has been added
    'redirectUrl' => 'https://my.app.example.com/account/added',
);

/**
 * Encode a string value into the url-safe base64 encoding
 * @param {String} $val Value to be encoded
 * @return {String} Url-safe base64 encoded value
 */
function base64_encode_urlsafe($val)
{
    return str_replace(array('+', '/', '='), array('-', '_', ''), base64_encode($val));
}

/**
 * Decode a url-safe base64 encoded value to a binary string
 * @param {String} $val Url-safe base64 encoded value
 * @return {String} Decoded string value
 */
function base64_decode_urlsafe($val)
{
    $data = str_replace(array('-', '_'), array('+', '/'), $val);
    $mod4 = strlen($data) % 4;
    if ($mod4) {
        $data .= substr('====', $mod4);
    }
    return base64_decode($data);
}

/**
 * Sign a value
 * @param {String} $value String value to be signed
 * @param {String} $secret Service secret
 * @return {String} url-safe base64 encoded signature
 */
function sign_request($val, $secret)
{
    $hmac = hash_hmac("sha256", $val, $secret, true);
    return base64_encode_urlsafe($hmac);
}

/**
 * Build the hosted authentication form URL
 * @param {String} $baseUrl Base URL for EmailEngine
 * @param {Object} $data Account data to be signed
 * @param {String} $secret Service secret
 * @return {String} URL to hosted authentication page
 */
function get_authentication_url($email_engine_base_url, $data, $secret)
{
    $data_json = json_encode($data);
    $signature = sign_request($data_json, $secret);

    return preg_replace('{/$}', '', $email_engine_base_url) .
    '/accounts/new?data=' . base64_encode_urlsafe($data_json) . '&sig=' . ($signature);
}

// Form URL is now ready to be used
echo get_authentication_url($email_engine_base_url, $account_data, $service_secret);
echo "\n";

Redirect URL

EmailEngine appends two additional query arguments to the redirect URL once the account has been registered.

  1. account is the account ID of the registered account. If you provided the account variable in the signed request, then this is the same value, otherwise, it's an autogenerated string.
  2. state is either "new" if it's a new account, or "existing" if you used an already existing account ID. In the latter case, the existing account gets updated and no new account is added.