Prerequisites
- Go 1.21 or higher
- An Unkey account (free at unkey.com)
1. Install the SDK
Copy
Ask AI
go get github.com/unkeyed/sdks/api/go/v2@latest
2. Set up your Unkey credentials
- Create an API in the Unkey Dashboard
- Create a root key at Settings → Root Keys
- Copy your API ID (looks like
api_xxxx)
Copy
Ask AI
export UNKEY_ROOT_KEY="unkey_xxxx"
3. Create middleware
Here’s how to verify API keys with standard librarynet/http:
Copy
Ask AI
package main
import (
"context"
"net/http"
"os"
"strings"
unkey "github.com/unkeyed/sdks/api/go/v2"
"github.com/unkeyed/sdks/api/go/v2/models/components"
)
var unkeyClient *unkey.Unkey
func init() {
unkeyClient = unkey.New(
unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")),
)
}
// AuthMiddleware verifies API keys on incoming requests
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Extract API key from header
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, `{"error": "Missing Authorization header"}`, http.StatusUnauthorized)
return
}
apiKey := strings.TrimPrefix(authHeader, "Bearer ")
// Verify with Unkey
res, err := unkeyClient.Keys.VerifyKey(r.Context(), components.V2KeysVerifyKeyRequestBody{
Key: apiKey,
})
if err != nil {
http.Error(w, `{"error": "Verification service unavailable"}`, http.StatusServiceUnavailable)
return
}
result := res.V2KeysVerifyKeyResponseBody.Data
if !result.Valid {
http.Error(w, `{"error": "Invalid API key"}`, http.StatusUnauthorized)
return
}
// Add key info to context for handlers
ctx := context.WithValue(r.Context(), "keyId", *result.KeyID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func main() {
mux := http.NewServeMux()
// Protected route
mux.HandleFunc("/api/protected", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"message": "Access granted!"}`))
})
// Wrap with auth middleware
http.ListenAndServe(":8080", AuthMiddleware(mux))
}
4. Run your server
Copy
Ask AI
go run main.go
Copy
Ask AI
curl -H "Authorization: Bearer YOUR_API_KEY" http://localhost:8080/api/protected
Using with Gin
If you’re using the Gin framework:Copy
Ask AI
package main
import (
"net/http"
"os"
"strings"
"github.com/gin-gonic/gin"
unkey "github.com/unkeyed/sdks/api/go/v2"
"github.com/unkeyed/sdks/api/go/v2/models/components"
)
var unkeyClient *unkey.Unkey
func init() {
unkeyClient = unkey.New(
unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")),
)
}
func UnkeyAuth() gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Missing API key"})
return
}
apiKey := strings.TrimPrefix(authHeader, "Bearer ")
res, err := unkeyClient.Keys.VerifyKey(c.Request.Context(), components.V2KeysVerifyKeyRequestBody{
Key: apiKey,
})
if err != nil {
c.AbortWithStatusJSON(http.StatusServiceUnavailable, gin.H{"error": "Verification failed"})
return
}
result := res.V2KeysVerifyKeyResponseBody.Data
if !result.Valid {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
"error": "Invalid API key",
"code": string(result.Code),
})
return
}
// Store verification result in context
c.Set("unkeyResult", &result)
c.Next()
}
}
func main() {
r := gin.Default()
// Protected routes
api := r.Group("/api", UnkeyAuth())
{
api.GET("/data", func(c *gin.Context) {
result := c.MustGet("unkeyResult").(*components.V2KeysVerifyKeyResponseData)
c.JSON(http.StatusOK, gin.H{
"message": "Access granted",
"key_id": *result.KeyID,
})
})
}
r.Run(":8080")
}
Using with Echo
For the Echo framework:Copy
Ask AI
package main
import (
"net/http"
"os"
"strings"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
unkey "github.com/unkeyed/sdks/api/go/v2"
"github.com/unkeyed/sdks/api/go/v2/models/components"
)
var unkeyClient *unkey.Unkey
func init() {
unkeyClient = unkey.New(
unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")),
)
}
func UnkeyAuthMiddleware() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
authHeader := c.Request().Header.Get("Authorization")
if authHeader == "" {
return c.JSON(http.StatusUnauthorized, map[string]string{"error": "Missing API key"})
}
apiKey := strings.TrimPrefix(authHeader, "Bearer ")
res, err := unkeyClient.Keys.VerifyKey(c.Request().Context(), components.V2KeysVerifyKeyRequestBody{
Key: apiKey,
})
if err != nil {
return c.JSON(http.StatusServiceUnavailable, map[string]string{"error": "Verification failed"})
}
result := res.V2KeysVerifyKeyResponseBody.Data
if !result.Valid {
return c.JSON(http.StatusUnauthorized, map[string]string{
"error": "Invalid API key",
"code": string(result.Code),
})
}
// Store in context
c.Set("keyId", *result.KeyID)
return next(c)
}
}
}
func main() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
// Protected route
e.GET("/api/protected", func(c echo.Context) error {
keyId, ok := c.Get("keyId").(string)
if !ok || keyId == "" {
return c.JSON(http.StatusInternalServerError, map[string]string{
"error": "Key ID not found in context",
})
}
return c.JSON(http.StatusOK, map[string]string{
"message": "Access granted",
"key_id": keyId,
})
}, UnkeyAuthMiddleware())
e.Start(":8080")
}

