Email Redirection in Multi-Frontend Structure

This guide describes the configuration required to enable frontend-aware email redirection in a Commerce environment with multiple frontend domains. In multi-tenant or multi-regional setups, users must receive emails with redirect links that point to the appropriate frontend based on their original interaction. This feature ensures that all links in transactional emails are correctly generated per the frontend that initiated the request.

In a traditional setup, email templates generate links using a static domain or path. In projects supporting multiple frontends (e.g., UAE, Kuwait), this approach causes inconsistencies, potentially redirecting users to the wrong site. For example, a user interacting with example.com/ae might receive an email containing a link to example.com/kw.

With this feature, frontend identification is embedded in each request via a custom header. The backend reads this information and uses it to generate the correct domain and path in email templates.

How It Works

  1. Frontend Identification: Every API request includes an x-frontend-id header that specifies which frontend initiated the request.

  2. Backend Context Injection: A custom account adapter captures this header and injects it into the template rendering context.

  3. Email Template Logic: The email template uses this context or fallback values from the user profile to determine the appropriate domain path.

  4. URL Generation: Email links are constructed dynamically, resulting in accurate redirection per user.

Commerce Configuration

1. Custom Account Adapter

To support frontend-aware context in all authentication operations (user registration, password reset, etc.), a custom account adapter must be used:

ACCOUNT_ADAPTER = 'omnishop.users.allauth_adapter.CustomDefaultAccountAdapter'

The CustomDefaultAccountAdapter reads the x-frontend-id from incoming HTTP requests and passes it as frontend_id in the rendering context. This makes it available within email templates and other processes requiring frontend-aware logic.

2. Valid Frontend IDs Setting

The VALID_FRONTEND_IDS configuration defines all acceptable frontend identifiers used by the system. It prevents invalid or unknown frontend values from affecting link generation.

This setting is managed via dynamic settings and must follow the structure below:

VALID_FRONTEND_IDS = [
    {"id": "1", "name": "Kuwait Frontend"},
    {"id": "2x", "name": "UAE Frontend"},
    // Additional frontend entries
]

Field Definitions:

  • id: A unique string identifier (CharField) sent by the frontend in the x-frontend-id header. It is used for routing and URL generation.

  • name: A human-readable name for distinguishing frontend entries in configuration UIs. Not used in logic.

User Schema Configuration in Omnitron

To persist the user’s frontend source, the frontend_id field must be added to the user schema in Omnitron. This allows email redirection logic to work even when the email is triggered by an external system (e.g., Omnitron).

Add frontend_id to the User Schema

curl --location --request PUT 'https://{omnitron_url}/api/v1/remote/1/schema_types/user/' \
--header 'authorization: Token YOUR_API_TOKEN_HERE' \
--header 'Content-Type: application/json' \
--data '{
   "schema": {
       "frontend_id": {
           "required": false,
           "data_type": "text",
           "key": "frontend_id",
           "label": "Frontend ID"
       }
       // include the rest of the schema as-is
   }
}'

Behavior:

  • When a user registers or makes an authenticated request from the frontend, the x-frontend-id value is automatically recorded in the user object under user.attributes.frontend_id.

  • This value becomes available in all contexts where user attributes are accessible, such as email templates triggered outside of the Commerce backend.

Frontend Implementation

Every frontend application that sends API requests to the backend must include the x-frontend-id HTTP header.

x-frontend-id: 1

This value must match one of the defined entries in the VALID_FRONTEND_IDS list. If it does not, backend logic will treat the frontend as undefined for email redirection purposes.

API Request Example:

Here is an example password reset request from a frontend application:

curl --location 'https://{commerce_url}/users/password/reset/' \
--header 'x-frontend-id: 1' \
--header 'Content-Type: application/json' \
--data-raw '{
    "email": "[email protected]"
}'

In this request, x-frontend-id: 1 indicates the request originates from the Kuwait frontend, and links in the email will be tailored accordingly.

Email Template Implementation

To generate frontend-aware links in email templates, use both the incoming frontend_id from context and the fallback stored in user.attributes.frontend_id.

Example Snippet:

{# Get frontend ID from context or fallback to user attribute #}
{% set x_frontend_id = frontend_id or user.attributes.get("frontend_id") %}

{# Define dynamic path based on frontend ID #}
{% if x_frontend_id == "1" %}
    {% set dynamic_path = "/frontend_id_1" %}
{% elif x_frontend_id == "2x" %}
    {% set dynamic_path = "/frontend_id_2" %}
{% else %}
   {% set dynamic_path = "/defualt" %}
{% endif %}

{# Construct the final confirmation URL #}
{% set confirm_url = "https://" + current_site.domain + dynamic_path + url('account_confirm_email', key=key) %}

<a href="{{ confirm_url }}">{{ confirm_url }}</a>

Template Variables Explained:

  • frontend_id: Retrieved from the incoming request context, if available.

  • user.attributes.get("frontend_id"): Fallback value stored in the user schema, typically used when the email is triggered by the other system (e.g., Omnitron).

  • dynamic_path: A frontend-specific prefix that adjusts the final URL path. Dynamically determined based on the identified frontend_id.

    • If neither frontend_id from context nor user.attributes.frontend_id is available, a default path (/default) is applied to avoid broken URLs and ensure graceful degradation.

  • confirm_url: Final redirect URL combining domain, path, and endpoint.

Last updated

Was this helpful?