MattermostOidcIntegration.java
package com.cloudforge.core.oidc;
import com.cloudforge.core.interfaces.Ec2Context;
import com.cloudforge.core.interfaces.OidcConfiguration;
import com.cloudforge.core.interfaces.OidcIntegration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* OIDC integration for Mattermost using native OpenID Connect.
*
* <p><strong>Why Use Native OIDC (vs GitLab OAuth):</strong></p>
* <ul>
* <li>Uses discovery endpoint for automatic configuration</li>
* <li>Proper single logout support via end_session_endpoint</li>
* <li>Standard OpenID Connect 1.0 compliance</li>
* <li>Works directly with AWS Cognito User Pools</li>
* </ul>
*
* <p><strong>Limitations vs SAML:</strong></p>
* <ul>
* <li>No automatic group synchronization from IdP</li>
* <li>No AD/LDAP sync integration</li>
* <li>Manual team/channel membership management required</li>
* </ul>
*
* <p><strong>License Requirement:</strong></p>
* <p>Native OpenID Connect requires Mattermost Enterprise or Professional.
* For Team Edition (free), use GitLab OAuth instead.</p>
*
* <p><strong>Supported Providers:</strong></p>
* <ul>
* <li>Amazon Cognito User Pools (oidcProvider: "cognito")</li>
* <li>Any OIDC-compliant provider with discovery endpoint</li>
* </ul>
*
* @see <a href="https://docs.mattermost.com/onboard/sso-openidconnect.html">Mattermost OpenID Connect SSO</a>
*/
public class MattermostOidcIntegration implements OidcIntegration {
@Override
public boolean isSupported() {
return true;
}
@Override
public String getIntegrationMethod() {
return "Native OpenID Connect (configured via MM_OPENIDSETTINGS_* environment variables)";
}
@Override
public Map<String, String> getEnvironmentVariables(OidcConfiguration config) {
Map<String, String> env = new HashMap<>();
// Use native OpenID Connect settings (requires Enterprise/Professional license)
env.put("MM_OPENIDSETTINGS_ENABLE", "true");
env.put("MM_OPENIDSETTINGS_ID", config.getClientId());
// Client secret is injected by ContainerFactory as MM_OPENIDSETTINGS_SECRET
// from ECS secrets (MATTERMOST_OIDC_CLIENT_SECRET mapped to MM_OPENIDSETTINGS_SECRET)
// DO NOT set it here - it will be added as an ECS secret by ContainerFactory
// Discovery endpoint for automatic OIDC configuration
// Cognito: https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/openid-configuration
String issuer = config.getIssuerUrl();
if (issuer != null && !issuer.isEmpty()) {
String discoveryEndpoint = issuer.endsWith("/")
? issuer + ".well-known/openid-configuration"
: issuer + "/.well-known/openid-configuration";
env.put("MM_OPENIDSETTINGS_DISCOVERYENDPOINT", discoveryEndpoint);
}
// Site URL (required for OAuth redirects)
String siteUrl = getEffectiveSiteUrl(config);
env.put("MM_SERVICESETTINGS_SITEURL", siteUrl);
// Scopes - openid is required, profile and email provide user info
env.put("MM_OPENIDSETTINGS_SCOPE", "openid profile email");
// Login button customization
String buttonText = "Sign in with AWS Cognito";
if (config.getProviderType() != null) {
if (config.getProviderType().equals("identity-center")) {
buttonText = "Sign in with AWS IAM Identity Center";
}
}
env.put("MM_OPENIDSETTINGS_BUTTONTEXT", buttonText);
env.put("MM_OPENIDSETTINGS_BUTTONCOLOR", "#FF9900"); // AWS orange color
return env;
}
@Override
public String getConfigurationFile(OidcConfiguration config) {
// Mattermost uses environment variables, not config files
return null;
}
@Override
public String getConfigurationFilePath() {
// Not used - configuration via environment variables
return null;
}
@Override
public List<String> getUserDataCommands(OidcConfiguration config, Ec2Context context) {
List<String> commands = new ArrayList<>();
commands.add("# Mattermost OIDC configured via environment variables");
commands.add("echo 'Mattermost OIDC integration active' >> /var/log/userdata.log");
return commands;
}
@Override
public String getContainerStartupCommand() {
// Mattermost uses a Go binary, not a shell script
// The official image is distroless (no /bin/sh)
return "/mattermost/bin/mattermost";
}
@Override
public String getOidcCallbackPath() {
// Native OpenID Connect uses /signup/openid/complete
return "/signup/openid/complete";
}
@Override
public boolean supportsCognito() {
// Full support for Cognito OIDC
return true;
}
@Override
public boolean supportsIdentityCenterSaml() {
// This is OIDC integration, not SAML
// Identity Center supports OIDC, so technically yes, but for SAML use MattermostSamlIntegration
return false;
}
@Override
public String getAuthenticationType() {
return "OIDC";
}
@Override
public String getPostDeploymentInstructions() {
return """
Mattermost OIDC Integration Completed
======================================
1. Access Mattermost at your configured domain
2. Click "Sign in with AWS Cognito" on the login page
3. You will be redirected to Cognito for authentication
4. After authentication, a Mattermost account will be created automatically
User Management:
- Users are auto-created on first OIDC login
- Email addresses from Cognito are used for Mattermost accounts
- Team membership must be managed manually (no automatic group sync)
Granting Admin Privileges:
Via Mattermost CLI:
docker exec -it <mattermost-container> mattermost user --email user@example.com --system-admin
Or via System Console:
System Console > User Management > Users > [Select User] > Make System Admin
Limitations (compared to SAML):
- No automatic group/team synchronization
- No AD/LDAP integration for team membership
- Manual role management required
For automatic group sync with SAML, consider:
- oidcProvider: "cognito-saml" (deploys Keycloak as SAML bridge)
- oidcProvider: "identity-center" (uses AWS IAM Identity Center SAML)
""";
}
/**
* Gets the effective site URL for Mattermost.
* Uses applicationUrl if available, otherwise derives from redirectUrl.
*/
private String getEffectiveSiteUrl(OidcConfiguration config) {
String appUrl = config.getApplicationUrl();
if (appUrl != null && !appUrl.isEmpty()) {
return appUrl;
}
// Derive from redirect URL by removing the callback path
String redirectUrl = config.getRedirectUrl();
if (redirectUrl != null && !redirectUrl.isEmpty()) {
String callbackPath = "/signup/openid/complete";
if (redirectUrl.endsWith(callbackPath)) {
return redirectUrl.substring(0, redirectUrl.length() - callbackPath.length());
}
}
return redirectUrl;
}
}