### Install gosaml2 Source: https://github.com/russellhaering/gosaml2/blob/main/README.md Use the go get command to add the library to your project. ```bash go get github.com/russellhaering/gosaml2 ``` -------------------------------- ### Initialize and Run SAML Service Provider HTTP Server Source: https://context7.com/russellhaering/gosaml2/llms.txt Sets up the SAML Service Provider by fetching IdP metadata, initializing the SP configuration, and starting an HTTP server with handlers for various SAML endpoints. Ensure the IdP metadata URL is correct and accessible. ```go package main import ( "crypto/x509" "encoding/base64" "encoding/xml" "fmt" "io/ioutil" "net/http" saml2 "github.com/russellhaering/gosaml2" "github.com/russellhaering/gosaml2/types" dsig "github.com/russellhaering/goxmldsig" ) var sp *saml2.SAMLServiceProvider func main() { // Initialize SP from IdP metadata initServiceProvider() // Routes http.HandleFunc("/saml/login", handleLogin) http.HandleFunc("/saml/acs", handleACS) http.HandleFunc("/saml/logout", handleLogout) http.HandleFunc("/saml/slo", handleSLO) http.HandleFunc("/saml/metadata", handleMetadata) fmt.Println("Server starting on :8080") http.ListenAndServe(":8080", nil) } ``` -------------------------------- ### Configure SAMLServiceProvider in Go Source: https://context7.com/russellhaering/gosaml2/llms.txt Sets up the SAMLServiceProvider by fetching IdP metadata, building a certificate store, and defining SP-specific configurations. Ensure proper key management in production environments. ```go package main import ( "crypto/x509" "encoding/base64" "encoding/xml" "io/ioutil" "net/http" saml2 "github.com/russellhaering/gosaml2" "github.com/russellhaering/gosaml2/types" dsig "github.com/russellhaering/goxmldsig" ) func main() { // Fetch IdP metadata res, err := http.Get("https://idp.example.com/metadata") if err != nil { panic(err) } rawMetadata, _ := ioutil.ReadAll(res.Body) metadata := &types.EntityDescriptor{} xml.Unmarshal(rawMetadata, metadata) // Build certificate store from IdP metadata certStore := dsig.MemoryX509CertificateStore{ Roots: []*x509.Certificate{}, } for _, kd := range metadata.IDPSSODescriptor.KeyDescriptors { for _, xcert := range kd.KeyInfo.X509Data.X509Certificates { certData, _ := base64.StdEncoding.DecodeString(xcert.Data) idpCert, _ := x509.ParseCertificate(certData) certStore.Roots = append(certStore.Roots, idpCert) } } // Create Service Provider configuration sp := &saml2.SAMLServiceProvider{ IdentityProviderSSOURL: metadata.IDPSSODescriptor.SingleSignOnServices[0].Location, IdentityProviderSLOURL: metadata.IDPSSODescriptor.SingleLogoutServices[0].Location, IdentityProviderIssuer: metadata.EntityID, ServiceProviderIssuer: "https://myapp.example.com/saml/metadata", AssertionConsumerServiceURL: "https://myapp.example.com/saml/acs", ServiceProviderSLOURL: "https://myapp.example.com/saml/slo", AudienceURI: "https://myapp.example.com/saml/metadata", IDPCertificateStore: &certStore, SPKeyStore: dsig.RandomKeyStoreForTest(), // Use proper key in production SignAuthnRequests: true, NameIdFormat: saml2.NameIdFormatEmailAddress, ForceAuthn: false, IsPassive: false, } // sp is now ready for use _ = sp } ``` -------------------------------- ### Initialize SAML Service Provider Configuration Source: https://context7.com/russellhaering/gosaml2/llms.txt Fetches Identity Provider metadata, parses it, builds a certificate store from IdP certificates, and configures the SAMLServiceProvider. This function must be called before handling any SAML requests. Ensure the IdP metadata URL is correct and accessible. ```go func initServiceProvider() { // Fetch and parse IdP metadata res, _ := http.Get("https://idp.example.com/metadata") rawMetadata, _ := ioutil.ReadAll(res.Body) metadata := &types.EntityDescriptor{} xml.Unmarshal(rawMetadata, metadata) // Build cert store certStore := dsig.MemoryX509CertificateStore{Roots: []*x509.Certificate{}} for _, kd := range metadata.IDPSSODescriptor.KeyDescriptors { for _, xcert := range kd.KeyInfo.X509Data.X509Certificates { certData, _ := base64.StdEncoding.DecodeString(xcert.Data) idpCert, _ := x509.ParseCertificate(certData) certStore.Roots = append(certStore.Roots, idpCert) } } sp = &saml2.SAMLServiceProvider{ IdentityProviderSSOURL: metadata.IDPSSODescriptor.SingleSignOnServices[0].Location, IdentityProviderIssuer: metadata.EntityID, ServiceProviderIssuer: "https://myapp.example.com/saml/metadata", AssertionConsumerServiceURL: "https://myapp.example.com/saml/acs", AudienceURI: "https://myapp.example.com/saml/metadata", IDPCertificateStore: &certStore, SPKeyStore: dsig.RandomKeyStoreForTest(), SignAuthnRequests: true, NameIdFormat: saml2.NameIdFormatEmailAddress, } } ``` -------------------------------- ### Configure SP Signing and Encryption Keys Source: https://context7.com/russellhaering/gosaml2/llms.txt Load and set cryptographic keys for signing SAML requests and decrypting assertions. This involves reading PEM-encoded private keys and certificates, then using SetSPKeyStore and SetSPSigningKeyStore. ```go import ( "crypto/rsa" "crypto/x509" "encoding/pem" "io/ioutil" ) func configureSPKeys(sp *saml2.SAMLServiceProvider) error { // Load private key keyPEM, _ := ioutil.ReadFile("sp-key.pem") keyBlock, _ := pem.Decode(keyPEM) privateKey, _ := x509.ParsePKCS1PrivateKey(keyBlock.Bytes) // Load certificate certPEM, _ := ioutil.ReadFile("sp-cert.pem") certBlock, _ := pem.Decode(certPEM) // Set encryption key store (for decrypting assertions) err := sp.SetSPKeyStore(&saml2.KeyStore{ Signer: privateKey, Cert: certBlock.Bytes, }) if err != nil { return err } // Optionally set separate signing key signingKey, _ := loadSigningKey("signing-key.pem") signingCert, _ := ioutil.ReadFile("signing-cert.der") err = sp.SetSPSigningKeyStore(&saml2.KeyStore{ Signer: signingKey, Cert: signingCert, }) if err != nil { return err } return nil } ``` -------------------------------- ### Handle SAML Login Request Source: https://context7.com/russellhaering/gosaml2/llms.txt Initiates the SAML authentication flow by redirecting the user to the Identity Provider. It captures the 'next' URL parameter to use as the RelayState for returning the user after authentication. ```go func handleLogin(w http.ResponseWriter, r *http.Request) { relayState := r.URL.Query().Get("next") if relayState == "" { relayState = "/" } sp.AuthRedirect(w, r, relayState) } ``` -------------------------------- ### Test OSS-Fuzz Integration Locally Source: https://github.com/russellhaering/gosaml2/blob/main/oss-fuzz/README.md Commands to build and execute fuzzers locally using the OSS-Fuzz helper script. ```bash # Clone OSS-Fuzz git clone https://github.com/google/oss-fuzz cd oss-fuzz # Build the image python infra/helper.py build_image gosaml2 # Build the fuzzers python infra/helper.py build_fuzzers gosaml2 # Run the fuzzers python infra/helper.py run_fuzzer gosaml2 fuzz_decode_response ``` -------------------------------- ### Run gosaml2 Fuzzers Locally Source: https://github.com/russellhaering/gosaml2/blob/main/internal/fuzz/README.md Execute local fuzzing tests for gosaml2 using Go's testing framework. Specify the fuzz target and duration for the test run. ```bash go test -fuzz=FuzzDecodeResponse ./internal/fuzz/ -fuzztime=30s ``` ```bash go test -fuzz=FuzzLogoutResponse ./internal/fuzz/ -fuzztime=30s ``` ```bash go test -fuzz=FuzzBuildRequest ./internal/fuzz/ -fuzztime=30s ``` -------------------------------- ### Configure Requested Authentication Context Source: https://context7.com/russellhaering/gosaml2/llms.txt Specify required authentication methods for the Identity Provider. Use the Comparison field to define matching policy (exact, minimum, maximum, better) and Contexts to list desired authentication classes. ```go sp := &saml2.SAMLServiceProvider{ // ... other config ... // Require password-protected transport (HTTPS with password auth) RequestedAuthnContext: &saml2.RequestedAuthnContext{ Comparison: saml2.AuthnPolicyMatchExact, Contexts: []string{ saml2.AuthnContextPasswordProtectedTransport, }, }, } ``` ```go const ( AuthnPolicyMatchExact = "exact" // Must match exactly AuthnPolicyMatchMinimum = "minimum" // At least as strong AuthnPolicyMatchMaximum = "maximum" // No stronger than AuthnPolicyMatchBetter = "better" // Stronger than specified ) ``` ```go // Context class reference: const ( AuthnContextPasswordProtectedTransport = "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport" ) ``` -------------------------------- ### Initiate SAML Single Logout (HTTP-Redirect) Source: https://context7.com/russellhaering/gosaml2/llms.txt Builds and sends a signed SAML LogoutRequest using the HTTP-Redirect binding to initiate single logout with the Identity Provider. Requires user session information. ```go func handleLogout(w http.ResponseWriter, r *http.Request) { // Get user's session info (stored during login) session := getSession(r) nameID := session.NameID sessionIndex := session.SessionIndex // Build signed logout request document logoutDoc, err := sp.BuildLogoutRequestDocument(nameID, sessionIndex) if err != nil { http.Error(w, "Failed to build logout request", http.StatusInternalServerError) return } // Option 1: Use HTTP-Redirect binding relayState := "/" redirectURL, err := sp.BuildLogoutURLRedirect(relayState, logoutDoc) if err != nil { http.Error(w, "Failed to build logout URL", http.StatusInternalServerError) return } http.Redirect(w, r, redirectURL, http.StatusFound) } ``` -------------------------------- ### Access SAML Assertion Attributes in Go Source: https://context7.com/russellhaering/gosaml2/llms.txt Demonstrates how to retrieve single and multi-valued attributes from a SAML assertion. The Values helper methods are safe to call on nil objects. ```go func processUserAttributes(assertionInfo *saml2.AssertionInfo) { values := assertionInfo.Values // Get single value (returns first value or empty string) email := values.Get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress") name := values.Get("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name") // Get all values for multi-valued attributes groups := values.GetAll("http://schemas.microsoft.com/ws/2008/06/identity/claims/groups") roles := values.GetAll("http://schemas.microsoft.com/ws/2008/06/identity/claims/role") // Check number of values groupCount := values.GetSize("http://schemas.microsoft.com/ws/2008/06/identity/claims/groups") fmt.Printf("User: %s <%s>\n", name, email) fmt.Printf("Groups (%d): %v\n", groupCount, groups) fmt.Printf("Roles: %v\n", roles) // Safe to call on nil - returns empty string/slice/0 var nilValues saml2.Values safeEmail := nilValues.Get("email") // Returns "" safeGroups := nilValues.GetAll("groups") // Returns []string{} safeCount := nilValues.GetSize("groups") // Returns 0 _, _, _ = safeEmail, safeGroups, safeCount } ``` -------------------------------- ### Generate SAML POST Binding Form Source: https://context7.com/russellhaering/gosaml2/llms.txt Use BuildAuthBodyPost to create an HTML form that automatically submits a SAML AuthnRequest to the IdP. This is typically used for initiating the SAML SSO flow. ```go func handlePostLogin(w http.ResponseWriter, r *http.Request) { relayState := "/dashboard" postBody, err := sp.BuildAuthBodyPost(relayState) if err != nil { http.Error(w, "Failed to build POST body", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/html") w.Write(postBody) } ``` ```html
``` -------------------------------- ### BuildAuthBodyPost Source: https://context7.com/russellhaering/gosaml2/llms.txt Generates an HTML form for HTTP-POST binding that automatically submits the SAML AuthnRequest to the Identity Provider. ```APIDOC ## BuildAuthBodyPost ### Description Generates an HTML form for HTTP-POST binding that automatically submits the SAML AuthnRequest to the IdP. ### Parameters #### Request Body - **relayState** (string) - Optional - The state to be returned after authentication. ### Response #### Success Response (200) - **HTML** (string) - An HTML form containing the SAMLRequest and RelayState with an auto-submit script. ``` -------------------------------- ### Initiate SAML Single Logout (HTTP-POST) Source: https://context7.com/russellhaering/gosaml2/llms.txt Builds a SAML LogoutRequest without an embedded signature for use with the HTTP-POST binding. This is part of initiating single logout with the Identity Provider. ```go func handleLogoutPOST(w http.ResponseWriter, r *http.Request) { session := getSession(r) // Build logout request without embedded signature (for HTTP-Redirect) logoutDoc, err := sp.BuildLogoutRequestDocumentNoSig(session.NameID, session.SessionIndex) if err != nil { http.Error(w, "Failed to build logout request", http.StatusInternalServerError) return } // Option 2: Use HTTP-POST binding postBody, err := sp.BuildLogoutBodyPostFromDocument("/logged-out", logoutDoc) if err != nil { http.Error(w, "Failed to build POST body", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/html") w.Write(postBody) } ``` -------------------------------- ### Configure SAML NameID Format Source: https://context7.com/russellhaering/gosaml2/llms.txt Set the NameID format for SAML requests. Use constants like NameIdFormatEmailAddress, NameIdFormatPersistent, or NameIdFormatTransient based on Identity Provider requirements. ```go sp := &saml2.SAMLServiceProvider{ // ... other config ... // Common NameID formats: NameIdFormat: saml2.NameIdFormatEmailAddress, // Other options: // saml2.NameIdFormatPersistent - Persistent opaque identifier // saml2.NameIdFormatTransient - Temporary identifier // saml2.NameIdFormatUnspecified - IdP decides format // saml2.NameIdFormatX509SubjectName - X.509 subject DN } ``` ```go const ( NameIdFormatPersistent = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" NameIdFormatTransient = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" NameIdFormatEmailAddress = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" NameIdFormatUnspecified = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" NameIdFormatX509SubjectName = "urn:oasis:names:tc:SAML:1.1:nameid-format:x509SubjectName" ) ``` -------------------------------- ### POST /api/saml/login - BuildAuthURL Source: https://context7.com/russellhaering/gosaml2/llms.txt Generates a URL that redirects users to the Identity Provider for authentication. The optional relayState parameter can be used to pass state information through the authentication flow. ```APIDOC ## POST /api/saml/login - BuildAuthURL ### Description The `BuildAuthURL` method generates a URL that redirects users to the Identity Provider for authentication. The optional relayState parameter can be used to pass state information through the authentication flow. ### Method POST ### Endpoint /api/saml/login ### Parameters #### Query Parameters - **next** (string) - Optional - Used to pass state information, such as the original destination URL. ### Request Example ```go func handleLogin(w http.ResponseWriter, r *http.Request) { // Generate redirect URL with optional RelayState for tracking original destination relayState := r.URL.Query().Get("next") // e.g., "/dashboard" authURL, err := sp.BuildAuthURL(relayState) if err != nil { http.Error(w, "Failed to build auth URL", http.StatusInternalServerError) return } // Redirect user to IdP for authentication http.Redirect(w, r, authURL, http.StatusFound) } ``` ### Response Example ``` Redirects to URL like: https://idp.example.com/sso?SAMLRequest=&RelayState=/dashboard ``` ``` -------------------------------- ### Handle SSO Redirect with AuthRedirect Source: https://context7.com/russellhaering/gosaml2/llms.txt A convenience method that combines building the SSO URL and performing the HTTP redirect. It handles the response writing directly. ```go func handleSSOLogin(w http.ResponseWriter, r *http.Request) { relayState := r.URL.Query().Get("returnTo") err := sp.AuthRedirect(w, r, relayState) if err != nil { http.Error(w, "Authentication redirect failed", http.StatusInternalServerError) return } // Response already written by AuthRedirect } ``` -------------------------------- ### Generate Service Provider Metadata in Go Source: https://context7.com/russellhaering/gosaml2/llms.txt Generates XML metadata for the Service Provider to share with Identity Providers. Includes variants for standard metadata and SLO-enabled metadata with custom validity periods. ```go func handleMetadata(w http.ResponseWriter, r *http.Request) { metadata, err := sp.Metadata() if err != nil { http.Error(w, "Failed to generate metadata", http.StatusInternalServerError) return } // Convert to XML doc := etree.NewDocument() doc.SetRoot(metadataToElement(metadata)) xmlBytes, err := doc.WriteToBytes() if err != nil { http.Error(w, "Failed to serialize metadata", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/samlmetadata+xml") w.Write(xmlBytes) } ``` ```go // For SLO-enabled metadata with custom validity func handleMetadataWithSLO(w http.ResponseWriter, r *http.Request) { // Validity in hours (7 days = 168 hours) validityHours := int64(168 * time.Hour) metadata, err := sp.MetadataWithSLO(validityHours) if err != nil { http.Error(w, "Failed to generate metadata", http.StatusInternalServerError) return } // Metadata includes: // - EntityID (ServiceProviderIssuer) // - AssertionConsumerService endpoint // - SingleLogoutService endpoint // - Signing and encryption certificates // - Supported encryption methods serializeAndRespond(w, metadata) } ``` -------------------------------- ### Handle SAML Logout Request Source: https://context7.com/russellhaering/gosaml2/llms.txt Builds and redirects the user to the Identity Provider's single logout endpoint. In a production environment, the NameID and sessionIndex should be retrieved from the user's active session. ```go func handleLogout(w http.ResponseWriter, r *http.Request) { // In production, get these from the user's session nameID := "user@example.com" sessionIndex := "session123" doc, _ := sp.BuildLogoutRequestDocument(nameID, sessionIndex) redirectURL, _ := sp.BuildLogoutURLRedirect("/", doc) http.Redirect(w, r, redirectURL, http.StatusFound) } ``` -------------------------------- ### Expose Service Provider Metadata Source: https://context7.com/russellhaering/gosaml2/llms.txt Provides an endpoint to expose the Service Provider's metadata in XML format. This metadata is required by the Identity Provider to establish trust and configure the SAML integration. ```go func handleMetadata(w http.ResponseWriter, r *http.Request) { metadata, _ := sp.Metadata() w.Header().Set("Content-Type", "application/xml") fmt.Fprintf(w, "", metadata.EntityID) } ``` -------------------------------- ### SAMLServiceProvider Configuration Source: https://context7.com/russellhaering/gosaml2/llms.txt Configuration of the SAMLServiceProvider struct, which holds all settings for interacting with an Identity Provider. ```APIDOC ## SAMLServiceProvider Configuration The `SAMLServiceProvider` struct is the main entry point for configuring SAML 2.0 Service Provider functionality. It holds all configuration for interacting with an Identity Provider including URLs, certificates, and signing options. ### Request Example ```go package main import ( "crypto/x509" "encoding/base64" "encoding/xml" "io/ioutil" "net/http" saml2 "github.com/russellhaering/gosaml2" "github.com/russellhaering/gosaml2/types" dsig "github.com/russellhaering/goxmldsig" ) func main() { // Fetch IdP metadata res, err := http.Get("https://idp.example.com/metadata") if err != nil { panic(err) } rawMetadata, _ := ioutil.ReadAll(res.Body) metadata := &types.EntityDescriptor{} xml.Unmarshal(rawMetadata, metadata) // Build certificate store from IdP metadata certStore := dsig.MemoryX509CertificateStore{ Roots: []*x509.Certificate{}, } for _, kd := range metadata.IDPSSODescriptor.KeyDescriptors { for _, xcert := range kd.KeyInfo.X509Data.X509Certificates { certData, _ := base64.StdEncoding.DecodeString(xcert.Data) idpCert, _ := x509.ParseCertificate(certData) certStore.Roots = append(certStore.Roots, idpCert) } } // Create Service Provider configuration sp := &saml2.SAMLServiceProvider{ IdentityProviderSSOURL: metadata.IDPSSODescriptor.SingleSignOnServices[0].Location, IdentityProviderSLOURL: metadata.IDPSSODescriptor.SingleLogoutServices[0].Location, IdentityProviderIssuer: metadata.EntityID, ServiceProviderIssuer: "https://myapp.example.com/saml/metadata", AssertionConsumerServiceURL: "https://myapp.example.com/saml/acs", ServiceProviderSLOURL: "https://myapp.example.com/saml/slo", AudienceURI: "https://myapp.example.com/saml/metadata", IDPCertificateStore: &certStore, SPKeyStore: dsig.RandomKeyStoreForTest(), // Use proper key in production SignAuthnRequests: true, NameIdFormat: saml2.NameIdFormatEmailAddress, ForceAuthn: false, IsPassive: false, } // sp is now ready for use _ = sp } ``` ``` -------------------------------- ### POST /api/saml/sso - AuthRedirect Source: https://context7.com/russellhaering/gosaml2/llms.txt Provides a convenient wrapper that handles the HTTP redirect directly, combining URL building and redirect in one call. ```APIDOC ## POST /api/saml/sso - AuthRedirect ### Description The `AuthRedirect` method provides a convenient wrapper that handles the HTTP redirect directly, combining URL building and redirect in one call. ### Method POST ### Endpoint /api/saml/sso ### Parameters #### Query Parameters - **returnTo** (string) - Optional - The URL to redirect to after successful authentication. ### Request Example ```go func handleSSOLogin(w http.ResponseWriter, r *http.Request) { relayState := r.URL.Query().Get("returnTo") err := sp.AuthRedirect(w, r, relayState) if err != nil { http.Error(w, "Authentication redirect failed", http.StatusInternalServerError) return } // Response already written by AuthRedirect } ``` ``` -------------------------------- ### Generate SSO Redirect URL with RelayState Source: https://context7.com/russellhaering/gosaml2/llms.txt Builds the SSO authentication URL, optionally including a RelayState parameter to maintain application state across the redirect. This is used to redirect the user to the Identity Provider. ```go func handleLogin(w http.ResponseWriter, r *http.Request) { // Generate redirect URL with optional RelayState for tracking original destination relayState := r.URL.Query().Get("next") // e.g., "/dashboard" authURL, err := sp.BuildAuthURL(relayState) if err != nil { http.Error(w, "Failed to build auth URL", http.StatusInternalServerError) return } // Redirect user to IdP for authentication http.Redirect(w, r, authURL, http.StatusFound) } ``` -------------------------------- ### Process SAML Response and Extract User Info Source: https://context7.com/russellhaering/gosaml2/llms.txt RetrieveAssertionInfo decodes, validates, and extracts user information from an encoded SAML response. It handles the callback from the IdP and checks for common validation warnings. ```go func handleACSCallback(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { http.Error(w, "Failed to parse form", http.StatusBadRequest) return } encodedResponse := r.FormValue("SAMLResponse") relayState := r.FormValue("RelayState") assertionInfo, err := sp.RetrieveAssertionInfo(encodedResponse) if err != nil { http.Error(w, "SAML validation failed: "+err.Error(), http.StatusForbidden) return } // Check for warnings if assertionInfo.WarningInfo.InvalidTime { http.Error(w, "Assertion timing is invalid", http.StatusForbidden) return } if assertionInfo.WarningInfo.NotInAudience { http.Error(w, "Service provider not in audience", http.StatusForbidden) return } // Extract user information userID := assertionInfo.NameID email := assertionInfo.Values.Get("email") firstName := assertionInfo.Values.Get("firstName") lastName := assertionInfo.Values.Get("lastName") groups := assertionInfo.Values.GetAll("groups") // Multi-valued attribute // Session information for SLO sessionIndex := assertionInfo.SessionIndex // Create session and redirect createUserSession(w, userID, email, sessionIndex) http.Redirect(w, r, relayState, http.StatusFound) } ``` -------------------------------- ### Handle SAML Single Logout (SLO) Response Source: https://context7.com/russellhaering/gosaml2/llms.txt Processes the SAML logout response from the Identity Provider. If the response is valid, it redirects the user to the root path. Invalid responses will result in a 403 Forbidden error. ```go func handleSLO(w http.ResponseWriter, r *http.Request) { r.ParseForm() if r.FormValue("SAMLResponse") != "" { // Logout response from IdP _, err := sp.ValidateEncodedLogoutResponsePOST(r.FormValue("SAMLResponse")) if err != nil { http.Error(w, "Logout failed", http.StatusForbidden) return } http.Redirect(w, r, "/", http.StatusFound) } } ``` -------------------------------- ### Handle SAML Assertion Consumer Service (ACS) Source: https://context7.com/russellhaering/gosaml2/llms.txt Processes the SAMLResponse POSTed by the Identity Provider after successful authentication. It retrieves and validates the assertion, then extracts user information like NameID and email to create a user session. Invalid assertions will result in a 403 Forbidden error. ```go func handleACS(w http.ResponseWriter, r *http.Request) { r.ParseForm() assertionInfo, err := sp.RetrieveAssertionInfo(r.FormValue("SAMLResponse")) if err != nil { http.Error(w, "Authentication failed", http.StatusForbidden) return } if assertionInfo.WarningInfo.InvalidTime || assertionInfo.WarningInfo.NotInAudience { http.Error(w, "Invalid assertion", http.StatusForbidden) return } // Create session with user info fmt.Fprintf(w, "Welcome %s!\nEmail: %s\n", assertionInfo.NameID, assertionInfo.Values.Get("email")) } ``` -------------------------------- ### RetrieveAssertionInfo Source: https://context7.com/russellhaering/gosaml2/llms.txt Decodes, validates, and extracts user information from an encoded SAML response. ```APIDOC ## RetrieveAssertionInfo ### Description Processes the callback from the IdP by decoding and validating the SAML response, then extracting user attributes and session information. ### Parameters #### Request Body - **encodedResponse** (string) - Required - The base64 encoded SAMLResponse string. ### Response #### Success Response (200) - **NameID** (string) - User identifier. - **Values** (map) - Map of SAML attributes. - **SessionIndex** (string) - Session identifier for logout. - **WarningInfo** (object) - Validation warnings including InvalidTime and NotInAudience. ``` -------------------------------- ### Low-Level SAML Response Validation Source: https://context7.com/russellhaering/gosaml2/llms.txt ValidateEncodedResponse offers detailed access to the validated SAML response object, including signature verification status and individual assertion details. Useful for complex scenarios or when raw response data is needed. ```go func handleResponse(encodedResponse string) (*types.Response, error) { response, err := sp.ValidateEncodedResponse(encodedResponse) if err != nil { return nil, fmt.Errorf("validation failed: %w", err) } // Check if response signature was validated if response.SignatureValidated { fmt.Println("Response signature verified") } // Access response details fmt.Printf("Response ID: %s\n", response.ID) fmt.Printf("InResponseTo: %s\n", response.InResponseTo) fmt.Printf("Issuer: %s\n", response.Issuer.Value) fmt.Printf("Status: %s\n", response.Status.StatusCode.Value) // Process multiple assertions if present for i, assertion := range response.Assertions { fmt.Printf("Assertion %d: %s\n", i, assertion.ID) fmt.Printf(" Issuer: %s\n", assertion.Issuer.Value) fmt.Printf(" Subject NameID: %s\n", assertion.Subject.NameID.Value) } return response, nil } ``` -------------------------------- ### ValidateEncodedResponse Source: https://context7.com/russellhaering/gosaml2/llms.txt Provides low-level access to the validated SAML response object. ```APIDOC ## ValidateEncodedResponse ### Description Validates the SAML response and returns the raw response structure, useful for accessing signature validation status and multiple assertions. ### Parameters #### Request Body - **encodedResponse** (string) - Required - The base64 encoded SAMLResponse string. ### Response #### Success Response (200) - **SignatureValidated** (boolean) - Whether the response signature was verified. - **ID** (string) - Response ID. - **Issuer** (object) - Issuer details. - **Assertions** (array) - List of assertions contained in the response. ``` -------------------------------- ### Validate IdP-Initiated Logout Request in Go Source: https://context7.com/russellhaering/gosaml2/llms.txt Validates a SAML logout request from an IdP and generates a corresponding response. Requires parsing the SAMLRequest form value from the incoming POST request. ```go func handleIdPLogoutRequest(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { http.Error(w, "Failed to parse form", http.StatusBadRequest) return } encodedRequest := r.FormValue("SAMLRequest") relayState := r.FormValue("RelayState") logoutRequest, err := sp.ValidateEncodedLogoutRequestPOST(encodedRequest) if err != nil { http.Error(w, "Logout request validation failed: "+err.Error(), http.StatusForbidden) return } // Get the user being logged out nameID := logoutRequest.NameID.Value fmt.Printf("IdP requested logout for user: %s\n", nameID) // Clear user's local session clearUserSession(nameID) // Send LogoutResponse back to IdP responseDoc, err := sp.BuildLogoutResponseDocument(saml2.StatusCodeSuccess, logoutRequest.ID) if err != nil { http.Error(w, "Failed to build response", http.StatusInternalServerError) return } postBody, err := sp.BuildLogoutResponseBodyPostFromDocument(relayState, responseDoc) if err != nil { http.Error(w, "Failed to build response body", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/html") w.Write(postBody) } ``` -------------------------------- ### Decode SAML Response Without Validation Source: https://context7.com/russellhaering/gosaml2/llms.txt Use DecodeUnverifiedBaseResponse to extract basic SAML response attributes before full validation. This is useful for routing responses in multi-tenant scenarios. ```go func handleMultiTenantCallback(w http.ResponseWriter, r *http.Request) { encodedResponse := r.FormValue("SAMLResponse") // Inspect response before validation to determine which SP config to use baseResponse, err := saml2.DecodeUnverifiedBaseResponse(encodedResponse) if err != nil { http.Error(w, "Failed to decode response", http.StatusBadRequest) return } // Route based on issuer (WARNING: This value is unverified!) idpIssuer := baseResponse.Issuer.Value destination := baseResponse.Destination inResponseTo := baseResponse.InResponseTo fmt.Printf("IdP Issuer: %s\n", idpIssuer) fmt.Printf("Destination: %s\n", destination) fmt.Printf("InResponseTo: %s\n", inResponseTo) // Look up the appropriate SP configuration spConfig := lookupSPConfig(idpIssuer) if spConfig == nil { http.Error(w, "Unknown identity provider", http.StatusBadRequest) return } // Now validate with the correct configuration assertionInfo, err := spConfig.RetrieveAssertionInfo(encodedResponse) if err != nil { http.Error(w, "Validation failed", http.StatusForbidden) return } processLogin(w, r, assertionInfo) } ``` -------------------------------- ### Validate SAML Logout Response (HTTP-POST) Source: https://context7.com/russellhaering/gosaml2/llms.txt Validates a SAML logout response received via HTTP-POST binding from the Identity Provider. It checks the status and clears the local session upon successful logout. ```go func handleLogoutResponse(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() if err != nil { http.Error(w, "Failed to parse form", http.StatusBadRequest) return } encodedResponse := r.FormValue("SAMLResponse") relayState := r.FormValue("RelayState") logoutResponse, err := sp.ValidateEncodedLogoutResponsePOST(encodedResponse) if err != nil { http.Error(w, "Logout response validation failed: "+err.Error(), http.StatusForbidden) return } // Check logout status if logoutResponse.Status.StatusCode.Value == saml2.StatusCodeSuccess { fmt.Println("Logout successful") // Clear local session clearSession(w, r) } else if logoutResponse.Status.StatusCode.Value == saml2.StatusCodePartialLogout { fmt.Println("Partial logout - some sessions may remain") } // Redirect to logged-out page http.Redirect(w, r, relayState, http.StatusFound) } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.