### 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.