Skip to main content

Support Access Diagrams

TrustedLogin is designed to be simple, secure, and easy way for users to grant access to a support team. Thanks to the design of the service, login credentials are end-to-end encrypted and unable to be accessed by TrustedLogin.

Below are simplified visualizations of the flow of data between the various components of TrustedLogin.

The three parts of TrustedLogin:

  1. TrustedLogin service, running on app.trustedlogin.com
  2. Connector plugin, running on the software provider's website
  3. Client, either as a stand-alone TrustedLogin plugin or the SDK integrated with a WordPress plugin or theme

Together, these three components allow for site access to be granted securely and with minimal effort.

Support Access Flow

What happens when a customer or client grants access to their website:

Flow of customer granting access to a website

Step 1: User Grants Access

User grants access to Vendor via the Client SDK.

Grant Access form

This creates a user in WordPress with the defined roles. A "User Identifier" is created and a hash is stored in the WordPress user meta (see \TrustedLogin\Client\SupportUser::setup()). The User Identifier will be used when the Vendor logs in.

In addition, a Secret ID is generated and added to the usermeta. This hash is used as the storage ID when the site is added to the SaaS Vault.

Step 2: Public Key is Requested

The Client SDK requests the public key from the wp-json/trustedlogin/v1/public_key endpoint from the Vendor's website.

note

The public key is fetched by default from the URL defined in the vendor/website setting. It's possible to override this using the trustedlogin/{namespace}/vendor/public_key/endpoint filter.

Step 3: Public Key is Generated

The public key request is handled by \TrustedLogin\Vendor\Endpoints\PublicKey::get(), which uses \TrustedLogin\Vendor\Encryption::generateKeys() to generate two sets of encryption keys (crypto_sign and crypto_box key pairs) but only returns the crypto_box public key.

Step 4: Envelope Created & Encrypted

The envelope is generated and encrypted using Vendor public keys.

The Client \TrustedLogin\Client\Envelope::get() uses \TrustedLogin\Client\Encryption::generate_keys(), \TrustedLogin\Client\Encryption::encrypt(), \TrustedLogin\Client\Encryption::get_vendor_public_key().

The Vendor's public key is stored in the Client using WordPress transients.

Step 5: Client POSTs Envelope to SaaS

The Client SDK, using, \TrustedLogin\SiteAccess::sync_secret() makes a POST request to https://app.trustedlogin.com/api/v1/sites. This is handled by \App\Http\Controllers\SiteController::createSite(). See endpoint documentation.

Step 6: SaaS Stores Envelope in Vault

In the SaaS, SiteController::createSite() generates Vault tokens to create a secret and stores the envelope in the Vault.

The SiteCreatedEvent() event is triggered in Laravel. This logs the event to Elasticsearch.

The successful response from the SaaS to the Client is:

{ "success": true }

The unsuccessful response is:

{ "message": "'Error', or \Exception::getMessage() value." }

Support Logging Into a Customer/Client Website

Swimlane diagram of the login flow for accessing a client website

Step 1: A Person Submits Site Access Form

Site Access Key login form

The form submits a POST HTTP request via AJAX that is received by the TrustedLogin\Vendor\AccessKeyLogin::handle() method.

Receiving that request, TrustedLogin\Vendor\AccessKeyLogin::verifyGrantAccessRequest() verifies that the nonce is valid and that the request is coming from inside the site.

In addition, TrustedLogin\Vendor\Traits\VerifyUser::verifyUserRole() checks to make sure the user is logged-in and has one or more of the roles that are required to access the site.

Step 2: Vendor Requests List of Matching Site IDs

The Connector plugin requests a list of Site IDs that match that access key by sending a POST request to the accounts/{$account_id}/sites/ SaaS endpoint.

The request includes an Authorization: Bearer {hashed private key} header as well as the following body:

{
"searchKeys": [ "The submitted Site Access Key"]
}

Step 3: SaaS Verifies Request and Returns Site IDs

The SaaS verifies the hashed Bearer token passed in the Authorization header using \App\Http\Middleware\CheckPrivateKey::handle().

Then the SaaS checks to make sure the Vendor account isn't in "Pause Mode", which is triggered by brute force attempts. When Pause Mode is enabled, new access may be granted, but site login and lookups are restricted. See /Http/Middleware/CheckPauseMode.php.

\App\Http\Controllers\SiteController::siteByLicenseOrAccessKeys() is called to retrieve a list of sites stored in the Vault. Read the endpoint documentation.

An array of Secret IDs is returned. These are not the envelope itself; Secret IDs refer to the IDs of Vault secrets.

{
"accessKey1": [
"secretId1"
],
"accessKey2": [
"secretId2",
"secretId3"
]
"accessKey3": [
"secretId2",
"secretId3"
]
}

Step 4: Connector Plugin Requests Matching Envelope(s) from SaaS

The Connector plugin uses the Secret IDs to retrieve the envelopes from the Vault.

