identity access management

Implementing Google Workspace SSO Configuration

Configure SAML 2.0 single sign-on for Google Workspace with a third-party identity provider, enabling centralized authentication and enforcing organization-wide access policies.

authenticationfederationgoogle-workspaceidentity-providersamlsso
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Single Sign-On (SSO) for Google Workspace allows organizations to authenticate users through their existing identity provider (IdP) such as Okta, Azure AD (Microsoft Entra ID), or ADFS, rather than managing separate Google passwords. This is implemented using SAML 2.0 protocol where Google Workspace acts as the Service Provider (SP) and the organization's IdP handles authentication. SSO centralizes credential management, enforces MFA policies at the IdP, and enables immediate access revocation when users leave the organization.

When to Use

  • When deploying or configuring implementing google workspace sso configuration capabilities in your environment
  • When establishing security controls aligned to compliance requirements
  • When building or improving security architecture for this domain
  • When conducting security assessments that require this implementation

Prerequisites

  • Google Workspace Business, Enterprise, or Education edition
  • Super Admin access to Google Admin Console
  • Identity Provider with SAML 2.0 support (Okta, Azure AD, ADFS, Ping Identity)
  • IdP signing certificate (X.509 PEM format, RSA or DSA)
  • DNS verification for the Google Workspace domain

Core Concepts

SAML 2.0 SSO Flow

User navigates to Google Workspace app (Gmail, Drive, etc.)

        ├── Google checks: Is SSO configured for this domain?

        ├── YES → Redirect user to IdP Sign-In Page URL
        │          (SAML AuthnRequest sent via browser redirect)

        ├── User authenticates at IdP (credentials + MFA)

        ├── IdP generates SAML Response with signed assertion

        ├── Browser POSTs SAML Response to Google ACS URL:
        │   https://www.google.com/a/{domain}/acs

        ├── Google validates SAML signature against uploaded certificate

        └── User is granted access to Google Workspace

Key SAML Parameters

Parameter Value
ACS URL https://www.google.com/a/{your-domain}/acs
Entity ID google.com/a/{your-domain} or google.com
NameID Format urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
NameID Value User's primary Google Workspace email
Binding HTTP-POST (for ACS), HTTP-Redirect (for SSO URL)

Workflow

Step 1: Prepare the Identity Provider

For Okta:

  1. Navigate to Applications > Add Application > Search "Google Workspace"
  2. Configure the Google Workspace app with your domain
  3. Assign users/groups to the application
  4. Download the IdP metadata or note: SSO URL, Entity ID, Certificate

For Azure AD (Microsoft Entra ID):

  1. Navigate to Enterprise Applications > New Application > Google Cloud/Workspace
  2. Configure Single sign-on > SAML
  3. Set Basic SAML Configuration:
    • Identifier (Entity ID): google.com
    • Reply URL (ACS): https://www.google.com/a/{your-domain}/acs
    • Sign on URL: https://www.google.com/a/{your-domain}/ServiceLogin
  4. Download Federation Metadata XML or Certificate (Base64)

For ADFS:

  1. Add Relying Party Trust using federation metadata
  2. Configure claim rules to pass NameID as email address
  3. Export the token-signing certificate

Step 2: Configure Google Workspace SSO

  1. Sign in to Google Admin Console (admin.google.com) as Super Admin
  2. Navigate to Security > Authentication > SSO with third-party IdP
  3. Click "Add SSO profile" or configure the default profile

Third-Party SSO Profile Settings:

Setting Value
Set up SSO with third-party IdP Enabled
Sign-in page URL IdP's SAML SSO endpoint (e.g., https://idp.example.com/sso/saml)
Sign-out page URL IdP's logout URL (e.g., https://idp.example.com/slo)
Change password URL IdP's password change URL
Verification certificate Upload IdP's X.509 signing certificate
Use a domain-specific issuer Enabled (uses google.com/a/{domain} as entity ID)

Step 3: Assign SSO Profile to Users

SSO profiles can be applied at different scopes:

Organization-wide (all users)

    ├── Org Unit level (specific departments)
    │   ├── Engineering OU → SSO via Okta
    │   ├── Marketing OU → SSO via Azure AD
    │   └── Contractors OU → SSO via specific IdP

    └── Group level (specific security groups)
        └── VPN Users → SSO with additional MFA
  1. Navigate to Security > Authentication > SSO with third-party IdP
  2. Select the SSO profile to assign
  3. Choose organizational units or groups
  4. Save and wait for propagation (up to 24 hours, typically minutes)

