v0.1.0-draft AI Drafted

Ping Identity Hardening Guide

Identity Last updated: 2025-12-14

Identity federation security for PingFederate, PingOne, and OAuth configurations

Overview

Ping Identity serves 50%+ of Fortune 100 with federation trust relationships connecting enterprise identity to hundreds of downstream applications. OAuth and SAML tokens, if compromised, provide persistent access across the enterprise. The PingOne DaVinci orchestration platform creates automated identity workflows that attackers can exploit for privilege escalation and persistent access.

Intended Audience

  • Security engineers managing identity infrastructure
  • IT administrators configuring Ping Identity products
  • GRC professionals assessing IAM compliance
  • Third-party risk managers evaluating federation security

How to Use This Guide

  • L1 (Baseline): Essential controls for all organizations
  • L2 (Hardened): Enhanced controls for security-sensitive environments
  • L3 (Maximum Security): Strictest controls for regulated industries

Scope

This guide covers Ping Identity security configurations including federation hardening, OAuth security, DaVinci orchestration controls, and token lifecycle management.


Table of Contents

  1. Authentication & Access Controls
  2. Federation Security
  3. OAuth & Token Security
  4. DaVinci Orchestration Security
  5. Monitoring & Detection
  6. Third-Party Integration Security
  7. Compliance Quick Reference

1. Authentication & Access Controls

1.1 Enforce Phishing-Resistant MFA

Profile Level: L1 (Baseline) CIS Controls: 6.3, 6.5 NIST 800-53: IA-2(1), IA-2(6)

Description

Require FIDO2/WebAuthn authenticators for administrator and high-privilege user authentication.

Rationale

Why This Matters:

  • Federation trust means Ping Identity compromise affects all connected apps
  • TOTP/SMS MFA can be bypassed via real-time phishing
  • FIDO2 provides origin-bound authentication resistant to phishing

Attack Scenario: Attacker phishes admin credentials, generates valid tokens for any connected application via federation trust exploitation.

ClickOps Implementation (PingOne)

Step 1: Enable FIDO2 Authentication

  1. Navigate to: Authentication → Policies → MFA Policies
  2. Create policy:
    • Name: “Phishing-Resistant MFA”
    • Methods: FIDO2 Security Key (required)
    • Fallback: None for admins
  3. Assign to administrator groups

Step 2: Configure Authentication Policy

  1. Navigate to: Authentication → Policies → Sign-On Policies
  2. Create rule:
    • Condition: User group = “Administrators”
    • Action: Require FIDO2 MFA
    • Session duration: 2 hours maximum

Step 3: Disable Legacy Methods for Admins

  1. Navigate to: Authentication → MFA
  2. For admin accounts:
    • Disable: SMS, Voice, Email OTP
    • Enable only: FIDO2, Mobile app (push with number matching)

Code Implementation (PingOne API)

See the API pack below for MFA policy creation and assignment commands.

Compliance Mappings

Framework Control ID Control Description
SOC 2 CC6.1 Logical access controls
NIST 800-53 IA-2(6) MFA for privileged accounts
PCI DSS 8.3.1 MFA for administrative access
Code Pack: Terraform
hth-ping-identity-1.01-enforce-phishing-resistant-mfa.tf View source on GitHub ↗
# MFA device policy requiring FIDO2 for admin authentication
resource "pingone_mfa_device_policy" "phishing_resistant" {
  environment_id = var.pingone_environment_id
  name           = "HTH Phishing-Resistant MFA"

  authentication = {
    device_selection = "DEFAULT_TO_FIRST"
  }

  fido2 = {
    enabled = true
  }

  mobile = {
    enabled = false
  }

  totp = {
    enabled = false
  }

  sms = {
    enabled = false
  }

  voice = {
    enabled = false
  }

  email = {
    enabled = false
  }
}

# FIDO2 policy with platform and cross-platform authenticators
resource "pingone_mfa_fido2_policy" "webauthn" {
  environment_id = var.pingone_environment_id
  name           = "HTH FIDO2 WebAuthn Policy"

  attestation_requirements      = "DIRECT"
  authenticator_attachment       = "BOTH"
  backup_eligibility             = { allow = true, enforce_during_authentication = false }
  device_display_name            = "FIDO2 Security Key"
  discoverable_credentials       = "PREFERRED"
  mds_authenticators_requirements = { enforce_during_authentication = true, option = "SPECIFIC" }
  relying_party_id               = ""
  user_display_name_attributes   = { attributes = [{ name = "username" }] }
  user_verification              = { enforce_during_authentication = true, option = "REQUIRED" }
}

# Sign-on policy enforcing FIDO2 for administrators
resource "pingone_sign_on_policy" "phishing_resistant_admin" {
  environment_id = var.pingone_environment_id
  name           = "HTH Phishing-Resistant Admin Sign-On"
  description    = "Requires FIDO2 MFA for all administrator access"
}

# Sign-on policy action: require MFA with FIDO2
resource "pingone_sign_on_policy_action" "admin_mfa" {
  environment_id    = var.pingone_environment_id
  sign_on_policy_id = pingone_sign_on_policy.phishing_resistant_admin.id
  priority          = 1

  mfa {
    device_sign_on_policy_id = pingone_mfa_device_policy.phishing_resistant.id
    no_device_mode           = "BLOCK"
  }

  conditions {
    user_attribute_equals {
      attribute_reference = "$${user.memberOfGroups[?(id == '${var.admin_group_id}')]}"
      value_boolean       = true
    }
  }
}

# L2+: Reduce admin session duration to 2 hours
resource "pingone_sign_on_policy_action" "admin_session_limit" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id    = var.pingone_environment_id
  sign_on_policy_id = pingone_sign_on_policy.phishing_resistant_admin.id
  priority          = 2

  login {
    recovery_enabled = false
  }
}
Code Pack: API Script
hth-ping-identity-1.01-enforce-phishing-resistant-mfa.sh View source on GitHub ↗
# Create MFA policy requiring FIDO2
curl -X POST "https://api.pingone.com/v1/environments/${ENV_ID}/mfaPolicies" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Phishing-Resistant MFA",
    "enabled": true,
    "configuration": {
      "fido2": {
        "enabled": true,
        "required": true
      },
      "sms": {
        "enabled": false
      },
      "totp": {
        "enabled": false
      }
    }
  }'