In addition to the Bearer token, the request generates a signed nonce in TrustedLogin\Vendor\Encryption::createIdentityNonce(). The method:

The nonce and signed nonce are both sent in the request, helping to verify that this site is indeed the sender of the data.

A POST request is made to sites/{account_id}/{secret_id}/get-envelope to retrieve the envelope from the Vault. The Bearer token is passed in the Authorization header, and a X-TL-TOKEN header is also sent. The X-TL-TOKEN header is a hash of the Vendor private and public keys.

Step 5: SaaS Verifies Request and Returns Envelope(s)

The SaaS verifies the hashed Bearer token and ensures that the Vendor account isn't in Pause Mode (see Step 3).

The SaaS verifies the signed nonce using \App\Http\Middleware\CheckSignedNonce::handle().

The request is handled by \App\Http\Controllers\SiteController::getEnvelope(), which retrieves the envelope from the Vault.

Inside getEnvelope(), the X-TL-TOKEN token is verified against the Vendor's account information.

The envelope with encrypted credentials is returned to the Vendor.

Step 6: Connector Plugin Receives & Decrypts Envelope

The Connector plugin receives the envelope. It includes the Site URL associated with the Site Access Key but not the endpoint, which is required to log in.

The Connector plugin decrypts the envelope and extracts the credentials, then cryptographically generates the URL to access Client site (using TrustedLogin\Vendor\TrustedLoginService::envelopeToUrl()).

The site URL and the access parts are returned as an AJAX response, completing the request started in Step 1.

Step 7: Connector Plugin POSTs to Client Site

A temporary form is created using JavaScript with the Client Site URL set as the form action property. A POST request is submitted, preventing the submitted data from being logged.

The form submits the following to the Client Site URL:

[
method: 'POST',
action: 'trustedlogin',
endpoint: {endpoint},
identifier: {identifier}
]

When the form submits, the user on the Vendor website is automatically redirected to the Client site.

Step 8: Client Verifies Login Request

The login request is received by the Client in {Client}\TrustedLogin\Endpoint::maybe_login_support().

The SDK performs security checks, including:

  1. The raw User Identifier value is found (using {Client}\TrustedLogin\Endpoint::get_user_identifier_from_request()) and then verified (using {Client}\TrustedLogin\SecurityChecks::verify()).
  2. The SDK checks whether a brute-force attack is underway (via {Client}\TrustedLogin\SecurityChecks::do_lockdown()). If an attack is determined, the code prevents login and enters Lockdown Mode. See the Security doc for more information about Lockdown Mode.
  3. The SDK determines whether the user access period has expired. If it has, the user is deleted and the login is prevented.
  4. The SDK sends a request to the SaaS to confirm that the validity of the request.

The following information is sent to sites/{secret_id}/verify-identifier using a HTTP POST request:

note

{secret_id} Refers to the Secret ID stored in user meta. It is returned using {Client}\TrustedLogin\SupportUser::get_secret_id().

{
'timestamp': time(),
'user_agent': $_SERVER['HTTP_USER_AGENT'],
'user_ip': $this->get_ip(),
'site_url': get_site_url(),
}

Step 9: SaaS Also Verifies Login Request

The SaaS receives the verify-identifier request and processes it using App\Http\Controllers\VerifyIdentifierController::handle().

The method verifies that the secret still exists in the Vault (it hasn't been deleted), and that the Vendor account is not in Pause Mode.

If success, the SaaS returns an empty JSON response [] with a 204 HTTP status code.

Possible error responses are indicated using the HTTP status codes 423 and 404:

  • 423: The Vendor account is in Pause Mode
  • 404: The Secret ID does not match any secrets in the Vault

Step 10: Client Logs User In

If the security checks pass in Step 8 and 9, the SDK calls {Client}\TrustedLogin\Endpoint::login() to log the support user in.

The user is logged-in by calling wp_set_current_user(), wp_set_auth_cookie() and do_action( 'wp_login' ).

Yay! 🎉 The user's now logged-in.

Step 11: Action Is Triggered

After login, the SDK triggers the following WordPress action: trustedlogin/{namespace}/logged_in. This allows other plugins to perform actions and to trigger webhooks.

The SDK hooks into the action to run any webhooks configured in the Config array.

Revoke Login

At any time, a website administrator may revoke TrustedLogin access. When access is revoked, the Client sends a HTTP DELETE request to the sites/{secret_id} endpoint along with a X-TL-TOKEN header.

The body of the request is:

{
'publicKey': {Client SDK API Key}
}

If the public key has been cycled, the request will fail.

Handled by \App\Http\Controllers\SiteController::deleteSite().

Possible responses are indicated using the HTTP status codes:

  • 201: Secret successfully deleted
  • 200: Secret failed to be deleted in \App\Http/Clients/Vault::deleteSite()
  • 404: The Secret ID does not match any secrets in the Vault or there
  • 500: An exception occurred