Skip to main content

Overview

The sso client provides enterprise-grade single sign-on for your developer organization. It supports multiple identity provider connections (OIDC, SAML 2.0, and LDAP), domain-based enforcement, JIT (just-in-time) provisioning, and session management. Compatible with Okta, Azure AD, Google Workspace, Auth0, OneLogin, PingFederate, OpenLDAP, FreeIPA, and any SAML 2.0, OIDC, or LDAP-compliant provider. Access the SSO client via client.sso.
from grantex import Grantex, CreateSsoConnectionParams

with Grantex(api_key="gx_live_...") as client:
    conn = client.sso.create_connection(CreateSsoConnectionParams(
        name="Okta Production",
        protocol="oidc",
        issuer_url="https://mycompany.okta.com",
        client_id="your-client-id",
        client_secret="your-client-secret",
        domains=["mycompany.com"],
        jit_provisioning=True,
    ))

Enterprise SSO Connections

Create Connection

Create a new SSO identity provider connection. You can create multiple connections for different domains or providers.
from grantex import Grantex, CreateSsoConnectionParams

with Grantex(api_key="gx_live_...") as client:
    conn = client.sso.create_connection(CreateSsoConnectionParams(
        name="Okta Production",
        protocol="oidc",
        issuer_url="https://mycompany.okta.com",
        client_id="your-okta-client-id",
        client_secret="your-okta-client-secret",
        domains=["mycompany.com", "mycompany.org"],
        jit_provisioning=True,
        default_role="member",
    ))

    print(f"ID: {conn.id}")                    # 'sso_conn_01HX...'
    print(f"Name: {conn.name}")                # 'Okta Production'
    print(f"Protocol: {conn.protocol}")        # 'oidc'
    print(f"Status: {conn.status}")            # 'active'
    print(f"Domains: {conn.domains}")          # ['mycompany.com', 'mycompany.org']
    print(f"JIT: {conn.jit_provisioning}")     # True
    print(f"Created: {conn.created_at}")       # '2026-03-29T12:00:00Z'

SAML example

from grantex import CreateSsoConnectionParams

saml_conn = client.sso.create_connection(CreateSsoConnectionParams(
    name="Azure AD SAML",
    protocol="saml",
    metadata_url="https://login.microsoftonline.com/.../federationmetadata/2007-06/federationmetadata.xml",
    assertion_consumer_service_url="https://yourapp.com/sso/saml/callback",
    entity_id="https://yourapp.com/saml/metadata",
    domains=["contoso.com"],
    jit_provisioning=True,
    attribute_mapping={
        "email": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
        "name": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/displayname",
    },
))

CreateSsoConnectionParams

FieldTypeRequiredDescription
namestrYesA human-readable name for this connection.
protocolstrYes"oidc", "saml", or "ldap".
issuer_urlstrOIDC onlyThe OIDC issuer URL.
client_idstrOIDC onlyOAuth 2.0 client ID from your IdP.
client_secretstrOIDC onlyOAuth 2.0 client secret from your IdP.
metadata_urlstrSAML onlyThe SAML metadata URL.
assertion_consumer_service_urlstrSAML onlyThe SAML ACS URL.
entity_idstrSAML onlyThe SAML service provider entity ID.
domainslist[str]NoEmail domains to associate with this connection.
jit_provisioningboolNoEnable just-in-time user provisioning. Defaults to False.
default_rolestrNoDefault role for JIT-provisioned users.
attribute_mappingdict[str, str]NoCustom attribute mapping for SAML assertions.

SsoConnection

FieldTypeDescription
idstrUnique connection identifier.
namestrThe connection display name.
protocolstr"oidc", "saml", or "ldap".
statusstr"active", "inactive", or "error".
domainslist[str]Associated email domains.
jit_provisioningboolWhether JIT provisioning is enabled.
created_atstrISO 8601 creation timestamp.
updated_atstrISO 8601 last-updated timestamp.
Note: The client_secret is never returned in responses. It is stored securely on the server.

List Connections

