Corporate Identity โ SAML & SCIM
Enable enterprise-grade single sign-on (SSO) and automated user provisioning for Organizations on Mallnline. Malet Owners with corporate teams can connect their existing Identity Provider โ Okta, Azure AD, Google Workspace, or any SAML 2.0 / SCIM 2.0 compliant provider โ to automate member lifecycle management.
How It Works
Corporate Identity has two complementary protocols that work together:
| Protocol | Purpose | Where |
|---|---|---|
| SAML 2.0 | Single Sign-On โ Visitors click "Login with SSO" and authenticate via their corporate IdP | Auth service |
| SCIM 2.0 | Automated Provisioning โ IdP pushes user creates, updates, and deactivations to Mallnline | SCIM service |
End-to-End Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Identity Provider (IdP) โ
โ (Okta / Azure AD / Google Workspace) โ
โโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ SAML Assertion โ SCIM REST API
โ (interactive login) โ (automated sync)
โผ โผ
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ auth :3008 โ โ scim :3019 โ
โ โ โ โ
โ SAML ACS โ โ User CRUD โ
โ Session โ โ Group CRUD โ
โ Creation โ โ Discovery โ
โโโโโโโโฌโโโโโโโโ โโโโโโโโฌโโโโโโโโ
โ โ
โ creates session โ creates/updates users
โ + audit event โ + org memberships
โผ โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Shared Platform Identity โ
โ Postgres (users) + MongoDB (memberships) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
SAML 2.0 โ Single Sign-On
Configuration
Organization admins configure SAML via the Auth subgraph's GraphQL API:
mutation ConfigureSaml($orgId: ID!, $input: ConfigureSamlInput!) {
configureSaml(orgId: $orgId, input: $input) {
orgId
entityId
ssoUrl
certificate # masked preview (first/last 20 chars)
attributeMapping {
email
firstName
lastName
}
}
}
Input:
{
"orgId": "org-enterprise-01",
"input": {
"entityId": "https://idp.corp.example.com/saml/metadata",
"ssoUrl": "https://idp.corp.example.com/saml/sso",
"certificate": "-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----",
"attributeMapping": {
"email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
"firstName": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname",
"lastName": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"
}
}
}
SAML Endpoints
| Method | Path | Description |
|---|---|---|
POST |
/auth/saml/:org_id/acs |
Assertion Consumer Service โ receives IdP SAML assertion, creates session, redirects to platform |
GET |
/auth/saml/:org_id/metadata |
SP Metadata โ XML metadata for configuring the IdP |
Login Flow
- Visitor clicks "Login with SSO" on the platform
- Platform redirects to the IdP's SSO URL with a SAML AuthnRequest
- Visitor authenticates with their corporate credentials (password, MFA, etc.)
- IdP posts a SAML assertion to
/auth/saml/:org_id/acs - Auth service:
- Parses and validates the SAML assertion (Base64 โ XML โ NameID extraction)
- Finds or creates the platform user by email
- Creates or reuses a device record for the session
- Issues session + refresh tokens (same dual-token strategy as MFA & Passkeys)
- Logs a
Samlaudit event
- Visitor is redirected to the platform with active session cookies
Attribute Mapping
The SAML assertion contains user attributes. The attributeMapping configuration tells the auth service which XML attributes correspond to which platform fields:
| Platform Field | Default SAML Attribute | Customizable |
|---|---|---|
email |
NameID or emailaddress claim |
โ |
firstName |
givenname claim |
โ |
lastName |
surname claim |
โ |
If no attribute mapping is configured, the auth service falls back to extracting the NameID element from the assertion as the user's email.
Managing SAML Configuration
# Update an existing config
mutation {
updateSamlConfig(orgId: "org-01", input: { ssoUrl: "https://new-idp.com/sso" }) {
orgId
ssoUrl
}
}
# Delete a config (disables SSO for the org)
mutation {
deleteSamlConfig(orgId: "org-01")
}
# Test the config (validates certificate + connectivity)
mutation {
testSamlConfig(orgId: "org-01") {
success
message
}
}
# View config (certificate is masked for security)
query {
samlConfig(orgId: "org-01") {
entityId
ssoUrl
certificate
attributeMapping {
email
firstName
lastName
}
}
}
SCIM 2.0 โ Automated Provisioning
SCIM handles the automated lifecycle management of users and groups. When an IT admin adds, updates, or removes a user in their IdP, the change is automatically reflected in Mallnline.
For full REST API reference, see the SCIM Subgraph documentation.
Token Management
SCIM tokens are generated via the Auth GraphQL API and scoped to a single Organization:
# Generate a new SCIM token (raw value shown ONCE)
mutation {
generateScimToken(orgId: "org-enterprise-01", label: "Okta Provisioning") {
token # โ copy this immediately โ never shown again
id
label
}
}
# List active tokens (no raw values)
query {
scimTokens(orgId: "org-enterprise-01") {
id
label
lastUsedAt
createdAt
}
}
# Revoke a token (immediate)
mutation {
revokeScimToken(orgId: "org-enterprise-01", tokenId: "token-id")
}
Provisioning Operations
| IdP Action | SCIM Operation | Platform Effect |
|---|---|---|
| Assign user to app | POST /scim/v2/Users |
Creates platform user + Organization membership |
| Update user profile | PUT /scim/v2/Users/:id |
Updates email, display name |
| Deactivate user | PATCH active=false |
Removes user from Organization (preserves account) |
| Remove user from app | DELETE /scim/v2/Users/:id |
Same as deactivate |
| Create group | POST /scim/v2/Groups |
Creates a Team in the Organization |
| Update group membership | PATCH /scim/v2/Groups/:id |
Adds/removes Team members |
| Delete group | DELETE /scim/v2/Groups/:id |
Deletes the Team |
Membership Tracking
When users are provisioned via SCIM, their Organization membership is tagged with provisionedBy: "scim":
query {
organization(id: "org-enterprise-01") {
members {
userId
role
provisionedBy # "scim" | "saml" | "manual"
}
}
}
This allows Organization admins to see which members were added manually vs. automatically by the IdP.
Organization Extensions
Corporate Identity adds several fields and capabilities to the Organizations subgraph:
New Fields
| Entity | Field | Type | Description |
|---|---|---|---|
Team |
externalId |
String |
IdP group identifier (sparse unique index) |
Membership |
provisionedBy |
String |
Source: "saml", "scim", or "manual" |
New Permission
| Permission | Auto-Granted To | Purpose |
|---|---|---|
MANAGE_SSO |
OWNER, ADMIN | Configure SAML, generate/revoke SCIM tokens |
Audit Events
All corporate identity operations are logged via the Organization Audit Trail:
| Event Type | Trigger |
|---|---|
SAML_CONFIG_CREATED |
SAML configuration added |
SAML_CONFIG_UPDATED |
SAML configuration changed |
SAML_CONFIG_DELETED |
SAML configuration removed |
SAML_LOGIN |
Visitor authenticated via SAML SSO |
SCIM_TOKEN_GENERATED |
New SCIM token created |
SCIM_TOKEN_REVOKED |
SCIM token revoked |
SCIM_USER_PROVISIONED |
User created via SCIM |
SCIM_USER_UPDATED |
User updated via SCIM |
SCIM_USER_DEPROVISIONED |
User deactivated via SCIM |
SCIM_GROUP_SYNCED |
Group membership synced via SCIM |
Security Model
SAML Security
- Certificates: IdP X.509 certificates are stored as-is (they are public keys, not secrets)
- Assertion validation: Base64 โ XML parsing with element extraction and signature verification
- Session creation: Uses the same dual-token strategy as all other auth methods
- Audit trail: Every SAML login is logged with
AuthMethod::Saml
SCIM Security
- Zero-knowledge tokens: Raw tokens are never stored โ only SHA-256 hashes
- Per-org isolation: Each token is scoped to exactly one Organization
- Instant revocation: Revoked tokens are excluded immediately via partial index
- Usage tracking:
last_used_atupdated on every authenticated request - No gateway: SCIM traffic does not pass through the Apollo gateway โ direct to the SCIM service
File Reference
| File | Purpose |
|---|---|
apps/auth/src/models/saml.rs |
SAML config + SCIM token models |
apps/auth/src/services/saml.rs |
SamlService + ScimTokenService |
apps/auth/src/routes/saml.rs |
SAML ACS + SP metadata endpoints |
apps/auth/src/graphql/mutation.rs |
SAML + SCIM token mutations |
apps/auth/src/graphql/query.rs |
SAML config + SCIM token queries |
apps/auth/migrations/20260408000000_saml_scim.sql |
saml_configs + scim_tokens tables |
apps/scim/ |
Dedicated SCIM 2.0 REST service |
apps/organizations/src/audit/org-audit-event.entity.ts |
Corporate identity audit events |
apps/organizations/src/team/team.entity.ts |
externalId for SCIM group mapping |
apps/organizations/src/membership/membership.entity.ts |
provisionedBy field |
Testing
| Suite | Tests | Service |
|---|---|---|
| SAML assertion parsing | 6 | Auth |
| SAML model serialization | 4 | Auth |
| SCIM token hashing | 6 | Auth |
| SCIM types + filters | 11 | SCIM |
| SCIM discovery | 1 | SCIM |
| Service provider config | 1 | SCIM |
| Total | 29 | โ |
# Auth SAML tests
cd apps/auth && cargo test saml
# SCIM service tests
cd apps/scim && cargo test
Related
- SCIM โ Provisioning Engine โ Full REST API reference for the SCIM 2.0 service
- Auth & Identity โ Identity management service that hosts SAML endpoints and SCIM token management
- MFA & Passkeys โ Other authentication methods (TOTP, WebAuthn) that complement SAML SSO
- Organizations & Permissions โ Membership, roles, and teams that Corporate Identity provisions into
- Teams & Sub-Groups โ SCIM Groups map to Teams via the
externalIdfield - Custom RBAC โ Custom roles that can be assigned to SCIM-provisioned members
- SIEM Event Streaming โ Stream corporate identity audit events (SAML logins, SCIM provisioning) to external SIEM platforms