# Assign to admin group
curl -X PUT "https://api.pingone.com/v1/environments/${ENV_ID}/groups/${ADMIN_GROUP_ID}/mfaPolicy" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "mfaPolicyId": "${MFA_POLICY_ID}"
  }'

1.2 Implement Least-Privilege Admin Roles

Profile Level: L1 (Baseline) NIST 800-53: AC-6, AC-6(1)

Description

Create granular administrative roles instead of using organization-wide admin access.

ClickOps Implementation (PingOne)

Step 1: Create Custom Admin Roles

  1. Navigate to: Settings → Roles
  2. Create roles:

Identity Administrator:

  • Manage users and groups
  • Reset passwords
  • Assign MFA
  • NO: Configure applications, manage policies

Application Administrator:

  • Configure SAML/OIDC applications
  • Manage application policies
  • NO: Manage users, access audit logs

Security Administrator:

  • Configure MFA policies
  • Manage authentication policies
  • Access audit logs
  • NO: Manage applications directly

Read-Only Auditor:

  • View all configurations
  • Access reports and logs
  • NO: Make any changes

Step 2: Assign Roles to Groups

  1. Navigate to: Identities → Groups
  2. Create admin groups (e.g., “Identity-Admins”, “App-Admins”)
  3. Assign appropriate roles to each group
  4. Add users to groups (not direct role assignment)
Code Pack: Terraform
hth-ping-identity-1.02-implement-least-privilege-admin-roles.tf View source on GitHub ↗
# Groups for role-based admin access
resource "pingone_group" "identity_admins" {
  environment_id = var.pingone_environment_id
  name           = "HTH Identity Administrators"
  description    = "Manage users, groups, reset passwords, assign MFA. No application or policy config."
}

resource "pingone_group" "app_admins" {
  environment_id = var.pingone_environment_id
  name           = "HTH Application Administrators"
  description    = "Configure SAML/OIDC applications and application policies. No user management."
}

resource "pingone_group" "security_admins" {
  environment_id = var.pingone_environment_id
  name           = "HTH Security Administrators"
  description    = "Configure MFA and authentication policies, access audit logs. No direct app management."
}

resource "pingone_group" "auditors" {
  environment_id = var.pingone_environment_id
  name           = "HTH Read-Only Auditors"
  description    = "View all configurations, access reports and logs. No write access."
}

# Role assignments -- Identity Admin
resource "pingone_group_role_assignment" "identity_admin_role" {
  count = var.identity_admin_group_id != "" ? 1 : 0

  environment_id = var.pingone_environment_id
  group_id       = pingone_group.identity_admins.id
  role_id        = data.pingone_role.identity_data_admin.id

  scope_environment_id = var.pingone_environment_id
}

# Role assignments -- Application Admin
resource "pingone_group_role_assignment" "app_admin_role" {
  count = var.app_admin_group_id != "" ? 1 : 0

  environment_id = var.pingone_environment_id
  group_id       = pingone_group.app_admins.id
  role_id        = data.pingone_role.application_owner.id

  scope_environment_id = var.pingone_environment_id
}

# Role assignments -- Security Admin
resource "pingone_group_role_assignment" "security_admin_role" {
  count = var.security_admin_group_id != "" ? 1 : 0

  environment_id = var.pingone_environment_id
  group_id       = pingone_group.security_admins.id
  role_id        = data.pingone_role.environment_admin.id

  scope_environment_id = var.pingone_environment_id
}

# Data sources for built-in PingOne roles
data "pingone_role" "identity_data_admin" {
  name = "Identity Data Admin"
}

data "pingone_role" "application_owner" {
  name = "Application Owner"
}

data "pingone_role" "environment_admin" {
  name = "Environment Admin"
}

data "pingone_role" "identity_data_read_only" {
  name = "Identity Data Read Only"
}

# L2+: Enforce group-only role assignments (no direct user roles)
# This is enforced via policy; direct user role assignments should be audited
resource "pingone_group_role_assignment" "auditor_role" {
  environment_id = var.pingone_environment_id
  group_id       = pingone_group.auditors.id
  role_id        = data.pingone_role.identity_data_read_only.id

  scope_environment_id = var.pingone_environment_id
}

1.3 Configure IP-Based Access Restrictions

Profile Level: L2 (Hardened) NIST 800-53: AC-3(7), SC-7

Description

Restrict administrative console and API access to known IP ranges.

ClickOps Implementation

Step 1: Configure IP Restrictions (PingOne)

  1. Navigate to: Settings → IP Restrictions
  2. Add allowed IP ranges:
    • Corporate network CIDRs
    • VPN egress IPs
  3. Set default: Deny all not in list

Step 2: Configure in Sign-On Policy

  1. Navigate to: Authentication → Policies → Sign-On Policies
  2. Create rule:
    • Condition: IP not in trusted ranges
    • Action: Deny access OR require additional verification
Code Pack: Terraform
hth-ping-identity-1.03-configure-ip-based-access-restrictions.tf View source on GitHub ↗
# IP-based access restriction for admin console and API access (L2+)
resource "pingone_sign_on_policy" "ip_restricted" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH IP-Restricted Access"
  description    = "Restricts admin and API access to known corporate IP ranges"
}

# Allow access from corporate network CIDRs
resource "pingone_sign_on_policy_action" "allow_corporate_ips" {
  count = var.profile_level >= 2 && length(var.corporate_gateway_cidrs) > 0 ? 1 : 0

  environment_id    = var.pingone_environment_id
  sign_on_policy_id = pingone_sign_on_policy.ip_restricted[0].id
  priority          = 1

  login {
    recovery_enabled = true
  }

  conditions {
    ip_out_of_range_cidr = var.corporate_gateway_cidrs
  }
}

