After successful authentication, Hanko generates a session token stored as a cookie. This guide shows how to validate these session tokens in your Go backend to authenticate API requests.

Get the Hanko API URL

Retrieve your project’s API URL from the Hanko console. This URL is used to validate session tokens.
If you are self-hosting Hanko, use your self-hosted instance URL instead.

Authentication Flow

To authenticate requests in your Go backend:
  1. Extract the session token from the HTTP cookie
  2. Validate the token using Hanko’s session validation API
  3. Allow or deny the request based on validation result

Implementation Example

This example shows how to create a session validator that can be used as middleware in your Go application:
// SessionValidator defines the interface for session validation
type SessionValidator interface {
	ValidateSession(token string) (bool, error)
}

// HankoSessionValidator implements SessionValidator
type HankoSessionValidator struct {
	apiURL string
}

// ValidationResponse represents the Hanko API response
type ValidationResponse struct {
	IsValid bool `json:"is_valid"`
}

func NewHankoSessionValidator(apiURL string) *HankoSessionValidator {
	return &HankoSessionValidator{apiURL: apiURL}
}

func (v *HankoSessionValidator) ValidateSession(token string) (bool, error) {
	payload := strings.NewReader(fmt.Sprintf(`{"session_token":"%s"}`, token))

	req, err := http.NewRequest(http.MethodPost, v.apiURL+"/sessions/validate", payload)
	if err != nil {
		return false, &fmt.Errorf("Failed to create request: %w", err)
	}

	req.Header.Add("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return false, fmt.Errorf("Failed to send request: %w", err)
	}
	defer res.Body.Close()

	body, err := io.ReadAll(res.Body)
	if err != nil {
		return false, fmt.Errorf("Failed to read response: %w", err)
	}

	var validationRes ValidationResponse
	if err := json.Unmarshal(body, &validationRes); err != nil {
		return false, fmt.Errorf("Failed to parse response: %w", err)
	}

	return validationRes.IsValid, nil
}

Using the Validator as Middleware

Here’s how to use the session validator to protect your API endpoints:
func AuthMiddleware(validator SessionValidator) func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// Extract session token from cookie
			cookie, err := r.Cookie("hanko")
			if err != nil {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}

			// Validate the session token
			isValid, err := validator.ValidateSession(cookie.Value)
			if err != nil {
				http.Error(w, "Internal Server Error", http.StatusInternalServerError)
				return
			}

			if !isValid {
				http.Error(w, "Unauthorized", http.StatusUnauthorized)
				return
			}

			// Continue to the next handler
			next.ServeHTTP(w, r)
		})
	}
}

// Usage example
func main() {
	validator := NewHankoSessionValidator("https://your-hanko-api-url.hanko.io")
	
	mux := http.NewServeMux()
	mux.HandleFunc("/protected", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("This is a protected endpoint!"))
	})

	// Apply authentication middleware to protected routes
	http.ListenAndServe(":8080", AuthMiddleware(validator)(mux))
}