List all SSO connections for your organization.
connections = client.sso.list_connections()

for conn in connections.connections:
    print(f"{conn.name} ({conn.protocol}) - {conn.status}")
    print(f"  Domains: {', '.join(conn.domains)}")

SsoConnectionList

FieldTypeDescription
connectionslist[SsoConnection]Array of SSO connection objects.

Get Connection

Retrieve a single SSO connection by ID.
conn = client.sso.get_connection("sso_conn_01HX...")

print(f"Name: {conn.name}")         # 'Okta Production'
print(f"Protocol: {conn.protocol}") # 'oidc'
print(f"Status: {conn.status}")     # 'active'

Parameters

ParameterTypeRequiredDescription
connection_idstrYesThe connection ID to retrieve.

Update Connection

Update an existing SSO connection.
from grantex import UpdateSsoConnectionParams

updated = client.sso.update_connection("sso_conn_01HX...", UpdateSsoConnectionParams(
    name="Okta Production (updated)",
    jit_provisioning=False,
    domains=["mycompany.com", "mycompany.org", "subsidiary.com"],
))

print(f"Name: {updated.name}")      # 'Okta Production (updated)'
print(f"Domains: {updated.domains}") # ['mycompany.com', 'mycompany.org', 'subsidiary.com']

UpdateSsoConnectionParams

FieldTypeRequiredDescription
namestrNoUpdated display name.
domainslist[str]NoUpdated list of associated email domains.
jit_provisioningboolNoEnable or disable JIT provisioning.
default_rolestrNoUpdated default role for JIT-provisioned users.
attribute_mappingdict[str, str]NoUpdated SAML attribute mapping.

Delete Connection

Delete an SSO connection. Users associated with this connection will no longer be able to log in via SSO.
client.sso.delete_connection("sso_conn_01HX...")
# Returns None -- connection is removed

Parameters

ParameterTypeRequiredDescription
connection_idstrYesThe connection ID to delete.
Warning: Deleting a connection immediately disables SSO login for all users routed through it. Ensure you have an alternative authentication method configured before removing a connection.

Test Connection

Test an SSO connection to verify that the IdP configuration is correct and reachable.
test = client.sso.test_connection("sso_conn_01HX...")

print(f"Success: {test.success}")            # True
print(f"Message: {test.message}")            # 'Connection verified successfully'
print(f"Response time: {test.response_time}") # 142 (ms)

Parameters

ParameterTypeRequiredDescription
connection_idstrYesThe connection ID to test.

SsoTestResult

FieldTypeDescription
successboolWhether the connection test passed.
messagestrHuman-readable result message.
response_timeintIdP response time in milliseconds.

Enforcement

Set Enforcement

Enforce SSO login for your organization. When enabled, all members must authenticate through an SSO connection.
from grantex import SsoEnforcementParams

enforcement = client.sso.set_enforcement(SsoEnforcementParams(
    enforced=True,
    exempt_roles=["owner"],
))

print(f"Enforced: {enforcement.enforced}")         # True
print(f"Exempt roles: {enforcement.exempt_roles}") # ['owner']

SsoEnforcementParams

FieldTypeRequiredDescription
enforcedboolYesWhether SSO login is enforced for all members.
exempt_roleslist[str]NoRoles exempt from SSO enforcement.

SsoEnforcement

FieldTypeDescription
enforcedboolWhether SSO enforcement is active.
exempt_roleslist[str]Roles exempt from the enforcement policy.

Session Management

List Sessions

List active SSO sessions for your organization.
sessions = client.sso.list_sessions()

for session in sessions.sessions:
    print(f"{session.email} - {session.connection_name} - expires {session.expires_at}")

SsoSessionList

FieldTypeDescription
sessionslist[SsoSession]Array of active SSO session objects.

SsoSession

FieldTypeDescription
idstrSession identifier.
emailstrThe user’s email address.
connection_idstrThe SSO connection used for this session.
connection_namestrDisplay name of the SSO connection.
created_atstrISO 8601 session creation timestamp.
expires_atstrISO 8601 session expiration timestamp.

