Skip to main content

Overview

Grantex provides enterprise-grade SSO that supports OIDC, SAML 2.0, and LDAP protocols. You can configure multiple identity provider connections per organization, automatically route users to the correct IdP by email domain, and map IdP groups to Grantex scopes. Key capabilities:
FeatureDescription
Multi-IdP connectionsConfigure multiple OIDC, SAML 2.0, and LDAP providers per org
OIDC DiscoveryAutomatic endpoint discovery via .well-known/openid-configuration
ID token verificationCryptographic JWT signature verification via JWKS
SAML 2.0Response parsing with X.509 certificate-based signature verification
LDAP / Active DirectoryDirect bind authentication against LDAP directories
Domain routingRoute users to the correct IdP based on email domain
JIT provisioningAuto-create principals on first SSO login
Group mappingMap IdP groups/roles to Grantex scopes
SSO enforcementRequire SSO for all users in an organization
Session managementTrack, list, and revoke active SSO sessions

Setting up OIDC

Step 1: Register Grantex in your IdP

In your identity provider (Okta, Azure AD, Google Workspace, Auth0, etc.), create a new application:
  • Application type: Web application
  • Sign-in redirect URI: https://yourapp.com/sso/callback/oidc
  • Scopes: openid email profile
  • Grant type: Authorization Code
Note the Client ID, Client Secret, and Issuer URL.

Step 2: Create the SSO connection

const connection = await grantex.sso.createConnection({
  name: 'Okta Production',
  protocol: 'oidc',
  issuerUrl: 'https://mycompany.okta.com',
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  domains: ['mycompany.com'],
  jitProvisioning: true,
  groupAttribute: 'groups',
  groupMappings: {
    Engineering: ['read', 'write', 'deploy'],
    Admins: ['admin', 'read', 'write'],
  },
  defaultScopes: ['read'],
});

Step 3: Test the connection

const result = await grantex.sso.testConnection(connection.id);
// result.success === true
// result.issuer, result.authorizationEndpoint, result.tokenEndpoint, result.jwksUri

Step 4: Handle the login flow

// Get the authorize URL (domain-based routing)
const { authorizeUrl } = await grantex.sso.getLoginUrl('dev_01HXYZ...', 'mycompany.com');
// Redirect user to authorizeUrl

// After IdP redirects back, handle the callback
const result = await grantex.sso.handleOidcCallback({
  code: req.query.code,
  state: req.query.state,
  redirect_uri: 'https://yourapp.com/sso/callback/oidc',
});

console.log(result.email);        // 'alice@mycompany.com'
console.log(result.mappedScopes); // ['read', 'write', 'deploy']
console.log(result.sessionId);    // 'ssosess_01HXYZ...'
console.log(result.principalId);  // 'scimuser_01HXYZ...' (JIT-provisioned)

Setting up SAML 2.0

Step 1: Configure your IdP

In your SAML identity provider:
  • SP Entity ID: urn:grantex:yourorg
  • ACS URL: https://yourapp.com/sso/callback/saml
  • NameID format: Email address or persistent identifier
  • Attributes: Map email, displayName, and groups
Download the IdP certificate and note the SSO URL and Entity ID.

Step 2: Create the SAML connection

const connection = await grantex.sso.createConnection({
  name: 'Azure AD SAML',
  protocol: 'saml',
  idpEntityId: 'https://sts.windows.net/tenant-id/',
  idpSsoUrl: 'https://login.microsoftonline.com/tenant-id/saml2',
  idpCertificate: '-----BEGIN CERTIFICATE-----\nMIIC...\n-----END CERTIFICATE-----',
  spEntityId: 'urn:grantex:yourorg',
  spAcsUrl: 'https://yourapp.com/sso/callback/saml',
  domains: ['yourorg.com'],
  jitProvisioning: true,
  groupMappings: {
    SecurityTeam: ['admin', 'audit'],
    Developers: ['read', 'write'],
  },
});

Step 3: Handle the SAML callback

// After the IdP POSTs the SAML response:
const result = await grantex.sso.handleSamlCallback({
  SAMLResponse: req.body.SAMLResponse,
  RelayState: req.body.RelayState,
});

