Skip to main content

Pre-authentication secrets

From Mattermost server v10.12 and mobile v2.32, Mattermost deployments can use a reverse proxy to validate authentication secrets before allowing desktop and mobile requests to reach the Mattermost server. This adds an additional security layer by checking for the X-Mattermost-Preauth-Secret header.

Authentication secrets are only supported for mobile and desktop applications. When a secret is required, desktop prompts users directly while mobile displays the secret field in Advanced Options. Web browser clients don't support this feature.

When authentication secret validation fails, the reverse proxy must return the X-Reject-Reason: pre-auth header along with the 403 status code. This header allows mobile and desktop applications to specifically identify authentication failures and provide appropriate error messaging to users.

NGINX configuration example​

Here's an example partial NGINX configuration that validates the authentication secret header. It builds on the HTTPS server in Set up an NGINX proxy. Each ... stands for the proxy lines, timeouts and limits of the matching location in that page.

server {
... # listen, TLS and server_name as in the NGINX guide

set $expected_secret "your-secure-auth-secret-here"; # the secret that clients must send

# Whitelisted endpoints where the secret may not be available
# Each one needs its own proxy lines, see the notes below

location = /api/v4/notifications/ack {
... # proxy lines as in the NGINX guide
}

location = /api/v4/config/client {
... # proxy lines as in the NGINX guide
}

location = /login/desktop {
... # proxy lines as in the NGINX guide
}

location ^~ /static/ {
... # proxy lines as in the NGINX guide
}

# Additional whitelisted endpoints based on your authentication setup
# Uncomment and configure as needed for your setup, with the same proxy lines as above
# Note: Replace {service:[A-Za-z0-9]+} with your specific service names
# (e.g., google, gitlab, openid, office365) or use the regex pattern for multiple services

# SAML
# location = /login/sso/saml {
# # ...
# }

# OpenID/OAuth patterns (use regex for multiple services)
# location ~ ^/oauth/[A-Za-z0-9]+/(complete|login|mobile_login|signup)$ {
# # ...
# }
# Or for specific services:
# location ~ ^/oauth/(google|gitlab|office365)/(complete|login|mobile_login|signup)$ {
# # ...
# }
# location ~ ^/api/v3/oauth/[A-Za-z0-9]+/complete$ {
# # ...
# }
# location ~ ^/(signup|login)/[A-Za-z0-9]+/complete$ {
# # ...
# }

location ~ /api/v[0-9]+/websocket/?$ {
# Check if X-Mattermost-Preauth-Secret header matches expected value
if ($http_x_mattermost_preauth_secret != $expected_secret) {
add_header X-Reject-Reason pre-auth always; # lets apps show a pre-auth error
add_header Cache-Control "no-store" always; # never cache the rejection
# repeat HSTS, because add_header here hides the server-level one
add_header Strict-Transport-Security "max-age=63072000" always;
return 403 "Forbidden: Invalid pre-authentication secret"; # reject before proxying
}
... # proxy lines as in the NGINX guide
}

location / {
# Check if X-Mattermost-Preauth-Secret header matches expected value
if ($http_x_mattermost_preauth_secret != $expected_secret) {
add_header X-Reject-Reason pre-auth always; # lets apps show a pre-auth error
add_header Cache-Control "no-store" always; # never cache the rejection
# repeat HSTS, because add_header here hides the server-level one
add_header Strict-Transport-Security "max-age=63072000" always;
return 403 "Forbidden: Invalid pre-authentication secret"; # reject before proxying
}
... # proxy lines as in the NGINX guide
}
}

Keep the following in mind:

  • Check the secret in the WebSocket location too. NGINX sends WebSocket requests to their own location, so a check only in location / doesn't apply to them.
  • Mattermost under a subpath: If you added the location /sub-path block from the NGINX FAQ, add the same secret check to it, and prefix each whitelisted location with the subpath, for example location = /sub-path/api/v4/config/client.
  • Other proxying locations: Add the same secret check to every other location that proxies to Mattermost. NGINX matches regular expression locations before location /, so those requests skip its check.

Replace your-secure-auth-secret-here with a strong, unique secret that will be configured in your mobile and desktop applications. Store this secret securely and rotate it regularly as part of your security practices.

For Apache2 configuration guidance, see Configuring Apache2 as a proxy for Mattermost Server.

Rotate secrets​

To rotate an authentication secret:

  1. Generate a new secret: openssl rand -base64 32.
  2. Update your reverse proxy configuration with the new secret.
  3. Reload the reverse proxy.
  4. Notify users:
  • Desktop users: No action needed. They'll be automatically prompted for the new secret on their next connection attempt.
  • Mobile users: Manually update the secret by editing the server in the app by tapping Servers, swiping left, tapping Edit > Advanced Options and updating the Authentication secret.

Security considerations​

The Mattermost desktop application stores authentication secrets using Electron's safeStorage API, which integrates with the operating system's secure credential storage:

  • Windows: Windows Credential Manager
  • macOS: Keychain Access
  • Linux: Secret Service API (requires kwallet, gnome-libsecret, or compatible backend)

For more information about Electron's secure storage behavior, see the Electron safeStorage documentation.