Revoke Session

Revoke an active SSO session, forcing the user to re-authenticate.
client.sso.revoke_session("sso_sess_01HX...")
# Returns None -- session is revoked

Parameters

ParameterTypeRequiredDescription
session_idstrYesThe session ID to revoke.

Enterprise Login Flow

Get Login URL (enterprise)

Get the SSO authorization URL for a user based on their email domain. The domain is matched against configured connections to route the user to the correct IdP.
from grantex import SsoLoginParams

login = client.sso.get_login_url(SsoLoginParams(
    domain="mycompany.com",
    redirect_uri="https://yourapp.com/sso/callback",
))

print(f"Redirect to: {login.authorize_url}")
print(f"Connection: {login.connection_id}")  # 'sso_conn_01HX...'
print(f"Protocol: {login.protocol}")         # 'oidc'

SsoLoginParams

FieldTypeRequiredDescription
domainstrYesEmail domain to match against SSO connections.
redirect_uristrNoOverride the redirect URI for this login request.

SsoLoginResponse

FieldTypeDescription
authorize_urlstrThe full authorization URL. Redirect the user here.
connection_idstrThe matched SSO connection ID.
protocolstrThe protocol of the matched connection.

Handle OIDC Callback

Handle the callback from an OIDC identity provider. Exchanges the authorization code for user information and provisions the user if JIT is enabled.
from grantex import SsoOidcCallbackParams

result = client.sso.handle_oidc_callback(SsoOidcCallbackParams(
    code="oidc_auth_code",
    state="csrf_state",
))

print(f"Email: {result.email}")               # 'alice@mycompany.com'
print(f"Name: {result.name}")                 # 'Alice Smith'
print(f"Subject: {result.sub}")               # 'okta|abc123'
print(f"Developer ID: {result.developer_id}") # 'dev_01HXYZ...'
print(f"Connection: {result.connection_id}")  # 'sso_conn_01HX...'
print(f"Provisioned: {result.provisioned}")   # True (if JIT-created)

SsoOidcCallbackParams

FieldTypeRequiredDescription
codestrYesThe authorization code from the OIDC callback.
statestrYesThe state parameter for CSRF protection.

SsoCallbackResponse

FieldTypeDescription
emailstr | NoneThe user’s email address from the IdP.
namestr | NoneThe user’s display name from the IdP.
substr | NoneThe user’s subject identifier from the IdP.
developer_idstrThe Grantex developer ID.
connection_idstrThe SSO connection that handled this authentication.
provisionedboolWhether the user was JIT-provisioned during this login.

Handle SAML Callback

Handle the callback from a SAML 2.0 identity provider. Validates the SAML assertion and provisions the user if JIT is enabled.
from grantex import SsoSamlCallbackParams

result = client.sso.handle_saml_callback(SsoSamlCallbackParams(
    saml_response=request.form["SAMLResponse"],
    relay_state=request.form.get("RelayState"),
))

print(f"Email: {result.email}")               # 'bob@contoso.com'
print(f"Name: {result.name}")                 # 'Bob Jones'
print(f"Developer ID: {result.developer_id}") # 'dev_01HXYZ...'
print(f"Connection: {result.connection_id}")  # 'sso_conn_02HX...'
print(f"Provisioned: {result.provisioned}")   # False

SsoSamlCallbackParams

FieldTypeRequiredDescription
saml_responsestrYesThe base64-encoded SAML response from the IdP.
relay_statestrNoThe RelayState parameter from the SAML callback.

Response

Returns the same SsoCallbackResponse as handle_oidc_callback().

Handle LDAP Callback

Authenticate a user via LDAP bind. Unlike OIDC and SAML which use browser redirects, LDAP authentication submits credentials directly. Grantex binds to the LDAP directory, verifies the user’s password, reads their attributes and group memberships, maps groups to scopes, and provisions the user if JIT is enabled.
from grantex import SsoLdapCallbackParams