Setting up LDAP

LDAP authentication differs from OIDC and SAML in that there is no browser redirect. Instead, the user submits their credentials directly to your application, and Grantex performs a direct bind against the LDAP directory server to verify them. This makes LDAP ideal for backend services, CLI tools, and environments where browser-based flows are not practical.

Step 1: Gather LDAP server details

You will need the following from your directory administrator:
  • LDAP URL — The server address (e.g. ldaps://ldap.mycompany.com:636 for TLS or ldap://ldap.mycompany.com:389)
  • Bind DN — The distinguished name used for the initial bind to search for users (e.g. cn=readonly,dc=mycompany,dc=com)
  • Bind password — The password for the bind DN
  • Search base — The base DN to search for user entries (e.g. ou=people,dc=mycompany,dc=com)
  • User attribute — The attribute used to match the username (e.g. uid for OpenLDAP, sAMAccountName for Active Directory)
  • Group search base — (Optional) The base DN for group lookups (e.g. ou=groups,dc=mycompany,dc=com)
Always use ldaps:// (LDAP over TLS) in production. Unencrypted LDAP transmits passwords in plaintext.

Step 2: Create the LDAP connection

const connection = await grantex.sso.createConnection({
  name: 'Corporate LDAP',
  protocol: 'ldap',
  ldapUrl: 'ldaps://ldap.mycompany.com:636',
  ldapBindDn: 'cn=readonly,dc=mycompany,dc=com',
  ldapBindPassword: 'bind-password',
  ldapSearchBase: 'ou=people,dc=mycompany,dc=com',
  ldapUserAttribute: 'uid',
  ldapGroupSearchBase: 'ou=groups,dc=mycompany,dc=com',
  domains: ['mycompany.com'],
  jitProvisioning: true,
  groupMappings: {
    'cn=engineering,ou=groups,dc=mycompany,dc=com': ['read', 'write', 'deploy'],
    'cn=admins,ou=groups,dc=mycompany,dc=com': ['admin', 'read', 'write'],
  },
  defaultScopes: ['read'],
});

Step 3: Handle LDAP authentication

Since LDAP does not use browser redirects, the user submits credentials directly to your application. Your backend then calls the LDAP callback endpoint:
// User submits username/password via your login form
const result = await grantex.sso.handleLdapCallback({
  username: req.body.username,
  password: req.body.password,
  connectionId: connection.id,
});

console.log(result.email);        // 'alice@mycompany.com'
console.log(result.name);         // 'Alice Smith'
console.log(result.mappedScopes); // ['read', 'write', 'deploy']
console.log(result.sessionId);    // 'ssosess_01HXYZ...'
console.log(result.principalId);  // 'scimuser_01HXYZ...' (JIT-provisioned)
LDAP authentication performs a two-step process: first, the service account binds and searches for the user entry by the configured user attribute; then, a second bind is attempted with the user’s own DN and password to verify their credentials.

Domain-based routing

When you assign domains to connections, Grantex automatically routes users to the correct IdP:
// Connection A: domains ['engineering.co']  -> Okta OIDC
// Connection B: domains ['marketing.co']    -> Azure AD SAML

// User with @engineering.co email -> routed to Okta
const login = await grantex.sso.getLoginUrl('dev_01HXYZ...', 'engineering.co');
// login.protocol === 'oidc', login.connectionId === Connection A's ID

// User with @marketing.co email -> routed to Azure AD
const login2 = await grantex.sso.getLoginUrl('dev_01HXYZ...', 'marketing.co');
// login2.protocol === 'saml', login2.connectionId === Connection B's ID

Group-to-scope mapping

Map identity provider groups to Grantex scopes:
await grantex.sso.createConnection({
  // ...
  groupAttribute: 'groups',    // OIDC claim name or SAML attribute name
  groupMappings: {
    'Engineering':    ['read', 'write', 'deploy'],
    'Administrators': ['admin', 'read', 'write', 'delete'],
    'ReadOnly':       ['read'],
  },
  defaultScopes: ['read'],  // Fallback when no groups match
});
When a user logs in with the groups ['Engineering', 'ReadOnly'], they receive the union of matched scopes: ['read', 'write', 'deploy'].

JIT provisioning

When jitProvisioning is enabled, Grantex automatically creates or updates a principal (SCIM user) on each SSO login. This eliminates the need for manual user provisioning:
  1. User authenticates via SSO
  2. Grantex checks if a SCIM user exists with the IdP’s subject identifier
  3. If not found, creates a new SCIM user with the IdP’s email and display name
  4. If found, updates the existing user’s details
  5. Returns the principalId in the callback result

SSO enforcement

Require all users in your organization to authenticate via SSO:
// Enable SSO enforcement
await grantex.sso.setEnforcement({ enforce: true });

// Disable SSO enforcement
await grantex.sso.setEnforcement({ enforce: false });
Enabling SSO enforcement marks all active connections as enforced. Ensure you have at least one active, tested SSO connection before enabling.

Session management

Track and manage active SSO sessions:
// List all active sessions
const { sessions } = await grantex.sso.listSessions();

for (const session of sessions) {
  console.log(session.email, session.groups, session.expiresAt);
}

// Revoke a specific session
await grantex.sso.revokeSession(sessions[0].id);
Sessions expire after 8 hours by default.

CLI reference

# Create an OIDC connection
grantex sso connections create --name "Okta" --protocol oidc \
  --issuer-url https://mycompany.okta.com \
  --client-id $CLIENT_ID --client-secret $CLIENT_SECRET \
  --domains mycompany.com --jit-provisioning

# Create a SAML connection
grantex sso connections create --name "Azure AD" --protocol saml \
  --idp-entity-id "https://sts.windows.net/tenant-id/" \
  --idp-sso-url "https://login.microsoftonline.com/tenant-id/saml2" \
  --idp-certificate "$(cat idp-cert.pem)" \
  --sp-entity-id "urn:grantex:mycompany" \
  --sp-acs-url "https://myapp.com/sso/callback/saml"

# Create an LDAP connection
grantex sso connections create --name "Corporate LDAP" --protocol ldap \
  --ldap-url ldaps://ldap.mycompany.com:636 \
  --ldap-bind-dn "cn=readonly,dc=mycompany,dc=com" \
  --ldap-bind-password "$LDAP_BIND_PW" \
  --ldap-search-base "ou=people,dc=mycompany,dc=com" \
  --ldap-user-attribute uid \
  --domains mycompany.com --jit-provisioning

# List all connections
grantex sso connections list

# Test a connection
grantex sso connections test sso_01HXYZ...

# Enable SSO enforcement
grantex sso enforce --enable

# List active sessions
grantex sso sessions list

# Revoke a session
grantex sso sessions revoke ssosess_01HXYZ...

Supported identity providers

ProviderOIDCSAML 2.0LDAP
OktaYesYesYes
Azure AD / Entra IDYesYesYes
Google WorkspaceYesYes-
Auth0YesYes-
OneLoginYesYesYes
PingFederateYesYesYes
JumpCloudYesYesYes
OpenLDAP--Yes
FreeIPA--Yes
Apache Directory Server--Yes
Any OIDC-compliant IdPYes--
Any SAML 2.0-compliant IdP-Yes-
Any LDAPv3-compliant server--Yes

API endpoints

EndpointMethodDescription
/v1/sso/connectionsPOSTCreate SSO connection
/v1/sso/connectionsGETList SSO connections
/v1/sso/connections/:idGETGet SSO connection
/v1/sso/connections/:idPATCHUpdate SSO connection
/v1/sso/connections/:idDELETEDelete SSO connection
/v1/sso/connections/:id/testPOSTTest SSO connection
/v1/sso/enforcePOSTSet SSO enforcement
/v1/sso/sessionsGETList SSO sessions
/v1/sso/sessions/:idDELETERevoke SSO session
/sso/loginGETGet IdP authorization URL
/sso/callback/oidcPOSTOIDC callback (with verification)
/sso/callback/samlPOSTSAML callback
/sso/callback/ldapPOSTLDAP bind authentication