# Allow access from VPN egress IPs
resource "pingone_sign_on_policy_action" "allow_vpn_ips" {
  count = var.profile_level >= 2 && length(var.vpn_egress_cidrs) > 0 ? 1 : 0

  environment_id    = var.pingone_environment_id
  sign_on_policy_id = pingone_sign_on_policy.ip_restricted[0].id
  priority          = 2

  login {
    recovery_enabled = true
  }

  conditions {
    ip_out_of_range_cidr = var.vpn_egress_cidrs
  }
}

# L3: Deny all traffic outside corporate and VPN ranges
resource "pingone_sign_on_policy_action" "deny_unknown_ips" {
  count = var.profile_level >= 3 ? 1 : 0

  environment_id    = var.pingone_environment_id
  sign_on_policy_id = pingone_sign_on_policy.ip_restricted[0].id
  priority          = 99

  login {
    recovery_enabled = false
  }
}

2. Federation Security

2.1 Harden SAML Federation Trust

Profile Level: L1 (Baseline) NIST 800-53: IA-5, SC-23

Description

Configure secure SAML settings to prevent assertion manipulation and replay attacks.

Rationale

Why This Matters:

  • SAML assertions can be manipulated if not properly validated
  • Weak signature algorithms enable forgery
  • Long assertion validity enables replay attacks

Attack Scenario: Federation trust exploitation enables attackers to generate valid tokens for any connected application.

ClickOps Implementation (PingFederate)

Step 1: Configure Secure Signature Settings

  1. Navigate to: System → Server Configuration → Signing & Encryption
  2. Configure:
    • Signature Algorithm: RSA-SHA256 (minimum)
    • Digest Algorithm: SHA-256 (minimum)
    • Key Size: 2048+ bits RSA or P-256 ECDSA
  3. Disable: SHA-1 algorithms

Step 2: Configure Assertion Validation

  1. Navigate to: Identity Provider → Connection → SAML Settings
  2. Enable:
    • Verify Signature: Required
    • Require Encrypted Assertions: Yes (L2)
    • Audience Restriction: Enforce
  3. Set:
    • Assertion Valid Period: 5 minutes (maximum)
    • Session Timeout: 8 hours

Step 3: Configure Certificate Validation

  1. Navigate to: Security → Certificate Management
  2. Enable:
    • Certificate revocation checking: CRL or OCSP
    • Key usage validation: Enabled
  3. Configure: Certificate expiration alerts (30 days)

Code Implementation

See the CLI pack below for SAML configuration examples.

Code Pack: Terraform
hth-ping-identity-2.01-harden-saml-federation-trust.tf View source on GitHub ↗
# Signing key for SAML assertions -- RSA 2048+ with SHA-256
resource "pingone_key" "saml_signing" {
  environment_id = var.pingone_environment_id
  name           = "HTH SAML Signing Key"
  algorithm      = "RSA"
  key_length     = 2048
  signature_algorithm = "SHA256withRSA"
  subject_dn     = "CN=PingOne SAML Signing, O=HTH Hardened"
  usage_type     = "SIGNING"
  validity_period = 365
}

# SAML application with hardened assertion settings
resource "pingone_application" "hardened_saml_sp" {
  environment_id = var.pingone_environment_id
  name           = "HTH Hardened SAML SP Template"
  enabled        = true

  saml_options {
    acs_urls            = ["https://sp.example.com/saml/acs"]
    assertion_duration  = var.saml_assertion_validity_seconds
    sp_entity_id        = "https://sp.example.com"
    response_is_signed  = true
    assertion_signed    = true
    slo_binding         = "HTTP_REDIRECT"

    idp_signing_key {
      key_id    = pingone_key.saml_signing.id
      algorithm = "SHA256withRSA"
    }

    sp_verification {
      authn_request_signed = true
    }
  }
}

# L2+: Require encrypted SAML assertions
resource "pingone_key" "saml_encryption" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH SAML Encryption Key"
  algorithm      = "RSA"
  key_length     = 2048
  signature_algorithm = "SHA256withRSA"
  subject_dn     = "CN=PingOne SAML Encryption, O=HTH Hardened"
  usage_type     = "ENCRYPTION"
  validity_period = 365
}

# L3+: Enforce ECDSA P-256 signing for maximum security
resource "pingone_key" "saml_signing_ecdsa" {
  count = var.profile_level >= 3 ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH SAML Signing Key (ECDSA)"
  algorithm      = "EC"
  key_length     = 256
  signature_algorithm = "SHA256withECDSA"
  subject_dn     = "CN=PingOne SAML Signing ECDSA, O=HTH Hardened"
  usage_type     = "SIGNING"
  validity_period = 365
}
Code Pack: CLI Script
hth-ping-identity-2.01-harden-saml-federation-trust.sh View source on GitHub ↗
# PingFederate SAML Configuration Example
# XML assertion with short validity window and audience restriction:
#
# <saml:Assertion>
#   <saml:Conditions
#     NotBefore="2025-01-15T10:00:00Z"
#     NotOnOrAfter="2025-01-15T10:05:00Z">
#     <saml:AudienceRestriction>
#       <saml:Audience>https://sp.company.com</saml:Audience>
#     </saml:AudienceRestriction>
#   </saml:Conditions>
# </saml:Assertion>

2.2 Implement Federation Monitoring

Profile Level: L1 (Baseline) NIST 800-53: AU-6, SI-4

Description

Monitor federation activity for anomalous patterns indicating compromise.

Detection Use Cases

See the DB pack below for federation monitoring queries.


2.3 Certificate Lifecycle Management

Profile Level: L1 (Baseline) NIST 800-53: SC-12

Description

Implement proactive certificate management to prevent federation disruption.

ClickOps Implementation

Step 1: Configure Certificate Rotation

  1. Navigate to: Security → Certificate Management
  2. Enable: Automatic certificate renewal alerts
  3. Set thresholds:
    • 90 days: Warning
    • 30 days: Critical alert
    • 14 days: Emergency procedures