result = client.sso.handle_ldap_callback(SsoLdapCallbackParams(
    username="alice",
    password="user-password",
    connection_id="sso_conn_03HX...",
))

print(f"Email: {result.email}")               # 'alice@mycompany.com'
print(f"Name: {result.name}")                 # 'Alice Smith'
print(f"Developer ID: {result.developer_id}") # 'dev_01HXYZ...'
print(f"Connection: {result.connection_id}")  # 'sso_conn_03HX...'
print(f"Provisioned: {result.provisioned}")   # True (if JIT-created)

SsoLdapCallbackParams

FieldTypeRequiredDescription
usernamestrYesThe user’s LDAP username (e.g. uid, sAMAccountName, or full DN).
passwordstrYesThe user’s LDAP password for bind authentication.
connection_idstrYesThe SSO connection ID for the LDAP directory.

Response

Returns the same SsoCallbackResponse as handle_oidc_callback().
Note: LDAP credentials are never stored by Grantex. They are used only for the bind operation and discarded immediately after authentication.

Full Enterprise SSO Flow Example

from flask import Flask, request, redirect
from grantex import (
    Grantex,
    CreateSsoConnectionParams,
    SsoEnforcementParams,
    SsoLoginParams,
    SsoOidcCallbackParams,
    SsoSamlCallbackParams,
)
import os

app = Flask(__name__)
client = Grantex(api_key=os.environ["GRANTEX_API_KEY"])

# Step 1: Create SSO connections (one-time setup)
client.sso.create_connection(CreateSsoConnectionParams(
    name="Okta Production",
    protocol="oidc",
    issuer_url="https://mycompany.okta.com",
    client_id=os.environ["OKTA_CLIENT_ID"],
    client_secret=os.environ["OKTA_CLIENT_SECRET"],
    domains=["mycompany.com"],
    jit_provisioning=True,
    default_role="member",
))

client.sso.create_connection(CreateSsoConnectionParams(
    name="Azure AD SAML",
    protocol="saml",
    metadata_url=os.environ["AZURE_METADATA_URL"],
    assertion_consumer_service_url="https://yourapp.com/sso/saml/callback",
    entity_id="https://yourapp.com/saml/metadata",
    domains=["contoso.com"],
    jit_provisioning=True,
))

# Step 2: Enforce SSO for the organization
client.sso.set_enforcement(SsoEnforcementParams(
    enforced=True,
    exempt_roles=["owner"],
))

# Step 3: Redirect user to SSO login based on email domain
@app.route("/sso/login")
def sso_login():
    domain = request.args["domain"]
    login = client.sso.get_login_url(SsoLoginParams(
        domain=domain,
        redirect_uri="https://yourapp.com/sso/callback",
    ))
    return redirect(login.authorize_url)

# Step 4a: Handle OIDC callback
@app.route("/sso/callback")
def sso_callback():
    result = client.sso.handle_oidc_callback(SsoOidcCallbackParams(
        code=request.args["code"],
        state=request.args["state"],
    ))

    print(f"Welcome, {result.name} ({result.email})")
    if result.provisioned:
        print("New user provisioned via JIT")
    return redirect("/dashboard")

# Step 4b: Handle SAML callback
@app.route("/sso/saml/callback", methods=["POST"])
def sso_saml_callback():
    result = client.sso.handle_saml_callback(SsoSamlCallbackParams(
        saml_response=request.form["SAMLResponse"],
        relay_state=request.form.get("RelayState"),
    ))

    print(f"Welcome, {result.name} ({result.email})")
    return redirect("/dashboard")

# Admin: List active sessions
@app.route("/admin/sso/sessions")
def sso_sessions():
    sessions = client.sso.list_sessions()
    return {"sessions": [s.__dict__ for s in sessions.sessions]}

Legacy Single-Config Methods

Note: The following methods manage a single OIDC configuration per organization. They are retained for backward compatibility. For new integrations, use the enterprise connection methods above which support multiple IdPs, SAML, and domain-based routing.

Create Config

Create or update the OIDC SSO configuration for your developer organization:
from grantex import Grantex, CreateSsoConfigParams

