AgeOnce Docs
Integration

Go

AgeOnce integration with Go (net/http, Gin, Echo)

Go Integration

Complete AgeOnce integration example with Go.

Install dependencies

go get github.com/golang-jwt/jwt/v5
go get github.com/gorilla/sessions

Configuration

// config.go
package main

import "os"

type Config struct {
    ClientID     string
    ClientSecret string
    RedirectURI  string
    ApiURL       string
}

func LoadConfig() *Config {
    apiURL := os.Getenv("AGEONCE_API_URL")
    if apiURL == "" {
        apiURL = "https://app.ageonce.com"
    }
    
    return &Config{
        ClientID:     os.Getenv("AGEONCE_CLIENT_ID"),
        ClientSecret: os.Getenv("AGEONCE_CLIENT_SECRET"),
        RedirectURI:  os.Getenv("AGEONCE_REDIRECT_URI"),
        ApiURL:       apiURL,
    }
}

AgeOnce client

// ageonce.go
package main

import (
    "bytes"
    "crypto/rand"
    "encoding/hex"
    "encoding/json"
    "fmt"
    "net/http"
    "net/url"
)

type AgeOnce struct {
    config *Config
}

type TokenResponse struct {
    AgeToken  string `json:"age_token"`
    TokenType string `json:"token_type"`
    ExpiresIn int    `json:"expires_in"`
}

type ValidateResponse struct {
    Valid   bool                   `json:"valid"`
    Payload map[string]interface{} `json:"payload"`
}

func NewAgeOnce(config *Config) *AgeOnce {
    return &AgeOnce{config: config}
}

func (a *AgeOnce) GenerateVerifyURL() (string, string, error) {
    stateBytes := make([]byte, 16)
    if _, err := rand.Read(stateBytes); err != nil {
        return "", "", err
    }
    state := hex.EncodeToString(stateBytes)
    
    params := url.Values{}
    params.Set("client_id", a.config.ClientID)
    params.Set("redirect_uri", a.config.RedirectURI)
    params.Set("state", state)
    
    verifyURL := fmt.Sprintf("%s/verify?%s", a.config.ApiURL, params.Encode())
    
    return verifyURL, state, nil
}

func (a *AgeOnce) ExchangeCodeForToken(code string) (*TokenResponse, error) {
    body := map[string]string{
        "client_id":     a.config.ClientID,
        "client_secret": a.config.ClientSecret,
        "code":          code,
        "redirect_uri":  a.config.RedirectURI,
    }
    
    jsonBody, _ := json.Marshal(body)
    
    resp, err := http.Post(
        a.config.ApiURL+"/api/oauth/token",
        "application/json",
        bytes.NewBuffer(jsonBody),
    )
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    var tokenResp TokenResponse
    if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
        return nil, err
    }
    
    return &tokenResp, nil
}

func (a *AgeOnce) ValidateToken(token string) (*ValidateResponse, error) {
    body := map[string]string{"token": token}
    jsonBody, _ := json.Marshal(body)
    
    resp, err := http.Post(
        a.config.ApiURL+"/api/oauth/validate",
        "application/json",
        bytes.NewBuffer(jsonBody),
    )
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    var validateResp ValidateResponse
    if err := json.NewDecoder(resp.Body).Decode(&validateResp); err != nil {
        return nil, err
    }
    
    return &validateResp, nil
}

Usage examples

// main.go
package main

import (
    "log"
    "net/http"
    
    "github.com/gorilla/sessions"
)

var store = sessions.NewCookieStore([]byte("your-secret-key"))
var ageonce *AgeOnce

func main() {
    config := LoadConfig()
    ageonce = NewAgeOnce(config)
    
    http.HandleFunc("/verify", verifyHandler)
    http.HandleFunc("/callback", callbackHandler)
    http.HandleFunc("/protected", protectedHandler)
    
    log.Println("Server starting on :3000")
    log.Fatal(http.ListenAndServe(":3000", nil))
}

func verifyHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := store.Get(r, "session")
    
    verifyURL, state, err := ageonce.GenerateVerifyURL()
    if err != nil {
        http.Error(w, "Failed to generate URL", http.StatusInternalServerError)
        return
    }
    
    session.Values["oauth_state"] = state
    session.Save(r, w)
    
    http.Redirect(w, r, verifyURL, http.StatusFound)
}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := store.Get(r, "session")
    
    code := r.URL.Query().Get("code")
    state := r.URL.Query().Get("state")
    
    // Verify state
    if state != session.Values["oauth_state"] {
        http.Error(w, "Invalid state", http.StatusBadRequest)
        return
    }
    
    tokenResp, err := ageonce.ExchangeCodeForToken(code)
    if err != nil {
        http.Error(w, "Token exchange failed", http.StatusInternalServerError)
        return
    }
    
    validateResp, err := ageonce.ValidateToken(tokenResp.AgeToken)
    if err != nil {
        http.Error(w, "Token validation failed", http.StatusInternalServerError)
        return
    }
    
    if validateResp.Valid {
        ageVerified, _ := validateResp.Payload["age_verified"].(bool)
        if ageVerified {
            session.Values["age_verified"] = true
            session.Save(r, w)
            http.Redirect(w, r, "/protected", http.StatusFound)
            return
        }
    }
    
    http.Error(w, "Age verification failed", http.StatusForbidden)
}

func protectedHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := store.Get(r, "session")
    
    if session.Values["age_verified"] != true {
        http.Redirect(w, r, "/verify", http.StatusFound)
        return
    }
    
    w.Write([]byte("Welcome to age-restricted content!"))
}
package main

import (
    "net/http"
    
    "github.com/gin-contrib/sessions"
    "github.com/gin-contrib/sessions/cookie"
    "github.com/gin-gonic/gin"
)

var ageonce *AgeOnce

func main() {
    config := LoadConfig()
    ageonce = NewAgeOnce(config)
    
    r := gin.Default()
    
    store := cookie.NewStore([]byte("secret"))
    r.Use(sessions.Sessions("session", store))
    
    r.GET("/verify", verifyHandler)
    r.GET("/callback", callbackHandler)
    r.GET("/protected", requireAgeVerification(), protectedHandler)
    
    r.Run(":3000")
}

func verifyHandler(c *gin.Context) {
    session := sessions.Default(c)
    
    verifyURL, state, _ := ageonce.GenerateVerifyURL()
    
    session.Set("oauth_state", state)
    session.Save()
    
    c.Redirect(http.StatusFound, verifyURL)
}

func callbackHandler(c *gin.Context) {
    session := sessions.Default(c)
    
    code := c.Query("code")
    state := c.Query("state")
    
    if state != session.Get("oauth_state") {
        c.String(http.StatusBadRequest, "Invalid state")
        return
    }
    
    tokenResp, _ := ageonce.ExchangeCodeForToken(code)
    validateResp, _ := ageonce.ValidateToken(tokenResp.AgeToken)
    
    if validateResp.Valid {
        if ageVerified, ok := validateResp.Payload["age_verified"].(bool); ok && ageVerified {
            session.Set("age_verified", true)
            session.Save()
            c.Redirect(http.StatusFound, "/protected")
            return
        }
    }
    
    c.String(http.StatusForbidden, "Age verification failed")
}

func requireAgeVerification() gin.HandlerFunc {
    return func(c *gin.Context) {
        session := sessions.Default(c)
        if session.Get("age_verified") != true {
            c.Redirect(http.StatusFound, "/verify")
            c.Abort()
            return
        }
        c.Next()
    }
}

func protectedHandler(c *gin.Context) {
    c.String(http.StatusOK, "Welcome to age-restricted content!")
}
package main

import (
    "net/http"
    
    "github.com/gorilla/sessions"
    "github.com/labstack/echo-contrib/session"
    "github.com/labstack/echo/v4"
)

var ageonce *AgeOnce

func main() {
    config := LoadConfig()
    ageonce = NewAgeOnce(config)
    
    e := echo.New()
    
    e.Use(session.Middleware(sessions.NewCookieStore([]byte("secret"))))
    
    e.GET("/verify", verifyHandler)
    e.GET("/callback", callbackHandler)
    
    protected := e.Group("/protected")
    protected.Use(requireAgeVerification)
    protected.GET("", protectedHandler)
    
    e.Start(":3000")
}

func verifyHandler(c echo.Context) error {
    sess, _ := session.Get("session", c)
    
    verifyURL, state, _ := ageonce.GenerateVerifyURL()
    
    sess.Values["oauth_state"] = state
    sess.Save(c.Request(), c.Response())
    
    return c.Redirect(http.StatusFound, verifyURL)
}

func callbackHandler(c echo.Context) error {
    sess, _ := session.Get("session", c)
    
    code := c.QueryParam("code")
    state := c.QueryParam("state")
    
    if state != sess.Values["oauth_state"] {
        return c.String(http.StatusBadRequest, "Invalid state")
    }
    
    tokenResp, _ := ageonce.ExchangeCodeForToken(code)
    validateResp, _ := ageonce.ValidateToken(tokenResp.AgeToken)
    
    if validateResp.Valid {
        if ageVerified, ok := validateResp.Payload["age_verified"].(bool); ok && ageVerified {
            sess.Values["age_verified"] = true
            sess.Save(c.Request(), c.Response())
            return c.Redirect(http.StatusFound, "/protected")
        }
    }
    
    return c.String(http.StatusForbidden, "Age verification failed")
}

func requireAgeVerification(next echo.HandlerFunc) echo.HandlerFunc {
    return func(c echo.Context) error {
        sess, _ := session.Get("session", c)
        if sess.Values["age_verified"] != true {
            return c.Redirect(http.StatusFound, "/verify")
        }
        return next(c)
    }
}

func protectedHandler(c echo.Context) error {
    return c.String(http.StatusOK, "Welcome to age-restricted content!")
}

Go provides excellent performance for high-load scenarios.

On this page