Step 2: Implement Dual Certificate

  1. Add new certificate before old expires
  2. Configure SP connections to accept both
  3. Coordinate rotation with SPs
  4. Remove old certificate after validation
Code Pack: Terraform
hth-ping-identity-2.03-certificate-lifecycle-management.tf View source on GitHub ↗
# Notification policy for certificate expiry warnings
resource "pingone_notification_policy" "cert_expiry_warning" {
  environment_id = var.pingone_environment_id
  name           = "HTH Certificate Expiry Warning"
}

# Alert condition: certificate approaching expiry (configurable threshold)
resource "pingone_alert_channel" "cert_expiry" {
  environment_id = var.pingone_environment_id
  alert_name     = "HTH Certificate Expiry Alert"

  addresses = var.siem_webhook_url != "" ? [var.siem_webhook_url] : []

  channel_type   = "EMAIL"
  include_severity = ["WARNING", "ERROR"]
}

# Dual certificate rotation support -- secondary signing key
# Deploy the new key before the old one expires, coordinate with SPs
resource "pingone_key" "saml_signing_rotation" {
  environment_id = var.pingone_environment_id
  name           = "HTH SAML Signing Key (Rotation)"
  algorithm      = "RSA"
  key_length     = 2048
  signature_algorithm = "SHA256withRSA"
  subject_dn     = "CN=PingOne SAML Signing Rotation, O=HTH Hardened"
  usage_type     = "SIGNING"
  validity_period = 365

  # This key exists for planned rotation -- activate when primary nears expiry
  default = false
}

# L2+: Shorter certificate validity for tighter rotation cycles
resource "pingone_key" "short_lived_signing" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH Short-Lived Signing Key"
  algorithm      = "RSA"
  key_length     = 2048
  signature_algorithm = "SHA256withRSA"
  subject_dn     = "CN=PingOne Short-Lived Signing, O=HTH Hardened"
  usage_type     = "SIGNING"
  validity_period = 180
}

3. OAuth & Token Security

3.1 Configure Secure OAuth Settings

Profile Level: L1 (Baseline) NIST 800-53: IA-5(13), SC-23

Description

Harden OAuth authorization server configuration with short token lifetimes and restricted scopes.

ClickOps Implementation (PingOne)

Step 1: Configure Token Lifetimes

  1. Navigate to: Applications → OAuth Settings
  2. Configure:
    • Access Token Lifetime: 1 hour (maximum)
    • Refresh Token Lifetime: 7 days (L1) / 24 hours (L2)
    • ID Token Lifetime: 1 hour
    • Authorization Code Lifetime: 60 seconds

Step 2: Enable Token Binding

  1. Navigate to: Applications → [App] → OAuth Settings
  2. Enable:
    • Require PKCE: For public clients
    • Token binding: Certificate-bound tokens (L2)

Step 3: Restrict Grant Types

  1. Disable unnecessary grant types:
    • Implicit grant: Disabled (deprecated)
    • Resource Owner Password: Disabled unless required
  2. Enable only: Authorization Code with PKCE

Code Implementation

See the API pack below for OAuth application configuration.

Code Pack: Terraform
hth-ping-identity-3.01-configure-secure-oauth-settings.tf View source on GitHub ↗
# Custom resource server (API) with restricted scopes
resource "pingone_resource" "hardened_api" {
  environment_id = var.pingone_environment_id
  name           = "HTH Hardened API"
  description    = "API resource with scoped access and short token lifetimes"

  audience                      = "https://api.example.com"
  access_token_validity_seconds = var.access_token_lifetime_seconds
}

# Define granular scopes for the API resource
resource "pingone_resource_scope" "read" {
  environment_id = var.pingone_environment_id
  resource_id    = pingone_resource.hardened_api.id
  name           = "read"
  description    = "Read-only access to API resources"
}

resource "pingone_resource_scope" "write" {
  environment_id = var.pingone_environment_id
  resource_id    = pingone_resource.hardened_api.id
  name           = "write"
  description    = "Write access to API resources"
}

# OIDC application with hardened OAuth settings
resource "pingone_application" "hardened_oidc" {
  environment_id = var.pingone_environment_id
  name           = "HTH Hardened OIDC Application"
  enabled        = true

  oidc_options {
    type                        = "WEB_APP"
    grant_types                 = ["AUTHORIZATION_CODE", "REFRESH_TOKEN"]
    response_types              = ["CODE"]
    token_endpoint_auth_method  = "CLIENT_SECRET_POST"
    redirect_uris               = ["https://app.example.com/callback"]
    post_logout_redirect_uris   = ["https://app.example.com/logout"]

    # Enforce PKCE with S256
    pkce_enforcement = "S256_REQUIRED"

    # Token lifetimes
    refresh_token_duration                   = var.refresh_token_lifetime_seconds
    refresh_token_rolling_duration           = var.refresh_token_lifetime_seconds
    refresh_token_rolling_grace_period_duration = 0
  }
}

# L2+: Tighten refresh token lifetime to 24 hours
resource "pingone_application" "hardened_oidc_l2" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH Hardened OIDC Application (L2)"
  enabled        = true

  oidc_options {
    type                        = "WEB_APP"
    grant_types                 = ["AUTHORIZATION_CODE", "REFRESH_TOKEN"]
    response_types              = ["CODE"]
    token_endpoint_auth_method  = "CLIENT_SECRET_POST"
    redirect_uris               = ["https://app.example.com/callback"]
    post_logout_redirect_uris   = ["https://app.example.com/logout"]

    pkce_enforcement = "S256_REQUIRED"

    # L2: Reduced refresh token lifetime (24 hours)
    refresh_token_duration                   = 86400
    refresh_token_rolling_duration           = 86400
    refresh_token_rolling_grace_period_duration = 0
  }
}

