Olympus Docs
IntegrateBackends

Echo (Go) integration

Authenticate Echo routes against Olympus

Echo is a high-performance Go web framework. Olympus integration via middleware.

Middleware

package main

import (
    "encoding/json"
    "net/http"
    "net/url"
    "os"
    "strings"

    "github.com/labstack/echo/v4"
)

type TokenInfo struct {
    Active bool   `json:"active"`
    Sub    string `json:"sub"`
    Scope  string `json:"scope"`
}

func OlympusAuth() echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            auth := c.Request().Header.Get("Authorization")
            if !strings.HasPrefix(auth, "Bearer ") {
                return c.JSON(http.StatusUnauthorized, map[string]string{"error": "missing_token"})
            }
            token := strings.TrimPrefix(auth, "Bearer ")

            form := url.Values{}
            form.Set("token", token)
            req, _ := http.NewRequestWithContext(
                c.Request().Context(),
                "POST",
                os.Getenv("OLYMPUS_ISSUER")+"/admin/oauth2/introspect",
                strings.NewReader(form.Encode()),
            )
            req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
            req.SetBasicAuth(os.Getenv("HYDRA_ADMIN_USER"), os.Getenv("HYDRA_ADMIN_PASS"))

            resp, err := http.DefaultClient.Do(req)
            if err != nil {
                return c.JSON(http.StatusInternalServerError, map[string]string{"error": "introspect_failed"})
            }
            defer resp.Body.Close()

            var info TokenInfo
            json.NewDecoder(resp.Body).Decode(&info)
            if !info.Active {
                return c.JSON(http.StatusUnauthorized, map[string]string{"error": "inactive"})
            }

            c.Set("user_sub", info.Sub)
            c.Set("scopes", strings.Fields(info.Scope))
            return next(c)
        }
    }
}

Wire up

e := echo.New()
api := e.Group("/api")
api.Use(OlympusAuth())

api.GET("/widgets", func(c echo.Context) error {
    return c.JSON(200, map[string]interface{}{
        "user": c.Get("user_sub"),
        "widgets": []string{},
    })
})

e.Logger.Fatal(e.Start(":8080"))

Scope checking

func RequireScope(scope string) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            scopes := c.Get("scopes").([]string)
            for _, s := range scopes {
                if s == scope {
                    return next(c)
                }
            }
            return c.JSON(http.StatusForbidden, map[string]string{"error": "insufficient_scope"})
        }
    }
}

api.POST("/widgets", RequireScope("write:widgets"), func(c echo.Context) error {
    // ...
})

On this page