Skip to content
DLTdevelopers
Sign in
DLT Merchant Portal/Getting started Public documentation

Authentication

For DLT Merchant Portal developers · DLT maintainers · Contract v1.7

Configured API origin: https://stage-checkout.dxp.dtic.com.ph

Build with the right credentials. API access requires the documented authentication and permissions. Examples use synthetic data and placeholder origins.

3. Authentication

3.1 Portal application identity and user access

DLT Merchant Portal is a DLT-owned ecosystem application used by businesses registered with DLT. In this guide, merchant means the registered business, and Portal user means a person authorized to manage that business through DLT Merchant Portal.

Portal API consumption involves two credentials:

Credential Identifies Used by the DLT Merchant Portal backend for
Portal ecosystem application client ID/secret DLT Merchant Portal itself HTTP Basic authentication at login and invitation acceptance, with X-DLT-Application: merchant_portal
User personal access token (PAT) The signed-in merchant user Bearer authentication for protected Portal APIs and logout

DLT provisions the Portal application identity. Successful login issues a user PAT after validating the application, user and merchant. The backend API resolves merchant access from that user and enforces the required role and merchant-portal scope. The Portal backend keeps both credentials in server-side storage.

Credentials managed on the Portal's merchant account screens are account assets, not additional Portal authentication methods. Section 6 documents those management APIs. Authentication by merchant-owned applications has its own Merchant integrations guide.

3.2 Portal login

Merchant Portal is DLT's own frontend within the DLT ecosystem, paired with the DLT backend/back office. DLT merchants use it to manage their accounts. In this flow, the browser displays DLT Merchant Portal; it does not represent a merchant-owned website or mobile application.

sequenceDiagram
    participant Browser as Merchant Portal browser
    participant Portal as DLT Merchant Portal backend
    participant DLT as DLT backend API
    Browser->>Portal: Username and password over HTTPS
    Portal->>DLT: POST /api/v1/auth/login (Basic application credentials)
    DLT->>DLT: Validate application, user, merchant and scopes
    DLT-->>Portal: User PAT, expires_at and user
    Portal-->>Browser: Portal session cookie
    Browser->>Portal: Request merchant data
    Portal->>DLT: Bearer user PAT
    DLT-->>Portal: Authorized merchant data
    Portal-->>Browser: Portal response

Use Merchant Portal browser → DLT Merchant Portal backend → DLT backend API. Keep application credentials and the user PAT on the backend; use Secure/HttpOnly cookies and CSRF protection for the browser session.

Request:

POST /api/v1/auth/login
Authorization: Basic <base64(ecosystem_client_id:ecosystem_client_secret)>
X-DLT-Application: merchant_portal
Accept: application/json
Content-Type: application/json

{
  "username": "merchant.admin@example.com",
  "password": "<user_password>"
}
Input Existing validation/behavior
username Required string, maximum 255 characters; searched against username or email
password Required string, minimum 6 and maximum 128 characters; existing shorter passwords retain login compatibility
Authorization HTTP Basic credentials for the active DLT Merchant Portal ecosystem application
X-DLT-Application merchant_portal, matching the supplied Portal application client ID
Origin If supplied, must match one of that application's allowed origins

Login requires an active merchant and an active Administrator (1) or Developer (10). DLT provisions the confidential application and PAT issuer; protected Portal calls require the merchant-portal scope.

200 response example:

{
  "success": true,
  "message": "Success",
  "data": {
    "access_token": "<user_passport_personal_access_token>",
    "token_type": "Bearer",
    "expires_at": "2026-10-01T10:00:00+00:00",
    "user": {
      "id": 42,
      "reference_id": "USR_EXAMPLE",
      "merchant_reference_id": "MER_EXAMPLE",
      "merchant_id": 7,
      "first_name": "Alex",
      "last_name": "Reyes",
      "role": 1,
      "email": "merchant.admin@example.com",
      "email_verified_at": "2026-09-01T00:00:00.000000Z",
      "created_at": "2026-09-01T00:00:00.000000Z"
    }
  }
}

PAT lifetime defaults to 60 minutes. Store data.access_token and respect the returned data.expires_at; no user refresh token is issued.

On expiration or 401, clear the Portal session and require login. On 403, handle the authorization failure rather than repeatedly retrying login.

