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.

NB! If you do not want to use the hosted authentication form but still need to manage OAuth2 somehow, consider using the authentication server feature instead.

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.

Generating with an API request

The easiest way to get a redirect link to the authentication form is to generate one by using EmailEngine's API.

curl -X 'POST' \
  'https://ee.example.com/v1/authentication/form' \
  -H 'Content-Type: application/json' \
  -d '{
  "account": "example",
  "name": "My Email Account",
  "email": "user@example.com",
  "redirectUrl": "https://myapp/account/settings.php"
}'
{
  "url": "https://ee.example.com/accounts/new?data=eyJhY2NvdW50IjoiZXhh...L0W_BkFH5HW6Krwmr7c"
}

Next, direct your user to the url value in order to show the authentication form.

Form URL

The URL for the hosted authentication form takes the following form:

https://emailengine.hostname/accounts/new?data=x&sig=y&type=z

Where

  • https://emailengine.hostname is the base URL for your EmailEngine installation
  • data=base64str is the encoded payload for the form (required)
  • sig=base64str is the payload signature (required)
  • type=value is an optional hint for the account type selection. If not set, then EmailEngine presents a list of options. Otherwise, the user would be redirected directly to the specific authentication option.
    • type=imap presents the IMAP setup form
    • type=gmail redirects to the Gmail's OAuth2 dialog (see the branding requirements)
    • type=outlook redirects to the Microsoft's OAuth2 dialog (see the branding requirements)

You can find the implementation guides to generate data and sig values below.

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

The redirect URL is the URL where EmailEngine redirects the user after successful authentication. EmailEngine appends two additional query arguments to that URL:

  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.

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.