# L3+: Disable refresh tokens entirely for maximum security
resource "pingone_application" "hardened_oidc_l3" {
  count = var.profile_level >= 3 ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH Hardened OIDC Application (L3)"
  enabled        = true

  oidc_options {
    type                        = "WEB_APP"
    grant_types                 = ["AUTHORIZATION_CODE"]
    response_types              = ["CODE"]
    token_endpoint_auth_method  = "CLIENT_SECRET_POST"
    redirect_uris               = ["https://app.example.com/callback"]
    post_logout_redirect_uris   = ["https://app.example.com/logout"]

    pkce_enforcement = "S256_REQUIRED"

    # L3: No refresh tokens -- force re-authentication
    refresh_token_duration         = 0
    refresh_token_rolling_duration = 0
  }
}
Code Pack: API Script
hth-ping-identity-3.01-configure-secure-oauth-settings.sh View source on GitHub ↗
# PingOne - Configure OAuth application
curl -X PUT "https://api.pingone.com/v1/environments/${ENV_ID}/applications/${APP_ID}" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Secure App",
    "protocol": "OPENID_CONNECT",
    "tokenEndpointAuthMethod": "CLIENT_SECRET_POST",
    "grantTypes": ["AUTHORIZATION_CODE", "REFRESH_TOKEN"],
    "pkceEnforcement": "S256_REQUIRED",
    "accessTokenValiditySeconds": 3600,
    "refreshTokenValiditySeconds": 86400,
    "refreshTokenRollingEnabled": true
  }'

3.2 Implement Token Revocation

Profile Level: L1 (Baseline) NIST 800-53: AC-2(6)

Description

Enable token revocation for user sessions and compromised tokens.

ClickOps Implementation

Step 1: Enable Session Revocation

  1. Navigate to: Authentication → Session Management
  2. Enable:
    • Allow session revocation: Yes
    • Propagate revocation: To all connected apps

Step 2: Configure Revocation on Risk

  1. Navigate to: Authentication → Risk Policies
  2. Create rule:
    • Trigger: High-risk authentication detected
    • Action: Revoke all user tokens
    • Notify: Security team

Step 3: Admin Revocation Capability

  1. Verify admin can revoke user sessions
  2. Document incident response procedure
  3. Test revocation propagation
Code Pack: Terraform
hth-ping-identity-3.02-implement-token-revocation.tf View source on GitHub ↗
# Sign-on policy with session revocation on risk detection
resource "pingone_sign_on_policy" "risk_based_revocation" {
  environment_id = var.pingone_environment_id
  name           = "HTH Risk-Based Token Revocation"
  description    = "Revoke tokens and sessions on high-risk authentication events"
}

# Risk policy: medium risk triggers step-up, high risk triggers revocation
resource "pingone_risk_policy" "token_revocation" {
  environment_id = var.pingone_environment_id
  name           = "HTH High-Risk Revocation Policy"
  default_result = { level = "LOW" }

  evaluated_predictors = [
    { predictor_id = data.pingone_risk_predictor.ip_reputation.id },
    { predictor_id = data.pingone_risk_predictor.anonymous_network.id },
    { predictor_id = data.pingone_risk_predictor.geovelocity.id },
  ]

  policy_scores = {
    medium = { min_score = 40, max_score = 69 }
    high   = { min_score = 70, max_score = 100 }
  }
}

# Data sources for built-in risk predictors
data "pingone_risk_predictor" "ip_reputation" {
  environment_id = var.pingone_environment_id
  name           = "IP Reputation"
}

data "pingone_risk_predictor" "anonymous_network" {
  environment_id = var.pingone_environment_id
  name           = "Anonymous Network Detection"
}

data "pingone_risk_predictor" "geovelocity" {
  environment_id = var.pingone_environment_id
  name           = "Geo-Velocity"
}

# Sign-on policy action: high risk triggers MFA + revocation
resource "pingone_sign_on_policy_action" "high_risk_mfa" {
  environment_id    = var.pingone_environment_id
  sign_on_policy_id = pingone_sign_on_policy.risk_based_revocation.id
  priority          = 1

  mfa {
    device_sign_on_policy_id = pingone_mfa_device_policy.phishing_resistant.id
    no_device_mode           = "BLOCK"
  }

  conditions {
    last_sign_on_older_than_seconds = 3600
  }
}

# L2+: Custom risk predictor for unusual token issuance volume
resource "pingone_risk_predictor" "token_velocity" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH Token Velocity"
  compact_name   = "hthTokenVelocity"

  predictor_velocity {
    measure = "DISTINCT_COUNT"
    of      = "$${event.ip}"
    by      = ["$${event.user.id}"]

    every = {
      min_sample = 5
      quantity   = 1
      unit       = "HOUR"
    }

    sliding_window = {
      min_sample = 5
      quantity   = 7
      unit       = "DAY"
    }

    use = {
      type = "POISSON_WITH_MAX"
      medium = 2.0
      high   = 4.0
    }
  }
}

Profile Level: L2 (Hardened) NIST 800-53: AC-6

Description

Control OAuth consent to prevent unauthorized application access.

ClickOps Implementation

Step 1: Enable Admin Consent Requirement

  1. Navigate to: Applications → Settings
  2. Enable: Require admin consent for new applications
  3. Configure approval workflow

Step 2: Review Existing Consents

  1. Navigate to: Identities → User → Authorized Applications
  2. Audit granted permissions
  3. Revoke unnecessary or suspicious consents
Code Pack: Terraform
hth-ping-identity-3.03-oauth-consent-management.tf View source on GitHub ↗
# Resource scope requiring admin consent for sensitive operations (L2+)
resource "pingone_resource_scope" "admin_consent_required" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  resource_id    = pingone_resource.hardened_api.id
  name           = "admin"
  description    = "Administrative scope requiring admin consent"
}

# Application resource grant -- explicitly grant only approved scopes
resource "pingone_application_resource_grant" "limited_scopes" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  application_id = pingone_application.hardened_oidc.id
  resource_id    = pingone_resource.hardened_api.id

  scopes = [
    pingone_resource_scope.read.id,
  ]
}

# OpenID Connect scopes -- restrict to minimum required
resource "pingone_application_resource_grant" "oidc_scopes" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  application_id = pingone_application.hardened_oidc.id
  resource_id    = data.pingone_resource.openid.id

  scopes = [
    data.pingone_resource_scope.openid.id,
    data.pingone_resource_scope.profile.id,
  ]
}