with Grantex(api_key="gx_live_...") as client:
    config = client.sso.create_config(CreateSsoConfigParams(
        issuer_url="https://accounts.google.com",
        client_id="your-oidc-client-id",
        client_secret="your-oidc-client-secret",
        redirect_uri="https://myapp.com/sso/callback",
    ))

    print(f"Issuer: {config.issuer_url}")
    print(f"Client ID: {config.client_id}")
    print(f"Redirect URI: {config.redirect_uri}")
    print(f"Created at: {config.created_at}")

CreateSsoConfigParams

FieldTypeRequiredDescription
issuer_urlstrYesThe OIDC issuer URL (e.g. https://accounts.google.com).
client_idstrYesThe OIDC client ID from your IdP.
client_secretstrYesThe OIDC client secret from your IdP.
redirect_uristrYesThe redirect URI registered with your IdP.

Get Config

Retrieve the current SSO configuration. The client secret is not included in the response:
config = client.sso.get_config()

print(f"Issuer: {config.issuer_url}")
print(f"Client ID: {config.client_id}")
print(f"Redirect URI: {config.redirect_uri}")
print(f"Updated at: {config.updated_at}")

SsoConfig

FieldTypeDescription
issuer_urlstrThe OIDC issuer URL.
client_idstrThe OIDC client ID.
redirect_uristrThe registered redirect URI.
created_atstrISO 8601 creation timestamp.
updated_atstrISO 8601 last-updated timestamp.

Delete Config

Remove the SSO configuration:
client.sso.delete_config()
# Returns None on success

Get Login URL (legacy)

Generate the OIDC authorization URL to redirect a user to for SSO login:
from grantex import Grantex

with Grantex(api_key="gx_live_...") as client:
    login = client.sso.get_login_url("my-organization")

    print(f"Redirect to: {login.authorize_url}")

Parameters

ParameterTypeRequiredDescription
orgstrYesThe organization identifier for SSO lookup.

SsoLoginResponse (legacy)

FieldTypeDescription
authorize_urlstrThe OIDC authorization URL to redirect the user to.

Handle Callback

Exchange the OIDC authorization code for user information after the IdP redirects back to your application:
from grantex import Grantex

with Grantex(api_key="gx_live_...") as client:
    result = client.sso.handle_callback(
        code="oidc_auth_code",
        state="csrf_state_value",
    )

    print(f"Developer ID: {result.developer_id}")
    print(f"Email: {result.email}")
    print(f"Name: {result.name}")
    print(f"Subject: {result.sub}")

Parameters

ParameterTypeRequiredDescription
codestrYesThe authorization code from the OIDC callback.
statestrYesThe state parameter for CSRF verification.

SsoCallbackResponse (legacy)

FieldTypeDescription
developer_idstrThe Grantex developer ID for the authenticated user.
emailstr | NoneThe user’s email address (if provided by IdP).
namestr | NoneThe user’s display name (if provided by IdP).
substr | NoneThe OIDC subject identifier.

Legacy SSO Flow Example

from grantex import Grantex, CreateSsoConfigParams

with Grantex(api_key="gx_live_...") as client:
    # 1. Configure SSO (one-time setup)
    client.sso.create_config(CreateSsoConfigParams(
        issuer_url="https://accounts.google.com",
        client_id="your-client-id",
        client_secret="your-client-secret",
        redirect_uri="https://myapp.com/sso/callback",
    ))

    # 2. Generate login URL for a user
    login = client.sso.get_login_url("my-organization")
    # Redirect the user's browser to login.authorize_url

    # 3. Handle the callback (in your /sso/callback route)
    result = client.sso.handle_callback(
        code="code_from_idp",
        state="state_from_idp",
    )
    print(f"Logged in as: {result.email} (developer: {result.developer_id})")

    # 4. Verify or clean up configuration
    config = client.sso.get_config()
    print(f"SSO configured with: {config.issuer_url}")

    # To remove SSO:
    # client.sso.delete_config()