go-auth0 icon indicating copy to clipboard operation
go-auth0 copied to clipboard

Add support for `domain metadata` in `CustomDomain` struct, implement `ListWithPagination` method, and update Management options with `WithCustomDomainHeader`

Open developerkunal opened this issue 8 months ago • 1 comments

🔧 Changes

  • Added support for domain metadata to the CustomDomain struct.
  • Implemented the ListWithPagination method to handle paginated results in domain listing.
  • Updated Management options with the WithCustomDomainHeader to allow setting a custom domain for requests.
  • Refactored custom domain manager tests to incorporate domain metadata and pagination.
  • Added CustomDomainHeader option to request options to apply a custom domain header on a per-request basis.

🌟 Examples

Managing Custom Domain Metadata

You can associate custom metadata (key-value pairs) with your custom domains.

1. Creating a Custom Domain with Metadata

When creating a new custom domain, you can include a DomainMetadata field.

import (
    "context"
    "fmt"

    "github.com/auth0/go-auth0"
    "github.com/auth0/go-auth0/management"
)

func ExampleCreateCustomDomainWithMetadata() {
    // Initialize your Management API client (apiClient)
    // var apiClient *management.Management = ...

    customDomain := &management.CustomDomain{
        Domain:    auth0.String("shop.example-app.com"),
        Type:      auth0.String(management.CustomDomainTypeAuth0ManagedCerts), // Or management.CustomDomainTypeSelfManagedCerts
        TLSPolicy: auth0.String(management.CustomDomainTLSPolicyRecommended),
        DomainMetadata: map[string]interface{}{
            "region":      "eu-central-1",
            "environment": "production",
            "team_owner":  "checkout-squad",
        },
    }

    err := apiClient.CustomDomain.Create(context.Background(), customDomain)
    if err != nil {
        fmt.Printf("Error creating custom domain: %v\n", err)
        return
    }

    fmt.Printf("Custom domain %s created successfully with ID: %s\n", *customDomain.Domain, *customDomain.ID)
    fmt.Printf("Metadata: %v\n", customDomain.DomainMetadata)
}

2. Updating Custom Domain Metadata

You can update the metadata of an existing custom domain. To remove a metadata key, set its value to nil or an empty map if you want to clear all metadata.

import (
    "context"
    "fmt"

    "github.com/auth0/go-auth0"
    "github.com/auth0/go-auth0/management"
)

func ExampleUpdateCustomDomainMetadata() {
    // Initialize your Management API client (apiClient)
    // var apiClient *management.Management = ...
    // var customDomainID string = "cd_..." // ID of the custom domain to update

    // To add or modify metadata keys:
    updatedMetadata := &management.CustomDomain{
        DomainMetadata: map[string]interface{}{
            "region":      "us-east-1",       // Update existing key
            "sla_tier":    "premium",         // Add new key
            "team_owner":  "core-infra",      // Update existing key
            "environment": nil,               // To remove the 'environment' key
        },
    }

    err := apiClient.CustomDomain.Update(context.Background(), customDomainID, updatedMetadata)
    if err != nil {
        fmt.Printf("Error updating custom domain metadata: %v\n", err)
        return
    }
    fmt.Printf("Custom domain %s metadata updated.\n", customDomainID)
}

3. Listing Custom Domains with Pagination

The ListWithPagination method allows you to retrieve custom domains in pages, which is useful for tenants with a large number of custom domains. It uses checkpoint pagination.

import (
    "context"
    "fmt"

    "github.com/auth0/go-auth0"
    "github.com/auth0/go-auth0/management"
)

func ExampleListCustomDomainsWithPagination() {
    var allCustomDomains []*management.CustomDomain
    var nextCheckpoint string

    fmt.Println("Fetching custom domains...")

    for {
        requestOptions := []management.RequestOption{management.Take(5)}
        if nextCheckpoint != "" {
            requestOptions = append(requestOptions, management.From(nextCheckpoint))
        }

        pageResult, err := apiClient.CustomDomain.ListWithPagination(context.Background(), requestOptions...)
        if err != nil {
            fmt.Println("Error:", err)
            return
        }

        if len(pageResult.CustomDomains) == 0 {
            fmt.Println("No more pages.")
            break
        }

        allCustomDomains = append(allCustomDomains, pageResult.CustomDomains...)

        for _, cd := range pageResult.CustomDomains {
            fmt.Printf("Domain: %s, ID: %s\n", *cd.Domain, *cd.ID)
        }

        if pageResult.Next == "" {
            break
        }
        nextCheckpoint = pageResult.Next
    }

    fmt.Printf("Total custom domains: %d\n", len(allCustomDomains))
}

4. Using Custom Domain Headers

You can configure the SDK to send an Auth0-Custom-Domain header. This is useful in scenarios where Auth0 needs to know the original custom domain a request was intended for, especially for certain email templates or universal login flows.

  • WithCustomDomainHeader (Management Option)

This option sets a custom domain header globally for all requests made by a management client instance. The header is only applied if the request path is whitelisted (e.g., /api/v2/users, /api/v2/jobs/verification-email).

import (
    "context"
    "fmt"

    "github.com/auth0/go-auth0/management"
)

