Client Portal) a contact signing in with a password is kept here * until it is on (includes/check_login.php). * * SCOPING: everything is the session contact's own - no id is read from the * request. The forms post to post.php. */ // intl-tel-input for the phone numbers - see includes/header.php $portal_load_phone_inputs = true; header("Content-Security-Policy: default-src 'self'"); require_once 'includes/inc_all.php'; $me = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT contact_name, contact_title, contact_department, contact_email, contact_extension, contact_location_id, contact_mobile, contact_mobile_country_code, contact_phone, contact_phone_country_code, contact_pin, contact_billing, contact_technical, contact_primary, contact_ticket_emails, contact_company_ticket_emails, location_name, location_address, location_city, location_state, location_zip, location_country FROM contacts LEFT JOIN locations ON location_id = contact_location_id AND location_client_id = $session_client_id AND location_archived_at IS NULL WHERE contact_id = $session_contact_id AND contact_client_id = $session_client_id LIMIT 1")); $password_account = $session_auth_method === 'local'; $microsoft_account = $session_auth_method === 'azure'; $mfa_required = intval($config_client_portal_require_mfa) === 1; $mfa_setup = $password_account && !$session_mfa_on && (($_GET['mfa'] ?? '') === 'setup' || $portal_mfa_setup_needed) && !portalViewingAs(); $remembered = $session_mfa_on ? portalRememberedBrowsers($session_user_id) : 0; $sees_company = $session_contact_primary || $session_contact_is_technical_contact; // they see every ticket at their company // Setting up two-step sign-in: one secret, kept until a code from it is checked $mfa_secret = ''; $mfa_qr = ''; if ($mfa_setup && !portalViewingAs()) { if (empty($_SESSION['portal_mfa_secret'])) { $_SESSION['portal_mfa_secret'] = generateTotpSecret(); } $mfa_secret = $_SESSION['portal_mfa_secret']; $mfa_qr = portalMfaQrSvg($mfa_secret, $me['contact_email'] ?? ''); } $roles = portalContactRoles($me ?: []); $location = ''; if (!empty($me['location_name'])) { $location = escapeHtml($me['location_name']); $address = formatAddress($me['location_address'], $me['location_city'], $me['location_state'], $me['location_zip'], $me['location_country']); if (trim(strip_tags((string) $address)) !== '') { $location .= '
' . nl2br(escapeHtml($address)) . '
'; } } $facts = [ 'Name' => escapeHtml($me['contact_name'] ?? ''), 'Email' => escapeHtml($me['contact_email'] ?? '') . ($password_account || $microsoft_account ? '
You sign in with it
' : ''), ]; if (trim((string) ($me['contact_title'] ?? '')) !== '') { $facts['Job title'] = escapeHtml($me['contact_title']); } if (trim((string) ($me['contact_department'] ?? '')) !== '') { $facts['Department'] = escapeHtml($me['contact_department']); } $facts['Company'] = escapeHtml($session_client_name); if ($location !== '') { $facts['Location'] = $location; } $facts['Roles'] = $roles ? '' . implode('', array_map(function ($role) { return '' . escapeHtml($role) . ''; }, $roles)) . '' : 'None - you see your own tickets'; $sql_logins = mysqli_query($mysqli, "SELECT log_created_at, log_ip, log_user_agent FROM logs WHERE log_type = 'Client Login' AND log_action = 'Success' AND log_user_id = $session_user_id AND log_client_id = $session_client_id AND $session_user_id > 0 ORDER BY log_id DESC LIMIT 5"); $phone_input = ($me['contact_phone'] ?? '') !== '' ? formatPhoneNumber($me['contact_phone'], $me['contact_phone_country_code'], false) : ''; $mobile_input = ($me['contact_mobile'] ?? '') !== '' ? formatPhoneNumber($me['contact_mobile'], $me['contact_mobile_country_code'], false) : ''; $ticket_pref = isset(PORTAL_TICKET_EMAILS[$me['contact_ticket_emails'] ?? '']) ? $me['contact_ticket_emails'] : 'all'; $company_pref = isset(PORTAL_COMPANY_TICKET_EMAILS[$me['contact_company_ticket_emails'] ?? '']) ? $me['contact_company_ticket_emails'] : 'none'; $csrf = escapeHtml($_SESSION['csrf_token']); ?>

Your account

Your details, the emails you get from us and how you sign in

asks everyone to use two-step sign-in. Set it up below to carry on - it takes a minute with an authenticator app on your phone.

Your details

Ask us to change them
$value) { ?>

Your phone numbers

So we can reach you about a ticket without hunting for a number. Clear one to remove it.

Emails from us

About your tickets $option) { ?>
>
About other tickets at $option) { ?>
>

Invoices, quotes, receipts and statements go to 's billing contacts - ask us if they should go somewhere else.

Signing in

You sign in with your Microsoft 365 account - Microsoft looks after your password and its own sign-in checks.

There is no client portal sign-in for this contact.

  • Password At least characters
  • Two-step sign-in Turn on
  1. Open an authenticator app on your phone - Microsoft or Google Authenticator, 1Password, Authy, Bitwarden - and add an account.
  2. Scan this code:
Can't scan it? Type this key in:
  1. Enter the 6-digit code it shows:
Not now

Signing in will ask for your password alone.

asks everyone to use it, so it stays on. Lost your phone? Ask us to reset it.
browser the code for day after signing in

Phone PIN

We ask for it to make sure it is really you when you call. ' . escapeHtml($me['contact_pin']) . '.' ?>

Recent sign-ins

All your activity
No sign-ins yet.
Somewhere you don't recognise? Change your password and let us know.