Step 4: Configure Network Masks (Optional)

Network masks control when SSO is enforced based on the user's IP:

  • If the user's IP matches a network mask, they use Google's sign-in page
  • If the user's IP does NOT match, they are redirected to the IdP

This is useful for allowing direct Google login from corporate network while enforcing SSO for external access.

Step 5: Test SSO

  1. Open an incognito browser window
  2. Navigate to https://mail.google.com/a/{your-domain}
  3. Verify redirect to IdP sign-in page
  4. Authenticate at the IdP
  5. Verify successful redirect back to Google Workspace
  6. Test sign-out flow redirects to IdP logout page
  7. Test with user not assigned in IdP (should fail)

Validation Checklist

  • IdP SAML application configured with correct ACS URL and Entity ID
  • IdP signing certificate uploaded to Google Admin Console
  • SSO profile assigned to target organizational units/groups
  • SAML assertion includes correct NameID (email format)
  • MFA enforced at IdP for all Google Workspace users
  • Sign-out URL configured to terminate IdP session
  • Network masks configured if internal/external access differs
  • Break-glass Super Admin accounts bypass SSO (use Google auth)
  • SSO tested with multiple user types (admin, standard, contractor)
  • SAML response signature validated successfully
  • Error handling tested (expired cert, invalid user, clock skew)

References

Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md1.8 KB

API Reference: Implementing Google Workspace SSO Configuration

SAML 2.0 Endpoints

Endpoint URL
SP ACS URL https://accounts.google.com/samlrp/acs?rpid=RPID
SP Entity ID google.com/a/DOMAIN
SP Metadata https://accounts.google.com/samlrp/metadata?rpid=RPID

Admin Console Path

Admin Console > Security > Authentication > SSO with third-party IdP

SAML Configuration Fields

Field Description
Sign-in page URL IdP SSO endpoint (HTTPS required)
Sign-out page URL IdP SLO endpoint
Change password URL IdP password change page
Verification certificate IdP X.509 signing cert (PEM, RSA 2048+)
Domain-specific issuer Use domain in SAML issuer

Certificate Validation (Python cryptography)

from cryptography import x509
cert = x509.load_pem_x509_certificate(pem_data)
print(cert.not_valid_after_utc)
print(cert.subject.rfc4514_string())
print(cert.public_key().key_size)

Admin SDK Reports API (Login Activity)

from googleapiclient.discovery import build
service = build("admin", "reports_v1", credentials=creds)
activities = service.activities().list(
    userKey="all", applicationName="login",
    eventName="login_success").execute()

Common IdP Providers

IdP SAML SSO URL Pattern
Okta https://DOMAIN.okta.com/app/APP_ID/sso/saml
Azure AD https://login.microsoftonline.com/TENANT/saml2
ADFS https://ADFS_HOST/adfs/ls/
Ping Identity https://sso.connect.pingidentity.com/sso/sp/initsso

References

standards.md1.4 KB

Google Workspace SSO - Standards Reference

SAML 2.0 Standard

OASIS SAML 2.0 Core

  • Assertions: Authentication statements, attribute statements
  • Protocols: AuthnRequest, Response, LogoutRequest
  • Bindings: HTTP Redirect, HTTP POST, Artifact
  • Profiles: Web Browser SSO, Single Logout

