IdP-Initiated SAML SSO Integration Guide
Overview
This guide is for third-party organizations integrating their Identity Provider (IdP) with Sequent's voting platform using IdP-initiated SAML 2.0 Single Sign-On (SSO).
What is IdP-Initiated SSO?
In IdP-initiated SSO, users begin their authentication journey at your identity provider and are then redirected to Sequent's voting platform with an established authentication session. This allows your users to:
- Start at your organization's portal or login page
- Authenticate using your existing authentication system
- Be automatically logged into Sequent's voting platform
- Access the voting interface without additional login prompts
Architecture Overview
Integration Approach
This guide uses Sequent's SimpleSAMLphp reference implementation as the primary integration method. This implementation:
- Demonstrates all patterns and configurations needed
- Can be run locally to test against Sequent's staging
- Provides working code you can adapt to your platform
- Shows exactly how to construct URLs and metadata
You will:
- Get the reference implementation working with Sequent staging
- Understand how it works by reviewing the code
- Adapt the patterns to your production IdP (ADFS, Okta, Shibboleth, etc.)
Step 1: Get Configuration from Sequent
Contact Sequent technical support to request your integration parameters. You'll receive:
| Parameter | Description | Example |
|---|---|---|
TENANT_ID | Your organization's tenant UUID | abc12345-6789-... |
EVENT_ID | Your voting event UUID | def67890-1234-... |
SP_BASE_URL | Sequent's authentication service URL | https://login-example.sequent.vote/auth/ |
VOTING_PORTAL_URL | Voting portal base URL | https://voting-example.sequent.vote |
SP_IDP_ALIAS | Your IdP identifier | yourcompany-idp |
SP_CLIENT_ID | SAML client ID | vp-sso |
SP_CERT_DATA | Sequent's public certificate | MIIDOzCCAi... (base64) |
Save these values - you'll use them in the next step.
Step 2: Set Up the Reference Implementation
2.1 Clone and Navigate
# Clone the Step repository
git clone <step-repo-url>
cd step/.devcontainer/
2.2 Configure Environment
Copy the environment template and fill in your values:
cp .env.development .env
Edit .devcontainer/.env with the configuration from Step 1. Note that there
are more environment variables that can be edited, but the most important one
to make this work are the following:
# Your local IdP URLs (for development)
IDP_BASE_URL=https://localhost:8083/simplesaml
# Example users for testing SimpleSAMLphp authentication
# Format: "username1:password1:email1,username2:password2:email2,..."
# You can customize these for your testing needs
SSP_EXAMPLE_USERS=user1:password:user1@example.com,user2:password:user2@example.com
# Sequent configuration (from Step 1)
TENANT_ID=<your-tenant-id>
EVENT_ID=<your-event-id>
SP_BASE_URL=https://login-example.sequent.vote/auth/
SP_IDP_ALIAS=yourcompany-idp
SP_CLIENT_ID=vp-sso
SP_CERT_DATA=<certificate-from-sequent>
VOTING_PORTAL_URL=https://voting-example.sequent.vote
The variables above appear in the .devcontainer/.env file under the section
titled SimpleSAMLphp Id Configuration that starts as it is shown below:
################################################################################
# SimpleSAMLphp IdP Configuration
# These variables configure the SimpleSAMLphp instance as a reference IdP
# implementation for third-party integrators.
# =============================================================================
# Simple SAML PHP General Configuration
# =============================================================================
2.3 Start SimpleSAMLphp
Using Docker (recommended):
cd .devcontainer/
docker-compose up simplesamlphp
2.4 Set Up ngrok Tunnel (Required for Staging Testing)
Why ngrok is needed: Your SimpleSAMLphp runs locally on localhost:8083,
but Sequent's staging environment needs to reach your IdP to fetch metadata and
validate signatures. ngrok creates a public URL that tunnels to your local
instance.
Install ngrok:
# Download from https://ngrok.com/download
# Or install via package manager:
brew install ngrok # macOS
snap install ngrok # Linux
choco install ngrok # Windows
Start ngrok tunnel:
ngrok http https://localhost:8083
You'll see output like:
Forwarding https://abc123.ngrok.io -> https://localhost:8083
Update your configuration with the ngrok URL:
Edit simplesamlphp/.env and change:
# Before:
IDP_BASE_URL=https://localhost:8083/simplesaml
# After (use your ngrok URL):
IDP_BASE_URL=https://abc123.ngrok.io/simplesaml
Restart SimpleSAMLphp to pick up the new configuration:
docker-compose restart simplesamlphp
2.5 Access the Reference Implementation
Once running with ngrok, access:
- Admin interface:
https://abc123.ngrok.io/simplesaml/module.php/admin(use your ngrok URL) - IdP metadata:
https://abc123.ngrok.io/simplesaml/saml2/idp/metadata.php - SSO trigger page:
https://abc123.ngrok.io/simplesaml/idp-initiated-sso.php
Important: Keep ngrok running throughout your testing session. If you restart ngrok, you'll get a new URL and need to update configuration again.
Step 3: Provide Your Metadata to Sequent
Sequent needs your IdP metadata to configure trust. Provide:
- IdP Metadata URL:
https://abc123.ngrok.io/simplesaml/saml2/idp/metadata.php- Or download and send the XML file
- IdP Entity ID: Found in the metadata (will be
https://abc123.ngrok.io/simplesaml/saml2/idp/metadata.php) - IdP SSO URL:
https://abc123.ngrok.io/simplesaml/saml2/idp/SSOService.php - Public Certificate: Found in
.devcontainer/simplesamlphp/cert/server.crt
server.crtWhen you expose SimpleSAMLphp through ngrok, ngrok terminates HTTPS on the public URL and presents its TLS certificate to browsers. That only affects the transport channel. The certificate stored at .devcontainer/simplesamlphp/cert/server.crt is different: SimpleSAMLphp uses it to sign SAML responses/assertions and it is embedded in your IdP metadata. Sequent needs this signing certificate (not the ngrok TLS cert) to validate every message your IdP issues, regardless of which tunnel or proxy you use.
For testing: Sequent will configure their staging environment to accept assertions from your local SimpleSAMLphp instance.
Step 4: Test the Integration
4.1 Test End-to-End Flow
- Open browser to:
https://abc123.ngrok.io/simplesaml/idp-initiated-sso.php - Click "Login to Voting Portal"
- Authenticate with test credentials (as configured in
SSP_EXAMPLE_USERS):- Default: Username:
user1/ Password:password - Or: Username:
user2/ Password:password - You can customize these users by editing the
SSP_EXAMPLE_USERSenvironment variable
- Default: Username:
- You should be redirected to Sequent's staging voting portal
- Verify you're logged in
4.2 Verify the Flow
Use the SAML-tracer Chrome extension to inspect:
- SAML assertion is generated by SimpleSAMLphp
- Assertion includes
emailattribute - Response/Assertion is signed
- POST goes to correct ACS URL
- RelayState is preserved
- Sequent validates and creates session
4.3 Common Test Issues
Issue: "Identity Provider not found"
- Verify Sequent has configured your
SP_IDP_ALIAScorrectly - Check the metadata you provided matches
Issue: "Signature validation failed"
- Verify Sequent has your correct
server.crtcertificate - Check SimpleSAMLphp is signing (see
metadata/saml20-idp-hosted.php)
Issue: "User not created"
- Verify
emailattribute is in the assertion - Check Sequent has configured email attribute mapping
Step 5: Understand How It Works
Now that you have it working, understand the implementation so you can adapt it.
5.1 Centralized Configuration
File: .devcontainer/simplesamlphp/config.php
All configuration in one place, loaded from environment variables:
<?php
return [
// Your IdP configuration
'idp_base_url' => getenv('IDP_BASE_URL') ?: 'https://abc123.ngrok.io/simplesaml',
// Sequent provided values
'tenant_id' => getenv('TENANT_ID') ?: '...',
'event_id' => getenv('EVENT_ID') ?: '...',
'sp_base_url' => getenv('SP_BASE_URL') ?: 'https://login-example.sequent.vote',
'sp_idp_alias' => getenv('SP_IDP_ALIAS') ?: 'yourcompany-idp',
'sp_client_id' => getenv('SP_CLIENT_ID') ?: 'vp-sso',
'sp_cert_data' => getenv('SP_CERT_DATA') ?: 'MII...',
'voting_portal_url' => getenv('VOTING_PORTAL_URL') ?: 'https://voting-example.sequent.vote',
// Automatically computed
'sp_realm' => 'tenant-' . (getenv('TENANT_ID') ?: '...') . '-event-' . (getenv('EVENT_ID') ?: '...'),
];
Key takeaway: All deployment-specific values come from environment variables. No hardcoding.
5.2 Service Provider Metadata
File: .devcontainer/simplesamlphp/metadata/saml20-sp-remote.php
Shows how to configure Sequent as a trusted Service Provider:
<?php
$config = require __DIR__ . '/../config.php';
// Build the ACS URL where Sequent receives SAML responses
$acsUrl = sprintf(
'%s/realms/%s/broker/%s/endpoint/clients/%s',
$config['sp_base_url'], // https://login-example.sequent.vote/auth/
$config['sp_realm'], // tenant-...-event-...
$config['sp_idp_alias'], // yourcompany-idp
$config['sp_client_id'] // vp-sso
);
// The Entity ID is the realm identifier
$metadata[$config['sp_realm']] = [
'AssertionConsumerService' => [
[
'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
'Location' => $acsUrl
],
],
'certData' => $config['sp_cert_data'], // Sequent's public certificate
];
Key takeaway: The ACS URL follows a specific pattern. The SP Entity ID is the realm identifier (tenant-event combination).
5.3 IdP Metadata
File: .devcontainer/simplesamlphp/metadata/saml20-idp-hosted.php
Configures your IdP's behavior:
<?php
$config = require __DIR__ . '/../config.php';
// Your IdP's Entity ID
$entityId = rtrim($config['idp_base_url'], '/') . '/saml2/idp/metadata.php';
$metadata[$entityId] = [
'privatekey' => 'server.pem', // For signing
'certificate' => 'server.crt', // Public certificate
'auth' => 'example-userpass', // How users authenticate
'saml20.sign.response' => true, // Sign SAML response
'saml20.sign.assertion' => true, // Sign assertion
];
Key takeaway: You must sign SAML responses/assertions. Your IdP needs a certificate.
5.4 SSO Trigger Page
File: .devcontainer/simplesamlphp/public/idp-initiated-sso.php
Shows how to initiate the SSO flow:
<?php
$config = require __DIR__ . '/../config.php';
// The SP Entity ID to target
$spEntityId = $config['sp_realm'];
// Build SSO URL with parameters
$ssoUrl = "/simplesaml/saml2/idp/SSOService.php?" . http_build_query([
'spentityid' => $spEntityId, // Which SP to send to
]);
?>
<a href="<?php echo htmlspecialchars($ssoUrl); ?>">Login to Voting Portal</a>
Key takeaway: You need two parameters: spentityid (the realm) and RelayState (voting portal URL).
5.5 User Authentication Configuration
File: .devcontainer/simplesamlphp/config/authsources.php
Shows how to configure test users (for the reference implementation only):
// Parse SSP_EXAMPLE_USERS environment variable
// Format: "username1:password1:email1,username2:password2:email2,..."
$exampleUsersEnv = getenv('SSP_EXAMPLE_USERS') ?: 'user1:password:user1@example.com,user2:password:user2@example.com';
$exampleUsers = [];
foreach (explode(',', $exampleUsersEnv) as $userSpec) {
$parts = explode(':', trim($userSpec), 3);
if (count($parts) === 3) {
$username = $parts[0];
$password = $parts[1];
$email = $parts[2];
$exampleUsers["$username:$password"] = [
'email' => $email,
];
}
}
Key takeaway: Test users are configured via the SSP_EXAMPLE_USERS environment variable. In production, your IdP will use its own authentication mechanism.
5.6 SAML Assertion Requirements
Your IdP must generate assertions with:
Subject:
NameID: User's email addressNameID Format:urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
Conditions:
AudienceRestriction: Must betenant-{TENANT_ID}-event-{EVENT_ID}NotBefore/NotOnOrAfter: 5-minute validity window
Attributes:
email: User's email address (required)
Signing:
- Sign the Response and/or Assertion with RSA-SHA256 or stronger
SimpleSAMLphp handles this automatically when configured correctly.
Step 6: Adapt to Your Production IdP
Now that you understand the reference implementation, adapt it to your production IdP.
For ADFS
- Create a Relying Party Trust in ADFS
- Entity ID:
tenant-{TENANT_ID}-event-{EVENT_ID} - ACS URL:
{SP_BASE_URL}/realms/{SP_REALM}/broker/{SP_IDP_ALIAS}/endpoint/clients/{SP_CLIENT_ID} - Claim Rules: Map user email to SAML attribute named
email - Signing: Enable signing with certificate
- IdP-Initiated SSO: Configure endpoint URL with
spentityidandRelayState
For Okta
- Create a SAML 2.0 App Integration
- Single Sign-On URL: Use ACS URL from above
- Audience URI:
tenant-{TENANT_ID}-event-{EVENT_ID} - Attribute Statements: Map email to
email - Enable IdP-Initiated SSO: In app settings
- Test: Use Okta's IDP-initiated SSO URL with RelayState parameter
For Shibboleth
- Configure
<RelyingParty>inrelying-party.xml - Set entityID to the realm identifier
- Configure
<AttributeResolver>for email attribute - Set up IdP-initiated SSO endpoint with parameters
- Configure signing in metadata
For Custom IdP
Follow the patterns from the reference implementation:
- Configuration: Centralize all values (tenant ID, event ID, URLs)
- Metadata: Build ACS URL using the pattern shown
- Entity ID: Use the realm identifier as SP Entity ID
- SSO URL: Include
spentityidandRelayStateparameters - Assertion: Include email attribute, sign response/assertion
- POST: Send to ACS URL with RelayState preserved
Step 7: Production Checklist
- All URLs updated to production values
- HTTPS enabled on all endpoints
- Production certificates exchanged
- Integration tested in staging environment first
- Email attribute mapping verified
- Signing enabled and tested
- RelayState validation working
- Clock synchronization configured (NTP)
- Monitoring and logging enabled
- Go-live date coordinated with Sequent
Troubleshooting
Common Issues
"SAML Response Signature Invalid"
- Sequent doesn't have your correct public certificate
- You're not signing the response/assertion
- Signing algorithm mismatch
- Fix: Verify certificate exchange, check signing configuration
"Invalid AudienceRestriction"
- Assertion audience doesn't match realm identifier
- Fix: Ensure audience is exactly
tenant-{TENANT_ID}-event-{EVENT_ID}
"User Not Created / Email Missing"
- Email attribute not included in assertion
- Fix: Verify email attribute is present and named
email
"RelayState Not Working"
- RelayState not preserved through flow
- Fix: Ensure IdP passes RelayState in SAML Response POST
"Clock Skew Error"
- Time difference between servers
- Fix: Configure NTP on all servers
Debug Tools
- SAML Tracer (browser extension) - inspect SAML messages
- Sequent logs - request from Sequent support
- Reference implementation - compare with working SimpleSAMLphp
Security Requirements
Critical
- HTTPS in production (mandatory)
- Sign all SAML messages (response and/or assertion)
- Validate signatures (both sides)
- Protect private keys (never commit to git)
- Certificate rotation (monitor expiration)
- Audience restriction (include correct realm)
- Time-based validation (NotBefore/NotOnOrAfter)
- Unique assertion IDs (prevent replay)
- NTP synchronization (prevent clock skew)
Support
Technical Questions: support@sequentech.io
Reference Implementation: .devcontainer/simplesamlphp/ in Step repository
Integration Coordination: Contact Sequent to schedule staging/production setup
Summary
What You Did
- Got configuration values from Sequent
- Set up SimpleSAMLphp reference implementation
- Provided your metadata to Sequent
- Tested integration end-to-end
- Understood how the implementation works
- Adapted patterns to your production IdP
- Deployed to production
Key Files to Reference
config.php- Centralized configuration patternmetadata/saml20-sp-remote.php- How to configure Sequent SPmetadata/saml20-idp-hosted.php- How to configure your IdPpublic/idp-initiated-sso.php- How to initiate SSO
Remember
You don't have to use SimpleSAMLphp in production. The reference implementation shows you the patterns and configuration that work with any SAML 2.0 IdP platform. Adapt them to your technology stack.