Logout: POST /api/v1/auth/logout with the user PAT, no body. Returns 200:

{
  "success": true,
  "message": "Logged out successfully"
}

Logout revokes the current PAT only. Also destroy the Portal browser session.

3.3 Authenticated Portal requests

After login, the DLT Merchant Portal backend reads the user PAT from its server-side session and sends it to the DLT backend API:

GET /api/v1/portal/me
Authorization: Bearer <user_pat>
Accept: application/json

The browser uses its Portal session cookie when calling the Portal backend. The backend API uses the PAT to resolve the current user, merchant and permissions. Do not send a merchant selector or repeat the application Basic credentials on protected Portal requests.

When the PAT expires or is revoked, clear the Portal session and require login again. This flow does not issue a refresh token.

3.4 Password recovery and change

Method and path Authentication Body Success
POST /auth/reset-password/reset-link Public email, reset_url 200, generic account-independent message
POST /auth/reset-password/new-password Public reset token email, token, password, password_confirmation 200, new password saved
PUT /portal/me/password Administrator or Developer user PAT with portal eligibility current_password, password, password_confirmation 200, portal envelope with reauthentication_required=true

Reset-link validation: email maximum 255 characters; URL maximum 150 characters, matching the configured Merchant Portal origin, without URL credentials, query, or fragment. DLT appends the reset token and email. Password reset tokens use the configured password broker; this checkout sets expiry to 60 minutes.

New passwords require 12–128 characters, uppercase and lowercase letters, numbers, symbols and matching confirmation. Current passwords have a maximum of 128 characters. Login retains its 6-character minimum for existing accounts.

Successful password change/reset revokes all user tokens and requires login. Invalid, expired or consumed reset proofs return 400. New-account invitations use the same new-password policy.

3.5 Authentication failures and throttling

HTTP Existing error code Meaning
401 ECOSYSTEM_CLIENT_UNAUTHENTICATED Application code/credentials/origin eligibility failed
401 INVALID_CREDENTIALS Username/password failed
401 UNAUTHENTICATED / ACCESS_TOKEN_INVALID Protected-route token invalid, expired, revoked, or absent
403 MERCHANT_USER_NOT_AUTHORIZED Merchant user or merchant is not eligible
403 ECOSYSTEM_APPLICATION_NOT_AUTHORIZED Token's ecosystem application is not eligible
403 ACCESS_TOKEN_SCOPE_INSUFFICIENT User token lacks required application scope
403 MERCHANT_SCOPE_MISMATCH Supplied merchant identifier differs from authenticated merchant
503 TOKEN_ISSUER_MISCONFIGURED Personal-access token issuer does not match application

Existing application API throttling defaults to 60 requests/minute per resolved user ID or IP. Public routes also have a 30/minute limiter. Login additionally defaults to 5 attempts/minute keyed by application, submitted identity, and IP. These are configured code defaults; deployment configuration can change them. Respect 429 and any Retry-After header.

3.6 Authentication/header matrix for implementation

Call Authorization Additional headers Credential holder
Login Basic Portal application client ID/secret X-DLT-Application: merchant_portal, Accept/JSON content type Portal backend
Invitation acceptance Same Basic Portal application credentials Same application header, Accept/JSON content type Portal backend; submit emailed proof separately in JSON
Protected Portal APIs and logout Bearer user PAT Accept; JSON content type when sending JSON, multipart for logo Portal backend acting for the logged-in user
Public password reset No Basic/Bearer required by these routes Accept/JSON content type Portal backend can relay the public flow

Use X-DLT-Application: merchant_portal for Portal login and invitation acceptance. Basic application authentication and the resulting user's PAT are separate credentials; after login, use Bearer for protected resource requests rather than repeating Basic login credentials. The same Authorization header cannot hold both schemes. If sending Origin to login/acceptance, it must be an allowed configured origin. A browser's session cookie alone does not authenticate it to DLT.

The DLT Merchant Portal architecture is Merchant Portal browser → DLT Merchant Portal backend → DLT backend API: keep the ecosystem secret and user PAT in server-side secret/session storage, exchange a Secure/HttpOnly browser cookie with the browser, rotate the session identifier at login and enforce CSRF on browser cookie-authenticated mutations. The browser must not call confidential login directly. The DLT Merchant Portal backend manages its browser session and cookie/CORS policies; the DLT backend API does not create or manage that session. A one-use merchant credential handover is a privileged operation described in section 6.2, not permission to persist integration secrets in frontend bundles, local storage, URLs or telemetry.

