Skip to main content

Overview

The Auth0 Go SDK provides two mechanisms for working with fields that aren’t included in the generated types:
  1. ExtraProperties - Available on select request/response types for capturing additional fields
  2. WithBodyProperties - Request option for adding custom fields to any request body
These features are useful when:
  • Working with custom metadata fields
  • Using advanced configuration options not yet in the SDK
  • Handling fields that may be added to the Auth0 API
  • Accessing beta or experimental API features

ExtraProperties Field

Some SDK types include an ExtraProperties field (map[string]interface{}) that works in both directions:
  • In Requests: Send additional fields to Auth0
  • In Responses: Capture additional fields returned by Auth0

Setting ExtraProperties in Requests

Add custom fields directly to request structures:
import (
    "context"
    "github.com/auth0/go-auth0/v2/management"
)

ctx := context.Background()

// Example: Creating a connection with extra properties
createRequest := &management.CreateConnectionRequestContent{
    Name:     "My Custom DB",
    Strategy: management.ConnectionIdentityProviderEnumAuth0,
    Options: &management.ConnectionPropertiesOptions{
        ImportMode: management.Bool(true),
        // Set extra properties directly on the struct
        ExtraProperties: map[string]interface{}{
            "custom_timeout":     30,
            "retry_policy":       "exponential_backoff",
            "webhook_endpoints": []string{
                "https://api.example.com/auth-webhook",
                "https://backup.example.com/auth-webhook",
            },
            "advanced_config": map[string]interface{}{
                "enable_debug_logs": true,
                "api_version":       "v2.1",
                "custom_headers": map[string]string{
                    "X-Custom-Source": "auth0-sdk",
                    "X-Environment":   "production",
                },
            },
        },
    },
}

newConn, err := mgmt.Connections.Create(ctx, createRequest)
if err != nil {
    return err
}

Nested ExtraProperties Example

Complex nested structures work seamlessly:
updateRequest := &management.UpdateConnectionRequestContent{
    Options: &management.ConnectionPropertiesOptions{
        ExtraProperties: map[string]interface{}{
            "maintenance_mode":    false,
            "last_updated_by":     "admin-user-123",
            "migration_settings": map[string]interface{}{
                "batch_size":     1000,
                "rate_limit_ms":  100,
                "error_handling": "continue",
                "transformations": []map[string]interface{}{
                    {
                        "field":     "email",
                        "operation": "lowercase",
                    },
                    {
                        "field":     "phone",
                        "operation": "normalize",
                        "format":    "E.164",
                    },
                },
            },
        },
    },
}

updatedConn, err := mgmt.Connections.Update(ctx, "con_123", updateRequest)

Reading ExtraProperties from Responses

Access additional fields returned by Auth0:
conn, err := mgmt.Connections.Get(ctx, "con_123")
if err != nil {
    return err
}

if conn.Options != nil && conn.Options.ExtraProperties != nil {
    // Access custom fields from the response
    if timeout, ok := conn.Options.ExtraProperties["custom_timeout"].(float64); ok {
        fmt.Printf("Custom timeout: %v seconds\n", timeout)
    }

    if config, ok := conn.Options.ExtraProperties["advanced_config"].(map[string]interface{}); ok {
        if debugLogs, ok := config["enable_debug_logs"].(bool); ok {
            fmt.Printf("Debug logs enabled: %v\n", debugLogs)
        }
    }
}

WithBodyProperties Request Option

The WithBodyProperties option allows adding custom fields to any request body, even for types without ExtraProperties:
import (
    "github.com/auth0/go-auth0/v2/management/option"
)

// Example: Creating a client with additional custom properties
customBodyProps := map[string]interface{}{
    "custom_domain_verified": true,
    "integration_metadata": map[string]interface{}{
        "source":      "terraform",
        "environment": "production",
        "team":        "platform",
    },
    "advanced_settings": map[string]interface{}{
        "custom_login_page":         true,
        "universal_login_branding":  true,
    },
}

createRequest := &management.CreateClientRequestContent{
    Name:    "My Custom App",
    AppType: &management.ClientAppTypeEnumSpa,
    Callbacks: []string{"https://myapp.com/callback"},
}

client, err := mgmt.Clients.Create(
    ctx,
    createRequest,
    option.WithBodyProperties(customBodyProps),
)

Setting Fields to Null

Use WithBodyProperties to explicitly set fields to null:
_, err := mgmt.Clients.Update(
    ctx,
    clientID,
    &management.UpdateClientRequestContent{
        Name: management.String("Updated App Name"),
    },
    option.WithBodyProperties(map[string]interface{}{
        "description":     nil, // This will set description to null
        "logo_uri":        "https://example.com/new-logo.png",
        "custom_metadata": nil, // This will set custom_metadata to null
    }),
)

