Sdks
Go SDK
Use the HelloJohn Go SDK to verify tokens and manage users, tenants, and sessions from your Go backend.
Go SDK
github.com/hellojohn/hellojohn-go is the official Go SDK for HelloJohn. It provides token verification, user management, and full access to the admin API.
Installation
go get github.com/hellojohn/hellojohn-goSetup
package main
import (
"github.com/hellojohn/hellojohn-go"
)
func main() {
client, err := hellojohn.NewClient(hellojohn.Config{
SecretKey: os.Getenv("HJ_SECRET_KEY"),
// Optional: for self-hosted
APIURL: os.Getenv("HJ_API_URL"),
})
if err != nil {
log.Fatal(err)
}
}Token verification
import (
"context"
"net/http"
"strings"
"github.com/hellojohn/hellojohn-go"
)
// HTTP middleware
func AuthMiddleware(client *hellojohn.Client) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
token := strings.TrimPrefix(authHeader, "Bearer ")
payload, err := client.VerifyToken(r.Context(), token)
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Store payload in context
ctx := hellojohn.WithPayload(r.Context(), payload)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// Access payload in a handler
func GetMe(w http.ResponseWriter, r *http.Request) {
payload := hellojohn.GetPayload(r.Context())
json.NewEncoder(w).Encode(map[string]any{
"user_id": payload.Sub,
"email": payload.Email,
"tenant_id": payload.TenantID,
"role": payload.Role,
})
}Token payload struct
type TokenPayload struct {
Sub string `json:"sub"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
TenantID string `json:"tenant_id"`
OrganizationID string `json:"organization_id"`
Role string `json:"role"`
CustomClaims map[string]interface{} `json:"custom_claims"`
IssuedAt int64 `json:"iat"`
ExpiresAt int64 `json:"exp"`
}User management
// List users
users, err := client.Users.List(ctx, &hellojohn.UserListParams{
TenantID: "tnt_01H...",
Limit: 50,
})
// Get a user
user, err := client.Users.Get(ctx, "usr_01H...")
// Create a user
newUser, err := client.Users.Create(ctx, &hellojohn.CreateUserParams{
Email: "alice@example.com",
Password: "strongpassword",
DisplayName: "Alice",
TenantID: "tnt_01H...",
Role: "member",
})
// Update a user
updated, err := client.Users.Update(ctx, "usr_01H...", &hellojohn.UpdateUserParams{
DisplayName: hellojohn.String("Alice Smith"),
Role: hellojohn.String("admin"),
})
// Disable / delete
err = client.Users.Disable(ctx, "usr_01H...")
err = client.Users.Delete(ctx, "usr_01H...")Session management
// List active sessions
sessions, err := client.Sessions.List(ctx, &hellojohn.SessionListParams{
UserID: "usr_01H...",
})
// Revoke a session
err = client.Sessions.Revoke(ctx, "sess_01H...")
// Revoke all sessions for a user
err = client.Sessions.RevokeAll(ctx, &hellojohn.RevokeAllSessionsParams{
UserID: "usr_01H...",
})Tenant management
// Create a tenant
tenant, err := client.Tenants.Create(ctx, &hellojohn.CreateTenantParams{
Name: "Acme Corp",
Slug: "acme-corp",
})
// Get a tenant
tenant, err := client.Tenants.Get(ctx, "tnt_01H...")
// Update a tenant
updated, err := client.Tenants.Update(ctx, "tnt_01H...", &hellojohn.UpdateTenantParams{
Name: hellojohn.String("Acme Corporation"),
})
// Delete
err = client.Tenants.Delete(ctx, "tnt_01H...")Webhook verification
import "github.com/hellojohn/hellojohn-go/webhook"
http.HandleFunc("/webhooks/hellojohn", func(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
err := webhook.Verify(
body,
r.Header.Get("X-HelloJohn-Signature"),
r.Header.Get("X-HelloJohn-Timestamp"),
os.Getenv("WEBHOOK_SECRET"),
)
if err != nil {
http.Error(w, "Invalid signature", http.StatusBadRequest)
return
}
var event hellojohn.WebhookEvent
json.Unmarshal(body, &event)
switch event.Type {
case "user.created":
// handle user creation
case "user.login":
// handle login
}
w.WriteHeader(http.StatusOK)
})Audit logs
logs, err := client.AuditLogs.List(ctx, &hellojohn.AuditLogListParams{
Type: "user.login_failed",
Result: "failure",
From: time.Now().Add(-24 * time.Hour),
Limit: 100,
})
for _, entry := range logs.Data {
fmt.Printf("%s — %s — %s\n",
entry.CreatedAt.Format(time.RFC3339),
entry.Type,
entry.Actor.Email,
)
}Error handling
import "github.com/hellojohn/hellojohn-go"
user, err := client.Users.Get(ctx, "usr_nonexistent")
if err != nil {
var hjErr *hellojohn.Error
if errors.As(err, &hjErr) {
switch hjErr.Code {
case "user_not_found":
http.Error(w, "User not found", http.StatusNotFound)
case "insufficient_permissions":
http.Error(w, "Forbidden", http.StatusForbidden)
default:
http.Error(w, "Internal error", http.StatusInternalServerError)
}
return
}
// Network or other error
log.Printf("unexpected error: %v", err)
}Chi router example
Full example with Chi router and HelloJohn middleware:
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
"github.com/hellojohn/hellojohn-go"
)
func main() {
hj, _ := hellojohn.NewClient(hellojohn.Config{
SecretKey: os.Getenv("HJ_SECRET_KEY"),
})
r := chi.NewRouter()
// Public routes
r.Get("/health", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{"status":"ok"}`))
})
// Protected routes
r.Group(func(r chi.Router) {
r.Use(AuthMiddleware(hj))
r.Get("/api/me", GetMe)
r.Get("/api/data", GetData)
})
// Admin routes (require "admin" role)
r.Group(func(r chi.Router) {
r.Use(AuthMiddleware(hj))
r.Use(RequireRole("admin"))
r.Get("/api/admin/users", ListUsers)
})
http.ListenAndServe(":8080", r)
}
func RequireRole(role string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
payload := hellojohn.GetPayload(r.Context())
if payload.Role != role {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
}