Google Workspace SAML Requirements

  • SAML 2.0 compliant IdP
  • HTTP POST binding for Assertion Consumer Service
  • Signed SAML assertions (RSA-SHA256 recommended)
  • NameID format: emailAddress (user's primary email)
  • X.509 PEM certificate for signature verification

Google Workspace SSO Parameters

Parameter Value
ACS URL https://www.google.com/a/{domain}/acs
Entity ID (domain-specific) google.com/a/{domain}
Entity ID (generic) google.com
NameID Format urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
SAML Version 2.0
Binding HTTP-POST

Compliance Mapping

NIST SP 800-63-3 (Digital Identity Guidelines)

  • AAL2: Multi-factor authentication (enforced at IdP)
  • Federation assurance levels (FAL1-FAL3)
  • Assertion protection requirements

SOC 2 - CC6.1

  • Single sign-on centralizes access control
  • Audit trail of authentication events
  • Timely deprovisioning via IdP user removal

ISO 27001:2022 - A.8.5

  • Secure authentication through centralized IdP
  • MFA enforcement via SSO configuration
  • Session management controls
workflows.md3.8 KB

Google Workspace SSO - Workflows

SSO Configuration Workflow

1. PREPARE IDP
   ├── Create Google Workspace SAML application in IdP
   ├── Configure ACS URL: https://www.google.com/a/{domain}/acs
   ├── Configure Entity ID: google.com/a/{domain}
   ├── Set NameID to user email address
   ├── Map required attributes (firstName, lastName)
   └── Download IdP metadata (SSO URL, certificate, entity ID)
 
2. CONFIGURE GOOGLE ADMIN CONSOLE
   ├── Navigate to Security > Authentication > SSO with third-party IdP
   ├── Enable third-party SSO
   ├── Enter Sign-in page URL from IdP
   ├── Enter Sign-out page URL from IdP
   ├── Upload IdP verification certificate
   ├── Enable domain-specific issuer
   └── Save configuration
 
3. ASSIGN SSO PROFILE
   ├── Apply to entire organization OR
   ├── Apply to specific organizational units OR
   └── Apply to specific groups
 
4. TEST
   ├── Test IdP-initiated SSO (login from IdP portal)
   ├── Test SP-initiated SSO (login from Google page)
   ├── Test sign-out flow
   ├── Test with user not in IdP (should fail)
   └── Test break-glass Super Admin access (should bypass SSO)
 
5. ROLLOUT
   ├── Communicate changes to users
   ├── Apply to all organizational units
   ├── Monitor for authentication failures
   └── Update help desk with troubleshooting guide

User Authentication Flow (SP-Initiated)

User navigates to mail.google.com/a/{domain}

    ├── Google identifies federated domain

    ├── Redirect to IdP with SAML AuthnRequest
    │   URL: {IdP SSO URL}?SAMLRequest={base64encoded}

    ├── User authenticates at IdP:
    │   ├── Enter credentials
    │   ├── Complete MFA challenge
    │   └── IdP validates against directory

    ├── IdP generates SAML Response:
    │   ├── Assertion with NameID (email)
    │   ├── Authentication context (MFA)
    │   ├── Digitally signed with IdP certificate
    │   └── Optionally encrypted

    ├── Browser POSTs Response to Google ACS URL

    ├── Google validates:
    │   ├── Signature against uploaded certificate
    │   ├── Assertion not expired
    │   ├── Audience matches entity ID
    │   ├── NameID matches a Google Workspace user
    │   └── InResponseTo matches original request

    └── User logged in to Google Workspace

Certificate Renewal Workflow

IdP signing certificate approaching expiration (30 days before)

    ├── Generate new signing certificate in IdP

    ├── Upload new certificate to Google Admin Console
    │   (Google supports multiple verification certificates)

    ├── Promote new certificate as primary in IdP

    ├── Verify SSO still works with new certificate

    └── Remove old certificate from Google Admin Console after confirmation

Troubleshooting Workflow

User reports SSO failure

    ├── Check 1: Is user assigned to the Google Workspace app in IdP?
    │   └── NO → Assign user in IdP

    ├── Check 2: Does NameID match user's Google email exactly?
    │   └── NO → Fix attribute mapping in IdP

    ├── Check 3: Is the IdP certificate expired?
    │   └── YES → Upload renewed certificate

    ├── Check 4: Is there clock skew between IdP and Google?
    │   └── YES → Sync NTP on IdP server (max 5 min skew allowed)

    ├── Check 5: Is the SAML assertion properly signed?
    │   └── NO → Verify IdP signing algorithm matches uploaded cert

    └── Check 6: Check IdP SAML debug logs for detailed error

Scripts 2

agent.py6.9 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for auditing and configuring Google Workspace SAML SSO."""

import json
import argparse
from datetime import datetime
from pathlib import Path

try:
    from cryptography import x509
except ImportError:
    x509 = None


def parse_saml_certificate(cert_path):
    """Parse and validate an IdP SAML signing certificate."""
    if not x509:
        return {"error": "cryptography library not available"}
    with open(cert_path, "rb") as f:
        pem_data = f.read()
    cert = x509.load_pem_x509_certificate(pem_data)
    now = datetime.utcnow()
    return {
        "subject": cert.subject.rfc4514_string(),
        "issuer": cert.issuer.rfc4514_string(),
        "not_before": str(cert.not_valid_before_utc),
        "not_after": str(cert.not_valid_after_utc),
        "serial": str(cert.serial_number),
        "key_size": cert.public_key().key_size if hasattr(cert.public_key(), "key_size") else None,
        "expired": cert.not_valid_after_utc.replace(tzinfo=None) < now,
        "days_until_expiry": (cert.not_valid_after_utc.replace(tzinfo=None) - now).days,
        "signature_algorithm": cert.signature_algorithm_oid.dotted_string,
    }


def audit_sso_config(config_path):
    """Audit Google Workspace SSO configuration."""
    with open(config_path) as f:
        config = json.load(f)
    findings = []
    sso = config.get("sso", config)

    if not sso.get("sso_enabled", sso.get("enabled", False)):
        findings.append({"issue": "SSO not enabled", "severity": "HIGH"})

    sign_in_url = sso.get("sign_in_page_url", sso.get("sso_url", ""))
    if sign_in_url and not sign_in_url.startswith("https://"):
        findings.append({"issue": "SSO sign-in URL not HTTPS", "severity": "CRITICAL"})

    sign_out_url = sso.get("sign_out_page_url", sso.get("logout_url", ""))
    if not sign_out_url:
        findings.append({"issue": "Sign-out URL not configured", "severity": "MEDIUM"})

    cert_path = sso.get("verification_certificate", sso.get("certificate_path", ""))
    if cert_path and Path(cert_path).exists():
        cert_info = parse_saml_certificate(cert_path)
        if cert_info.get("expired"):
            findings.append({"issue": "IdP certificate expired", "severity": "CRITICAL",
                             "detail": cert_info})
        elif cert_info.get("days_until_expiry", 999) < 30:
            findings.append({"issue": f"IdP cert expires in {cert_info['days_until_expiry']} days",
                             "severity": "HIGH", "detail": cert_info})
        key_size = cert_info.get("key_size", 0)
        if key_size and key_size < 2048:
            findings.append({"issue": f"Weak IdP cert key size: {key_size}", "severity": "HIGH"})
    elif not cert_path:
        findings.append({"issue": "No verification certificate configured", "severity": "CRITICAL"})

    if not sso.get("use_domain_specific_issuer", True):
        findings.append({"issue": "Domain-specific issuer not enabled", "severity": "MEDIUM"})

    if sso.get("allow_password_auth_when_sso_enabled", True):
        findings.append({"issue": "Password auth still allowed alongside SSO", "severity": "MEDIUM",
                         "recommendation": "Disable direct password login for SSO users"})

    if not findings:
        findings.append({"issue": "No issues found", "severity": "INFO"})
    return findings


def generate_saml_config(idp_entity_id, sso_url, slo_url, cert_path, domain):
    """Generate Google Workspace SAML SSO configuration."""
    return {
        "sso_enabled": True,
        "sign_in_page_url": sso_url,
        "sign_out_page_url": slo_url,
        "change_password_url": f"https://{idp_entity_id}/change-password",
        "verification_certificate": cert_path,
        "use_domain_specific_issuer": True,
        "domain": domain,
        "saml_settings": {
            "idp_entity_id": idp_entity_id,
            "sp_entity_id": f"google.com/a/{domain}",
            "acs_url": f"https://accounts.google.com/samlrp/acs?rpid=RPID",
            "name_id_format": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress",
        },
    }


def audit_sso_login_activity(log_path):
    """Audit SSO login activity for anomalies."""
    with open(log_path) as f:
        events = json.load(f)
    items = events if isinstance(events, list) else events.get("events", [])
    total = len(items)
    failures = [e for e in items if e.get("status") in ("failed", "error", "FAILED")]
    sso_logins = [e for e in items if e.get("auth_method") == "saml_sso"]
    password_logins = [e for e in items if e.get("auth_method") == "password"]
    return {
        "total_logins": total,
        "sso_logins": len(sso_logins),
        "password_logins": len(password_logins),
        "sso_adoption_rate": round(len(sso_logins) / total * 100, 1) if total else 0,
        "failures": len(failures),
        "failure_rate": round(len(failures) / total * 100, 1) if total else 0,
        "recent_failures": failures[:10],
    }


def main():
    parser = argparse.ArgumentParser(description="Google Workspace SSO Agent")
    parser.add_argument("--config", help="SSO config JSON to audit")
    parser.add_argument("--cert", help="IdP SAML certificate PEM file")
    parser.add_argument("--login-log", help="Login activity log JSON")
    parser.add_argument("--action", choices=["audit", "cert", "activity", "generate", "full"],
                        default="full")
    parser.add_argument("--idp-url", help="IdP SSO URL")
    parser.add_argument("--domain", help="Google Workspace domain")
    parser.add_argument("--output", default="gws_sso_report.json")
    args = parser.parse_args()

    report = {"generated_at": datetime.utcnow().isoformat(), "results": {}}

    if args.action in ("audit", "full") and args.config:
        findings = audit_sso_config(args.config)
        report["results"]["audit"] = findings
        issues = sum(1 for f in findings if f["severity"] not in ("INFO",))
        print(f"[+] SSO audit: {issues} issues found")

    if args.action in ("cert", "full") and args.cert:
        cert_info = parse_saml_certificate(args.cert)
        report["results"]["certificate"] = cert_info
        status = "EXPIRED" if cert_info.get("expired") else "VALID"
        print(f"[+] Certificate: {status}, expires in {cert_info.get('days_until_expiry')} days")

    if args.action in ("activity", "full") and args.login_log:
        activity = audit_sso_login_activity(args.login_log)
        report["results"]["activity"] = activity
        print(f"[+] SSO adoption: {activity['sso_adoption_rate']}%")

    if args.action == "generate" and args.idp_url and args.domain:
        config = generate_saml_config("idp", args.idp_url, "", args.cert or "", args.domain)
        report["results"]["generated_config"] = config
        print("[+] SAML config generated")

    with open(args.output, "w") as f:
        json.dump(report, f, indent=2, default=str)
    print(f"[+] Report saved to {args.output}")


if __name__ == "__main__":
    main()
process.py8.1 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Google Workspace SSO Configuration Validator

Validates SAML SSO configuration between an IdP and Google Workspace by
checking SAML metadata, testing authentication flows, and verifying
certificate validity.

Requirements:
    pip install requests cryptography lxml
"""

import base64
import json
import sys
import zlib
from datetime import datetime, timezone
from urllib.parse import urlencode, parse_qs, urlparse

try:
    import requests
    from cryptography import x509
    from cryptography.hazmat.primitives import serialization
except ImportError:
    print("[ERROR] Required: pip install requests cryptography")
    sys.exit(1)


class GoogleWorkspaceSSOValidator:
    """Validate Google Workspace SAML SSO configuration."""

    def __init__(self, domain):
        self.domain = domain
        self.acs_url = f"https://www.google.com/a/{domain}/acs"
        self.entity_id = f"google.com/a/{domain}"

    def validate_idp_metadata(self, metadata_url):
        """Fetch and validate IdP SAML metadata XML."""
        try:
            from lxml import etree
        except ImportError:
            return {"valid": False, "error": "lxml required for XML parsing"}

        try:
            resp = requests.get(metadata_url, timeout=15)
            resp.raise_for_status()
        except requests.RequestException as e:
            return {"valid": False, "error": f"Cannot fetch metadata: {e}"}

        try:
            root = etree.fromstring(resp.content)
        except etree.XMLSyntaxError as e:
            return {"valid": False, "error": f"Invalid XML: {e}"}

        ns = {
            "md": "urn:oasis:names:tc:SAML:2.0:metadata",
            "ds": "http://www.w3.org/2000/09/xmldsig#",
        }

        entity_id = root.get("entityID")
        sso_elements = root.findall(
            ".//md:IDPSSODescriptor/md:SingleSignOnService", ns
        )
        sso_urls = [
            {"binding": el.get("Binding"), "location": el.get("Location")}
            for el in sso_elements
        ]

        cert_elements = root.findall(
            ".//md:IDPSSODescriptor/md:KeyDescriptor/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
            ns,
        )
        certificates = [el.text.strip() for el in cert_elements if el.text]

        return {
            "valid": True,
            "entity_id": entity_id,
            "sso_endpoints": sso_urls,
            "certificate_count": len(certificates),
            "has_post_binding": any(
                "HTTP-POST" in s["binding"] for s in sso_urls
            ),
            "has_redirect_binding": any(
                "HTTP-Redirect" in s["binding"] for s in sso_urls
            ),
        }

    def validate_certificate(self, cert_pem):
        """Validate the IdP signing certificate."""
        try:
            if "-----BEGIN CERTIFICATE-----" not in cert_pem:
                cert_pem = (
                    "-----BEGIN CERTIFICATE-----\n"
                    + cert_pem
                    + "\n-----END CERTIFICATE-----"
                )

            cert = x509.load_pem_x509_certificate(cert_pem.encode())
            now = datetime.now(timezone.utc)

            return {
                "valid": True,
                "subject": cert.subject.rfc4514_string(),
                "issuer": cert.issuer.rfc4514_string(),
                "not_before": cert.not_valid_before_utc.isoformat(),
                "not_after": cert.not_valid_after_utc.isoformat(),
                "is_expired": now > cert.not_valid_after_utc,
                "days_until_expiry": (cert.not_valid_after_utc - now).days,
                "serial_number": str(cert.serial_number),
                "signature_algorithm": cert.signature_algorithm_oid._name,
                "key_size": cert.public_key().key_size,
            }
        except Exception as e:
            return {"valid": False, "error": str(e)}

    def validate_sso_configuration(self, idp_sso_url, idp_entity_id,
                                     cert_pem):
        """Run complete SSO configuration validation."""
        results = {
            "domain": self.domain,
            "acs_url": self.acs_url,
            "entity_id": self.entity_id,
            "checks": [],
        }

        # Check 1: ACS URL format
        results["checks"].append({
            "check": "ACS URL format",
            "expected": f"https://www.google.com/a/{self.domain}/acs",
            "status": "PASS",
        })

        # Check 2: Entity ID format
        results["checks"].append({
            "check": "Entity ID format",
            "expected": f"google.com/a/{self.domain}",
            "status": "PASS",
        })

        # Check 3: IdP SSO URL is HTTPS
        is_https = idp_sso_url.startswith("https://")
        results["checks"].append({
            "check": "IdP SSO URL uses HTTPS",
            "value": idp_sso_url,
            "status": "PASS" if is_https else "FAIL",
        })

        # Check 4: IdP SSO URL is reachable
        try:
            resp = requests.head(idp_sso_url, timeout=10, allow_redirects=True)
            reachable = resp.status_code < 500
        except requests.RequestException:
            reachable = False
        results["checks"].append({
            "check": "IdP SSO URL reachable",
            "status": "PASS" if reachable else "FAIL",
        })

        # Check 5: Certificate validation
        cert_result = self.validate_certificate(cert_pem)
        results["checks"].append({
            "check": "IdP certificate valid",
            "status": "PASS" if cert_result.get("valid") and not cert_result.get("is_expired") else "FAIL",
            "details": cert_result,
        })

        # Check 6: Certificate expiry warning
        days_left = cert_result.get("days_until_expiry", 0)
        results["checks"].append({
            "check": "Certificate expiry > 30 days",
            "days_remaining": days_left,
            "status": "PASS" if days_left > 30 else "WARN",
        })

        # Check 7: Key size
        key_size = cert_result.get("key_size", 0)
        results["checks"].append({
            "check": "Certificate key size >= 2048",
            "key_size": key_size,
            "status": "PASS" if key_size >= 2048 else "FAIL",
        })

        all_pass = all(
            c["status"] in ("PASS", "WARN") for c in results["checks"]
        )
        results["overall_status"] = "PASS" if all_pass else "FAIL"

        return results

    def generate_saml_authn_request(self, idp_sso_url):
        """Generate a SAML AuthnRequest for testing SP-initiated SSO."""
        request_id = f"_google_workspace_test_{int(datetime.now().timestamp())}"
        issue_instant = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")

        authn_request = f"""<samlp:AuthnRequest
    xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
    xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
    ID="{request_id}"
    Version="2.0"
    IssueInstant="{issue_instant}"
    AssertionConsumerServiceURL="{self.acs_url}"
    Destination="{idp_sso_url}"
    ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST">
    <saml:Issuer>{self.entity_id}</saml:Issuer>
    <samlp:NameIDPolicy
        Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
        AllowCreate="true"/>
</samlp:AuthnRequest>"""

        compressed = zlib.compress(authn_request.encode())[2:-4]
        encoded = base64.b64encode(compressed).decode()
        sso_redirect_url = f"{idp_sso_url}?{urlencode({'SAMLRequest': encoded})}"

        return {
            "request_id": request_id,
            "authn_request_xml": authn_request,
            "encoded_request": encoded,
            "redirect_url": sso_redirect_url,
        }


if __name__ == "__main__":
    print("=" * 60)
    print("Google Workspace SSO Configuration Validator")
    print("=" * 60)
    print()
    print("Usage:")
    print("  validator = GoogleWorkspaceSSOValidator('example.com')")
    print("  result = validator.validate_sso_configuration(")
    print("      idp_sso_url='https://idp.example.com/sso/saml',")
    print("      idp_entity_id='https://idp.example.com',")
    print("      cert_pem=open('idp_cert.pem').read()")
    print("  )")
    print("  print(json.dumps(result, indent=2))")

Assets 1

template.mdtext/markdown · 1.3 KB
Keep exploring