# Data sources for OpenID scopes
data "pingone_resource" "openid" {
  environment_id = var.pingone_environment_id
  name           = "openid"
}

data "pingone_resource_scope" "openid" {
  environment_id = var.pingone_environment_id
  resource_id    = data.pingone_resource.openid.id
  name           = "openid"
}

data "pingone_resource_scope" "profile" {
  environment_id = var.pingone_environment_id
  resource_id    = data.pingone_resource.openid.id
  name           = "profile"
}

# L3+: Require admin consent for all non-standard scopes
resource "pingone_resource_scope" "elevated_consent" {
  count = var.profile_level >= 3 ? 1 : 0

  environment_id = var.pingone_environment_id
  resource_id    = pingone_resource.hardened_api.id
  name           = "elevated"
  description    = "Elevated scope requiring explicit admin approval and justification"
}

4. DaVinci Orchestration Security

4.1 Secure DaVinci Flows

Profile Level: L2 (Hardened) NIST 800-53: AC-3, CM-3

Description

Harden PingOne DaVinci orchestration flows to prevent abuse and unauthorized workflow execution.

Rationale

Why This Matters:

  • DaVinci flows automate identity processes
  • Misconfigured flows enable privilege escalation
  • Compromised flows provide persistent backdoors

ClickOps Implementation

Step 1: Implement Flow Approval Workflow

  1. Navigate to: DaVinci → Settings
  2. Enable:
    • Require approval for flow changes: Yes
    • Approvers: Security team

Step 2: Audit Existing Flows

  1. Navigate to: DaVinci → Flows
  2. For each flow, verify:
    • Business justification documented
    • Minimal permissions required
    • Error handling doesn’t leak information
    • Logging enabled

Step 3: Restrict Sensitive Connectors

  1. Identify high-risk connectors:
    • User provisioning
    • Group management
    • Password reset
  2. Limit to approved flows only
  3. Require additional authentication for sensitive actions

Step 4: Enable Flow Logging

  1. Navigate to: DaVinci → Settings → Logging
  2. Enable:
    • Log all flow executions: Yes
    • Include input/output: Masked sensitive data
    • Retention: 90 days minimum
Code Pack: Terraform
hth-ping-identity-4.01-secure-davinci-flows.tf View source on GitHub ↗
# DaVinci flow policy -- restrict flow execution to approved applications (L2+)
resource "pingone_application" "davinci_restricted" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH DaVinci Restricted Application"
  enabled        = true
  description    = "Hardened DaVinci application with restricted connector access"

  oidc_options {
    type                        = "WEB_APP"
    grant_types                 = ["AUTHORIZATION_CODE"]
    response_types              = ["CODE"]
    token_endpoint_auth_method  = "CLIENT_SECRET_POST"
    redirect_uris               = ["https://app.example.com/callback"]

    pkce_enforcement = "S256_REQUIRED"
  }
}

# Sign-on policy for DaVinci flows requiring elevated authentication
resource "pingone_sign_on_policy" "davinci_elevated" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH DaVinci Elevated Auth"
  description    = "Requires additional authentication for sensitive DaVinci flow actions"
}

# DaVinci flow action: require MFA for sensitive connector operations
resource "pingone_sign_on_policy_action" "davinci_mfa_required" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id    = var.pingone_environment_id
  sign_on_policy_id = pingone_sign_on_policy.davinci_elevated[0].id
  priority          = 1

  mfa {
    device_sign_on_policy_id = pingone_mfa_device_policy.phishing_resistant.id
    no_device_mode           = "BLOCK"
  }
}

# L3+: Strict DaVinci controls -- all flow changes require approval
resource "pingone_sign_on_policy" "davinci_strict" {
  count = var.profile_level >= 3 ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH DaVinci Strict Governance"
  description    = "Maximum security: all DaVinci changes require security team approval"
}

4.2 Version Control for Flows

Profile Level: L2 (Hardened) NIST 800-53: CM-3

Description

Implement version control and change management for DaVinci flows.

Implementation

  1. Export flows regularly to git repository
  2. Require pull request for changes
  3. Implement staging environment for testing
  4. Document rollback procedures
Code Pack: Terraform
hth-ping-identity-4.02-version-control-for-flows.tf View source on GitHub ↗
# Worker application for DaVinci flow export automation (L2+)
# Used by CI/CD pipeline to export flows to git for version control
resource "pingone_application" "davinci_export_worker" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH DaVinci Flow Export Worker"
  enabled        = true
  description    = "Service account for automated DaVinci flow export to version control"

  oidc_options {
    type                        = "WORKER"
    grant_types                 = ["CLIENT_CREDENTIALS"]
    token_endpoint_auth_method  = "CLIENT_SECRET_BASIC"
  }
}

# Grant the export worker read-only access to DaVinci resources
resource "pingone_application_role_assignment" "davinci_export_readonly" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  application_id = pingone_application.davinci_export_worker[0].id
  role_id        = data.pingone_role.identity_data_read_only.id

  scope_environment_id = var.pingone_environment_id
}

# L3+: Dedicated staging environment for flow testing before production
resource "pingone_environment" "davinci_staging" {
  count = var.profile_level >= 3 ? 1 : 0

  name        = "HTH DaVinci Staging"
  description = "Staging environment for testing DaVinci flow changes before production deployment"
  type        = "SANDBOX"
  region      = var.pingone_region

  service {
    type = "SSO"
  }

  service {
    type = "DaVinci"
  }
}

5. Monitoring & Detection

5.1 Configure Comprehensive Audit Logging

Profile Level: L1 (Baseline) NIST 800-53: AU-2, AU-3, AU-6

Description

Enable comprehensive audit logging for all identity operations.

ClickOps Implementation (PingOne)

Step 1: Configure Audit Settings

  1. Navigate to: Settings → Audit
  2. Enable:
    • Authentication events: All
    • Administrative events: All
    • API events: All
    • DaVinci flow events: All

