Single Sign-On (SSO)
| Field | Value |
|---|---|
| Document ID | ASCEND-SEC-015 |
| Version | 1.0.0 |
| Last Updated | December 19, 2025 |
| Author | Ascend Engineering Team |
| Publisher | OW-KAI Technologies Inc. |
| Classification | Enterprise Client Documentation |
| Compliance | SOC 2 CC6.1/CC6.2, PCI-DSS 7.1/8.3, HIPAA 164.312, NIST 800-53 AC-2/SI-4 |
Reading Time: 10 minutes | Skill Level: Advanced
Overview
ASCEND supports enterprise SSO integration with Okta, Azure Active Directory, and Google Workspace using OIDC. SSO provides centralized authentication, automatic group-to-role mapping, and seamless user provisioning.
Supported Providers
| Provider | Protocol | Group Sync | MFA |
|---|---|---|---|
| Okta | OIDC | Yes | Delegated |
| Azure AD | OIDC | Yes | Delegated |
| Google Workspace | OIDC | Limited | Delegated |
SSO Flow
+---------------------------------------------------------------------------------+
| SSO AUTHENTICATION FLOW |
+---------------------------------------------------------------------------------+
| |
| USER ASCEND IDENTITY PROVIDER |
| | | | |
| | 1. Click SSO Login | | |
| |----------------------->| | |
| | | | |
| | 2. Redirect to IdP | 3. Authorization URL | |
| |<-----------------------|----------------------------->| |
| | | | |
| | 4. Authenticate with IdP (MFA) | |
| |------------------------------------------------------>| |
| | | | |
| | 5. Redirect with code | | |
| |<------------------------------------------------------| |
| | | | |
| | 6. Code to ASCEND | | |
| |----------------------->| | |
| | | 7. Exchange code for token | |
| | |----------------------------->| |
| | | | |
| | | 8. Access token + ID token | |
| | |<-----------------------------| |
| | | | |
| | | 9. Validate token (JWKS) | |
| | |----------------------------->| |
| | | | |
| | | 10. Get user info + groups | |
| | |----------------------------->| |
| | | | |
| | 11. Create session | | |
| |<-----------------------| | |
| | | | |
+---------------------------------------------------------------------------------+
Configuration
Okta Setup
# Source: sso_manager.py:28
okta_config = {
"name": "Okta",
"client_id": "<okta-client-id>",
"client_secret": "<okta-client-secret>",
"domain": "your-org.okta.com",
"authorization_url": "https://your-org.okta.com/oauth2/v1/authorize",
"token_url": "https://your-org.okta.com/oauth2/v1/token",
"userinfo_url": "https://your-org.okta.com/oauth2/v1/userinfo",
"jwks_url": "https://your-org.okta.com/oauth2/v1/keys",
"scopes": ["openid", "profile", "email", "groups"]
}
Azure AD Setup
# Source: sso_manager.py:40
azure_config = {
"name": "Azure Active Directory",
"client_id": "<azure-client-id>",
"client_secret": "<azure-client-secret>",
"tenant_id": "<your-tenant-id>",
"authorization_url": "https://login.microsoftonline.com/<tenant>/oauth2/v2.0/authorize",
"token_url": "https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token",
"userinfo_url": "https://graph.microsoft.com/v1.0/me",
"jwks_url": "https://login.microsoftonline.com/common/discovery/v2.0/keys",
"scopes": ["openid", "profile", "email", "User.Read", "Directory.Read.All"]
}
Google Workspace Setup
# Source: sso_manager.py:52
google_config = {
"name": "Google Workspace",
"client_id": "<google-client-id>",
"client_secret": "<google-client-secret>",
"authorization_url": "https://accounts.google.com/o/oauth2/auth",
"token_url": "https://oauth2.googleapis.com/token",
"userinfo_url": "https://www.googleapis.com/oauth2/v2/userinfo",
"jwks_url": "https://www.googleapis.com/oauth2/v3/certs",
"scopes": ["openid", "email", "profile"]
}
SSO API Endpoints
Get Authorization URL
curl "https://pilot.owkai.app/api/sso/authorize?provider=okta&redirect_uri=https://app.company.com/callback" \
-H "Authorization: Bearer owkai_..."
Response:
{
"authorization_url": "https://your-org.okta.com/oauth2/v1/authorize?client_id=...&redirect_uri=...&scope=openid+profile+email+groups&response_type=code&state=...",
"state": "abc123xyz"
}
Exchange Code for Token
curl -X POST "https://pilot.owkai.app/api/sso/callback" \
-H "Content-Type: application/json" \
-d '{
"provider": "okta",
"code": "<authorization_code>",
"redirect_uri": "https://app.company.com/callback",
"state": "abc123xyz"
}'
Response:
{
"success": true,
"user": {
"email": "user@company.com",
"first_name": "John",
"last_name": "Doe",
"access_level": 4,
"role": "admin",
"sso_provider": "okta",
"groups": ["OW-AI-Administrators", "IT-Security"]
},
"session_token": "<jwt_token>",
"expires_at": "2025-12-15T18:30:00Z"
}
Group-to-Role Mapping
Default Mappings
# Source: sso_manager.py:64
GROUP_TO_ROLE_MAPPING = {
# Okta Groups
"OW-AI-Executives": 5, # Executive level
"OW-AI-Administrators": 4, # Admin level
"OW-AI-Managers": 3, # Manager level
"OW-AI-PowerUsers": 2, # Power user level
"OW-AI-BasicUsers": 1, # Basic level
"OW-AI-Restricted": 0, # Restricted level
# Azure AD Groups
"OW-AI Executive Team": 5,
"OW-AI System Administrators": 4,
"OW-AI Security Managers": 3,
"OW-AI Power Users": 2,
"OW-AI Standard Users": 1,
# Google Workspace Groups
"ow-ai-executives@company.com": 5,
"ow-ai-admins@company.com": 4,
"ow-ai-managers@company.com": 3,
"ow-ai-users@company.com": 1
}
Access Levels
| Level | Role | Permissions |
|---|---|---|
| 5 | Executive | Full access + executive dashboard |
| 4 | Admin | Full administrative access |
| 3 | Manager | Team management + approvals |
| 2 | Power User | Advanced features |
| 1 | Basic User | Standard access |
| 0 | Restricted | Read-only access |
Role Assignment Logic
# Source: sso_manager.py:240
def map_groups_to_access_level(self, groups: List[str]) -> int:
"""Map IdP groups to OW-AI access level."""
# Find highest access level from user's groups
max_level = 0
for group in groups:
if group in self.group_to_role_mapping:
level = self.group_to_role_mapping[group]
if level > max_level:
max_level = level
return max_level
Token Validation
JWKS Verification (SEC-079)
# Source: sso_manager.py:322
def validate_sso_token(self, provider: str, id_token: str) -> Dict:
"""
Validate SSO ID token with full cryptographic signature verification.
Security (SEC-079):
- Fetches JWKS from provider to get signing keys
- Validates RS256 signature cryptographically
- Validates audience, issuer, and expiration claims
"""
# Get signing key from JWKS
jwks_client = PyJWKClient(provider_config["jwks_url"])
signing_key = jwks_client.get_signing_key_from_jwt(id_token)
# Full cryptographic verification
decoded_token = jwt.decode(
id_token,
signing_key.key,
algorithms=["RS256"],
options={
"verify_signature": True,
"verify_exp": True,
"verify_iat": True,
"require": ["exp", "iat", "sub", "email"]
}
)
return decoded_token
User Provisioning
Automatic User Creation
# Source: sso_manager.py:257
def create_enterprise_user_profile(self, provider, user_info, groups):
"""Create enterprise user profile from SSO data."""
access_level = self.map_groups_to_access_level(groups)
return {
"email": user_info.get("email"),
"first_name": user_info.get("given_name"),
"last_name": user_info.get("family_name"),
"access_level": access_level,
"sso_provider": provider,
"sso_groups": groups,
"role": "admin" if access_level >= 4 else "user",
"department": self._extract_department_from_groups(groups),
"mfa_enabled": True, # SSO providers enforce MFA
"status": "Active",
"login_method": "SSO"
}
Department Extraction
# Source: sso_manager.py:300
DEPARTMENT_MAPPINGS = {
"finance": "Finance",
"hr": "Human Resources",
"it": "Information Technology",
"security": "Security",
"operations": "Operations",
"executive": "Executive",
"legal": "Legal",
"marketing": "Marketing"
}
Security Considerations
1. Token Security
- ID tokens validated with JWKS public keys
- RS256 signature verification
- Audience validation
- Expiration checking
2. State Parameter
# CSRF protection via state parameter
state = secrets.token_urlsafe(32)
# Store state and verify on callback
3. MFA Delegation
# MFA enforced by identity provider
{
"mfa_enabled": True,
"mfa_provider": "okta"
}
4. Group Sync
# Groups refreshed on each login
groups = sso.get_user_groups(provider, access_token)
access_level = sso.map_groups_to_access_level(groups)
Environment Variables
| Variable | Description | Example |
|---|---|---|
OKTA_DOMAIN | Okta organization domain | your-org.okta.com |
OKTA_CLIENT_ID | Okta application client ID | 0oa... |
OKTA_CLIENT_SECRET | Okta application secret | (secret) |
AZURE_TENANT_ID | Azure AD tenant ID | xxxxxxxx-xxxx-... |
AZURE_CLIENT_ID | Azure application ID | xxxxxxxx-xxxx-... |
AZURE_CLIENT_SECRET | Azure application secret | (secret) |
GOOGLE_CLIENT_ID | Google OAuth client ID | xxxx.apps.google... |
GOOGLE_CLIENT_SECRET | Google OAuth secret | (secret) |
Best Practices
1. Configure Group Mappings
# Map all relevant IdP groups
GROUP_TO_ROLE_MAPPING = {
"Security-Team": 4,
"Compliance-Officers": 3,
"All-Employees": 1
}
2. Enable Group Sync
# Ensure groups scope is requested
scopes = ["openid", "profile", "email", "groups"]
3. Regular Access Reviews
# Review access levels periodically
for user in users:
current_groups = sso.get_user_groups(provider, token)
expected_level = sso.map_groups_to_access_level(current_groups)
if user.access_level != expected_level:
update_user_access(user, expected_level)
4. Monitor SSO Events
# Log all SSO authentication events
{
"event_type": "SSO_LOGIN",
"provider": "okta",
"user_email": "user@company.com",
"groups": ["OW-AI-Administrators"],
"access_level": 4
}
Next Steps
- Authentication — JWT session management
- RBAC — Role-based access control
- Multi-Tenancy — Organization isolation
Document Version: 1.0.0 | Last Updated: December 2025