Session duration must not exceed the PAT's returned expiry. Password change/reset and actual team role/removal mutations revoke affected tokens; a successful current-user mutation can therefore be followed immediately by 401. Clear the local session on successful self-removal/demotion or password change, and on a protected-call invalid-token response. A login 401 INVALID_CREDENTIALS is a form error; 401 ECOSYSTEM_CLIENT_UNAUTHENTICATED is a Portal application/provisioning failure and should not be shown as a wrong user password. No public refresh or silent renewal is available for this user PAT.

3.7 Request validation reference

These tables reflect the current implementation’s request rules. See input conventions for required/optional/null behavior and error formats.

Validation: POST /api/v1/auth/login

Field Location Requirement Validation rules
username JSON body Required Nonempty string, maximum 255 characters; matched against stored username or email.
password JSON body Required String, 6–128 characters; must match the stored password. Existing login passwords use this minimum; new/reset passwords use 12 characters.

The current validator does not reject unlisted fields; they are not validated or used by this operation. Send only documented fields. Application headers must satisfy section 2.5. Invalid body fields return the legacy 422 validation envelope; incorrect credentials return 401 INVALID_CREDENTIALS.

Validation: POST /api/v1/auth/logout

Field Location Requirement Validation rules
(none) Body/query No documented inputs No payload is required; see behavior below.

Send no request body. The controller does not validate or use payload fields. An eligible user PAT is required.

Validation: POST /api/v1/auth/reset-password/reset-link

Field Location Requirement Validation rules
email JSON body Required Valid email address, maximum 255 characters.
reset_url JSON body Required URL, maximum 150 characters. Scheme/host/effective port must match configured APP_MERCHANT_URL (default ports normalized). HTTP or HTTPS according to that configured origin; no URL username/password, query or fragment. A path is permitted; missing/invalid Portal configuration fails validation.

The current validator does not reject unlisted fields; they are not validated or used by this operation. Send only documented fields. A nonexistent email still receives the generic success message; this is not an account-existence check.

Validation: POST /api/v1/auth/reset-password/new-password

Field Location Requirement Validation rules
email JSON body Required Valid email address, maximum 255 characters; must identify the account bound to the reset proof.
token JSON body Required Nonempty string, maximum 255 characters; must be the valid, unexpired proof for that email (broker expiry currently 60 minutes).
password JSON body Required String, 12–128 characters; at least one letter, uppercase and lowercase letters, number and symbol. Must exactly match password_confirmation.
password_confirmation JSON body Required by confirmation Same value as password; do not trim passwords.

The current validator does not reject unlisted fields; they are not validated or used by this operation. Send only documented fields. Body validation returns the legacy 422 envelope. Invalid/expired broker proof returns 400, rather than 410.

3.8 Logout and password recovery examples

The following are synthetic request/response pairs. Replace IDs and credential placeholders with values from your environment. GET requests have no body; filters belong in the query string. Examples show successful operations; validation and access rules above still apply.

POST /auth/logout

Request (no body):

POST /api/v1/auth/logout
Accept: application/json
Authorization: Bearer <user_pat>

Response: 200.

{
  "success": true,
  "message": "Logged out successfully"
}

POST /auth/reset-password/reset-link

Request:

POST /api/v1/auth/reset-password/reset-link
Accept: application/json
Content-Type: application/json

{
  "email": "merchant.admin@example.com",
  "reset_url": "https://portal.example.com/reset-password"
}

Response: 200.

{
  "success": true,
  "message": "If the account exists, a password reset link has been sent."
}

POST /auth/reset-password/new-password

Request:

POST /api/v1/auth/reset-password/new-password
Accept: application/json
Content-Type: application/json

{
  "email": "merchant.admin@example.com",
  "token": "<password_reset_token>",
  "password": "<new_password>",
  "password_confirmation": "<new_password>"
}

Response: 200.

{
  "success": true,
  "message": "New password has been saved."
}
DLT Developer Documentation · Updated October 2, 2026