Step 2: Configure Log Export

  1. Navigate to: Settings → Audit → Export
  2. Configure SIEM integration:
    • S3 bucket export
    • Webhook to SIEM
    • Splunk integration

Step 3: Configure Alerts

  1. Navigate to: Settings → Alerts
  2. Create alerts for:
    • Failed admin authentication (>5 in 5 minutes)
    • New application created
    • MFA policy disabled
    • High-privilege role assigned

Detection Queries

See the DB pack below for SIEM detection queries.

Code Pack: Terraform
hth-ping-identity-5.01-configure-comprehensive-audit-logging.tf View source on GitHub ↗
# Webhook for SIEM integration -- forward audit events to external systems
resource "pingone_webhook" "siem_integration" {
  count = var.siem_webhook_url != "" ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH SIEM Integration"
  enabled        = true
  http_endpoint_url = var.siem_webhook_url

  http_endpoint_headers = {
    "Content-Type" = "application/json"
  }

  # Subscribe to all security-relevant event types
  filter_options {
    included_action_types = [
      "USER.CREATED",
      "USER.UPDATED",
      "USER.DELETED",
      "USER.ACCESS_ALLOWED",
      "USER.ACCESS_DENIED",
      "USER.MFA_ENROLLMENT",
      "USER.PASSWORD_RESET",
      "APPLICATION.CREATED",
      "APPLICATION.UPDATED",
      "APPLICATION.DELETED",
      "ROLE_ASSIGNMENT.CREATED",
      "ROLE_ASSIGNMENT.DELETED",
      "POLICY.CREATED",
      "POLICY.UPDATED",
      "POLICY.DELETED",
      "FLOW.EXECUTION_COMPLETED",
      "FLOW.EXECUTION_FAILED",
    ]
  }

  format = "ACTIVITY"

  tls_client_auth_key_pair = {}

  verify_tls_certificates = true
}

# Alert rule: failed admin authentication (>5 in 5 minutes)
resource "pingone_notification_settings_email" "security_alerts" {
  environment_id = var.pingone_environment_id

  host     = "smtp.example.com"
  port     = 587
  username = "alerts@example.com"
  password = ""
  from     = { address = "pingone-alerts@example.com", name = "PingOne Security" }
}

# L2+: Extended logging with input/output capture (masked)
resource "pingone_webhook" "extended_logging" {
  count = var.profile_level >= 2 && var.siem_webhook_url != "" ? 1 : 0

  environment_id    = var.pingone_environment_id
  name              = "HTH Extended Audit Logging"
  enabled           = true
  http_endpoint_url = var.siem_webhook_url

  http_endpoint_headers = {
    "Content-Type" = "application/json"
    "X-HTH-Level"  = "extended"
  }

  # Extended: capture all event types for comprehensive audit trail
  filter_options {
    included_action_types = [
      "USER.CREATED",
      "USER.UPDATED",
      "USER.DELETED",
      "USER.ACCESS_ALLOWED",
      "USER.ACCESS_DENIED",
      "USER.MFA_ENROLLMENT",
      "USER.PASSWORD_RESET",
      "APPLICATION.CREATED",
      "APPLICATION.UPDATED",
      "APPLICATION.DELETED",
      "ROLE_ASSIGNMENT.CREATED",
      "ROLE_ASSIGNMENT.DELETED",
      "POLICY.CREATED",
      "POLICY.UPDATED",
      "POLICY.DELETED",
      "FLOW.CREATED",
      "FLOW.UPDATED",
      "FLOW.DELETED",
      "FLOW.EXECUTION_COMPLETED",
      "FLOW.EXECUTION_FAILED",
      "KEY.CREATED",
      "KEY.UPDATED",
      "KEY.DELETED",
      "GRANT.CREATED",
      "GRANT.DELETED",
    ]
  }

  format = "ACTIVITY"

  tls_client_auth_key_pair = {}

  verify_tls_certificates = true
}

# L3+: Real-time alerting on critical security events
resource "pingone_webhook" "critical_alerts" {
  count = var.profile_level >= 3 && var.siem_webhook_url != "" ? 1 : 0

  environment_id    = var.pingone_environment_id
  name              = "HTH Critical Security Alerts"
  enabled           = true
  http_endpoint_url = var.siem_webhook_url

  http_endpoint_headers = {
    "Content-Type"  = "application/json"
    "X-HTH-Level"   = "critical"
    "X-HTH-Priority" = "1"
  }

  filter_options {
    included_action_types = [
      "ROLE_ASSIGNMENT.CREATED",
      "POLICY.DELETED",
      "APPLICATION.DELETED",
      "KEY.DELETED",
      "FLOW.DELETED",
    ]
  }

  format = "ACTIVITY"

  tls_client_auth_key_pair = {}

  verify_tls_certificates = true
}

6. Third-Party Integration Security

6.1 SP Connection Hardening

Profile Level: L1 (Baseline)

Description

Harden Service Provider (SP) connections in federation.

For Each SP Connection:

  • ✅ Verify SP certificate validity
  • ✅ Configure audience restriction
  • ✅ Set minimum assertion validity
  • ✅ Enable encryption (L2)
  • ✅ Document business owner
Code Pack: Terraform
hth-ping-identity-6.01-sp-connection-hardening.tf View source on GitHub ↗
# Hardened SAML SP connection template with all required validations
resource "pingone_application" "hardened_sp_connection" {
  environment_id = var.pingone_environment_id
  name           = "HTH Hardened SP Connection"
  enabled        = true
  description    = "Template SP connection with audience restriction, signed assertions, minimum validity"

  saml_options {
    acs_urls           = ["https://sp.example.com/saml/acs"]
    assertion_duration = var.saml_assertion_validity_seconds
    sp_entity_id       = "https://sp.example.com"

    # Require signed responses and assertions
    response_is_signed = true
    assertion_signed   = true

    # SLO configuration
    slo_binding  = "HTTP_REDIRECT"
    slo_endpoint = "https://sp.example.com/saml/slo"

    # Signing configuration using hardened key
    idp_signing_key {
      key_id    = pingone_key.saml_signing.id
      algorithm = "SHA256withRSA"
    }

    # Require signed authn requests from SP
    sp_verification {
      authn_request_signed = true
    }
  }
}