func ExampleWithCustomDomainHeader() {
    // Initialize Management client with the global custom domain header
    apiClient, err := management.New(
        "YOUR_AUTH0_TENANT_DOMAIN", // e.g., my-tenant.auth0.com
        management.WithClientCredentials(context.Background(), "YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET"),
        management.WithCustomDomainHeader("login.mycompany.com"), // Global custom domain
    )
    if err != nil {
        fmt.Printf("Error creating management client: %v\n", err)
        return
    }

    fmt.Println("Management client initialized with global custom domain header: login.mycompany.com")

    // Example: Create a new user
    user := &management.User{
        Email:    management.String("[email protected]"),
        Password: management.String("password123"),
        Connection: management.String("Username-Password-Authentication"),
    }

    createdUser, err := apiClient.User.Create(context.Background(), user)
    if err != nil {
        fmt.Printf("Error creating user: %v\n", err)
        return
    }

    fmt.Printf("Successfully created user with ID: %s\n", *createdUser.ID)

    // Example: Request to a whitelisted endpoint (e.g., creating a user)
    // The "Auth0-Custom-Domain: login.mycompany.com" header WILL be sent.
    fmt.Println("Header 'Auth0-Custom-Domain: login.mycompany.com' would be sent with the user creation request.")
}
  • CustomDomainHeader (Request Option)

This option sets or overrides the Auth0-Custom-Domain header for a specific API request. It takes precedence over any globally set header via WithCustomDomainHeader and applies regardless of whether the endpoint is whitelisted for the global option.

import (
    "context"
    "fmt"

    "github.com/auth0/go-auth0/management"
)

func ExampleCustomDomainHeaderRequestOption() {
    // Initialize Management client with a global custom domain header
    apiClient, err := management.New(
        "YOUR_AUTH0_TENANT_DOMAIN",
        management.WithClientCredentials(context.Background(), "YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET"),
        management.WithCustomDomainHeader("global.login.mycompany.com"), // Global setting
    )
    if err != nil {
        fmt.Printf("Error creating management client: %v\n", err)
        return
    }
    fmt.Println("Management client initialized with global custom domain: global.login.mycompany.com")

    // Create a user with a per-request custom domain header
    user := &management.User{
        Email:      management.String("[email protected]"),
        Password:   management.String("password123"),
        Connection: management.String("Username-Password-Authentication"),
    }

    createdUser, err := apiClient.User.Create(
        context.Background(),
        user,
        management.CustomDomainHeader("specific-user-request.mycompany.com"), // Per-request override
    )
    if err != nil {
        fmt.Printf("Error creating user with per-request header: %v\n", err)
    } else {
        fmt.Printf("Created user with ID: %s and per-request header 'Auth0-Custom-Domain: specific-user-request.mycompany.com'.\n", *createdUser.ID)
    }
}
import (
    "context"
    "fmt"

    "github.com/auth0/go-auth0/management"
)

func ExampleCreateUserWithCustomDomainHeader() {
    // Initialize your Management API client
    apiClient, err := management.New(
        "YOUR_AUTH0_TENANT_DOMAIN", // e.g., my-tenant.auth0.com
        management.WithClientCredentials(context.Background(), "YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET"),
    )
    if err != nil {
        fmt.Printf("Error creating management client: %v\n", err)
        return
    }

    // Create a new user with a per-request custom domain header
    user := &management.User{
        Email:      management.String("[email protected]"),
        Password:   management.String("password123"),
        Connection: management.String("Username-Password-Authentication"),
    }

    createdUser, err := apiClient.User.Create(
        context.Background(),
        user,
        management.CustomDomainHeader("specific-user-request.mycompany.com"), // Per-request custom domain header
    )
    if err != nil {
        fmt.Printf("Error creating user with per-request header: %v\n", err)
        return
    }

    fmt.Printf("Successfully created user with ID: %s and per-request custom domain header 'Auth0-Custom-Domain: specific-user-request.mycompany.com'.\n", *createdUser.ID)
}

📚 References

🔬 Testing

  • Test the updated custom domain manager functionality, ensuring that domain metadata is handled correctly.
  • Verify that pagination works with the new ListWithPagination method.
  • Confirm that custom domain headers are being applied properly with the new Management option WithCustomDomainHeader and the CustomDomainHeader in request options.

📝 Checklist

  • [x] All new/changed/fixed functionality is covered by tests (or N/A)
  • [x] I have added documentation for all new/changed functionality (or N/A)

developerkunal avatar May 08 '25 09:05 developerkunal

Codecov Report

All modified and coverable lines are covered by tests :white_check_mark:

Project coverage is 96.01%. Comparing base (dd66229) to head (5417e8a).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #551      +/-   ##
==========================================
+ Coverage   95.98%   96.01%   +0.03%     
==========================================
  Files          60       60              
  Lines       12131    12226      +95     
==========================================
+ Hits        11644    11739      +95     
  Misses        367      367              
  Partials      120      120              

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

:rocket: New features to boost your workflow:
  • :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

codecov-commenter avatar May 08 '25 09:05 codecov-commenter