ExtraProperties vs WithBodyProperties

ExtraProperties

When to use:
  • Type has ExtraProperties field
  • Need to read extra fields from responses
  • Want type-safe structure access
Pros:
  • More idiomatic Go code
  • Access to response extra fields
  • Clear structure in code

WithBodyProperties

When to use:
  • Type lacks ExtraProperties field
  • Need to set fields to null
  • Adding fields to any request
Pros:
  • Works with any request type
  • Can override standard fields
  • Flexible for one-off needs

Combining Both Approaches

You can use both mechanisms together:
createRequest := &management.CreateConnectionRequestContent{
    Name:     "My Connection",
    Strategy: management.ConnectionIdentityProviderEnumAuth0,
    Options: &management.ConnectionPropertiesOptions{
        // Standard ExtraProperties
        ExtraProperties: map[string]interface{}{
            "connection_level_field": "value",
        },
    },
}

// Add additional top-level fields
newConn, err := mgmt.Connections.Create(
    ctx,
    createRequest,
    option.WithBodyProperties(map[string]interface{}{
        "top_level_custom_field": "value",
        "experimental_feature":   true,
    }),
)

Type Assertions and Safety

When reading from ExtraProperties, always use type assertions safely:
if conn.Options != nil && conn.Options.ExtraProperties != nil {
    // Safe type assertion with check
    if value, ok := conn.Options.ExtraProperties["field_name"].(string); ok {
        fmt.Printf("Field value: %s\n", value)
    }

    // Handle different types
    switch v := conn.Options.ExtraProperties["dynamic_field"].(type) {
    case string:
        fmt.Printf("String value: %s\n", v)
    case float64:
        fmt.Printf("Numeric value: %f\n", v)
    case bool:
        fmt.Printf("Boolean value: %t\n", v)
    case map[string]interface{}:
        fmt.Printf("Object value: %+v\n", v)
    case []interface{}:
        fmt.Printf("Array value: %+v\n", v)
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}

JSON Number Handling

JSON numbers are decoded as float64 by default. For integer values, cast accordingly:
if timeout, ok := props["timeout"].(float64); ok {
    timeoutInt := int(timeout)
    fmt.Printf("Timeout: %d seconds\n", timeoutInt)
}

Real-World Examples

Custom Client Metadata

// Track deployment metadata in client applications
customProps := map[string]interface{}{
    "deployment": map[string]interface{}{
        "deployed_by":   "ci-pipeline",
        "deployed_at":   time.Now().Format(time.RFC3339),
        "version":       "1.2.3",
        "git_commit":    "a1b2c3d4",
        "environment":   "production",
    },
    "monitoring": map[string]interface{}{
        "datadog_enabled":    true,
        "sentry_enabled":     true,
        "log_level":          "info",
    },
}

client, err := mgmt.Clients.Create(
    ctx,
    createRequest,
    option.WithBodyProperties(customProps),
)

Connection Configuration

// Advanced connection settings
connRequest := &management.CreateConnectionRequestContent{
    Name:     "Enterprise SAML",
    Strategy: management.ConnectionIdentityProviderEnumSamlp,
    Options: &management.ConnectionPropertiesOptions{
        ExtraProperties: map[string]interface{}{
            "sign_in_endpoint":     "https://idp.example.com/sso",
            "sign_out_endpoint":    "https://idp.example.com/logout",
            "signing_cert":         certString,
            "protocol_binding":     "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
            "request_template":     customTemplate,
            "user_id_attribute":    "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
            "debug":                true,
            "signature_algorithm": "rsa-sha256",
            "digest_algorithm":     "sha256",
        },
    },
}

Best Practices

1

Document Custom Fields

Maintain documentation of any custom fields you use, including their purpose, type, and valid values.
2

Validate Before Sending

Validate custom field values before sending to avoid API errors:
if timeout < 0 || timeout > 300 {
    return errors.New("timeout must be between 0 and 300")
}
3

Handle Missing Fields

Always check if fields exist before accessing them:
if props := conn.Options.ExtraProperties; props != nil {
    if value, ok := props["field"]; ok {
        // Use value
    }
}
4

Use Typed Wrappers

For frequently used custom fields, create typed wrapper functions:
func GetCustomTimeout(conn *management.Connection) (int, bool) {
    if conn.Options == nil || conn.Options.ExtraProperties == nil {
        return 0, false
    }
    if timeout, ok := conn.Options.ExtraProperties["custom_timeout"].(float64); ok {
        return int(timeout), true
    }
    return 0, false
}

Build docs developers (and LLMs) love