# L2+: SP connection with encrypted assertions
resource "pingone_application" "encrypted_sp_connection" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH Encrypted SP Connection"
  enabled        = true
  description    = "SP connection with encrypted SAML assertions for sensitive applications"

  saml_options {
    acs_urls           = ["https://secure-sp.example.com/saml/acs"]
    assertion_duration = var.saml_assertion_validity_seconds
    sp_entity_id       = "https://secure-sp.example.com"

    response_is_signed = true
    assertion_signed   = true

    slo_binding  = "HTTP_REDIRECT"
    slo_endpoint = "https://secure-sp.example.com/saml/slo"

    idp_signing_key {
      key_id    = pingone_key.saml_signing.id
      algorithm = "SHA256withRSA"
    }

    sp_verification {
      authn_request_signed = true
    }

    # Encryption for L2+ environments
    sp_encryption {
      algorithm     = "AES_256"
      key_transport = {
        algorithm = "RSA_OAEP"
      }
    }
  }
}

6.2 API Client Management

Client Type Token Lifetime Scopes Controls
SCIM Provisioner 1 hour Users, Groups IP restriction, audit logging
SSO Application 4 hours OpenID, Profile Standard validation
Admin API 15 minutes Admin scopes MFA required, IP restriction
Reporting 1 hour Read-only Dedicated service account
Code Pack: Terraform
hth-ping-identity-6.02-api-client-management.tf View source on GitHub ↗
# SCIM Provisioner -- 1 hour token, IP-restricted, audit logging
resource "pingone_application" "scim_provisioner" {
  environment_id = var.pingone_environment_id
  name           = "HTH SCIM Provisioner"
  enabled        = true
  description    = "SCIM provisioning client with restricted scopes and short token lifetime"

  oidc_options {
    type                        = "WORKER"
    grant_types                 = ["CLIENT_CREDENTIALS"]
    token_endpoint_auth_method  = "CLIENT_SECRET_BASIC"
  }
}

# SCIM provisioner role -- limited to identity data operations
resource "pingone_application_role_assignment" "scim_identity_admin" {
  environment_id = var.pingone_environment_id
  application_id = pingone_application.scim_provisioner.id
  role_id        = data.pingone_role.identity_data_admin.id

  scope_environment_id = var.pingone_environment_id
}

# Admin API client -- 15 minute token, MFA required, IP-restricted
resource "pingone_application" "admin_api_client" {
  environment_id = var.pingone_environment_id
  name           = "HTH Admin API Client"
  enabled        = true
  description    = "Administrative API client with minimal token lifetime and restricted access"

  oidc_options {
    type                        = "WORKER"
    grant_types                 = ["CLIENT_CREDENTIALS"]
    token_endpoint_auth_method  = "CLIENT_SECRET_BASIC"
  }
}

# Admin API role assignment -- environment admin scoped
resource "pingone_application_role_assignment" "admin_api_role" {
  environment_id = var.pingone_environment_id
  application_id = pingone_application.admin_api_client.id
  role_id        = data.pingone_role.environment_admin.id

  scope_environment_id = var.pingone_environment_id
}

# Reporting client -- read-only, 1 hour token, dedicated service account
resource "pingone_application" "reporting_client" {
  environment_id = var.pingone_environment_id
  name           = "HTH Reporting Client"
  enabled        = true
  description    = "Read-only reporting client for dashboards and compliance reports"

  oidc_options {
    type                        = "WORKER"
    grant_types                 = ["CLIENT_CREDENTIALS"]
    token_endpoint_auth_method  = "CLIENT_SECRET_BASIC"
  }
}

# Reporting client role -- read-only access
resource "pingone_application_role_assignment" "reporting_readonly" {
  environment_id = var.pingone_environment_id
  application_id = pingone_application.reporting_client.id
  role_id        = data.pingone_role.identity_data_read_only.id

  scope_environment_id = var.pingone_environment_id
}

# L2+: SSO Application with standard validation and 4-hour token
resource "pingone_application" "sso_app_template" {
  count = var.profile_level >= 2 ? 1 : 0

  environment_id = var.pingone_environment_id
  name           = "HTH SSO Application Template"
  enabled        = true
  description    = "Standard SSO application with OpenID and Profile scopes"

  oidc_options {
    type                        = "WEB_APP"
    grant_types                 = ["AUTHORIZATION_CODE", "REFRESH_TOKEN"]
    response_types              = ["CODE"]
    token_endpoint_auth_method  = "CLIENT_SECRET_POST"
    redirect_uris               = ["https://app.example.com/callback"]

    pkce_enforcement = "S256_REQUIRED"

    refresh_token_duration         = 14400
    refresh_token_rolling_duration = 14400
  }
}

7. Compliance Quick Reference

SOC 2 Mapping

Control ID Ping Identity Control Guide Section
CC6.1 MFA enforcement 1.1
CC6.2 RBAC 1.2
CC6.6 IP restrictions 1.3
CC7.2 Audit logging 5.1

NIST 800-53 Mapping

Control Ping Identity Control Guide Section
IA-2(6) Phishing-resistant MFA 1.1
IA-5 Federation security 2.1
SC-23 Token security 3.1
AU-2 Audit logging 5.1

Appendix A: Edition Compatibility

Control PingOne Essentials PingOne Plus PingOne Enterprise
MFA
FIDO2
DaVinci Limited
Risk-Based Auth
API Access Limited

Appendix B: References

Official Ping Identity Documentation:

API Documentation:

Compliance Frameworks:

  • SOC 2 Type II, ISO 27001, ISO 27018, CSA STAR Level 2 — via Trust Center

Security Incidents:

  • No major public security incidents involving Ping Identity infrastructure have been identified. Ping Identity operates a bug bounty program through HackerOne.

Changelog

Date Version Maturity Changes Author
2025-12-14 0.1.0 draft Initial Ping Identity hardening guide Claude Code (Opus 4.5)