This guide shows how to add API key verification to your Go applications using the official Unkey Go SDK.
Prerequisites#
- Go 1.21 or higher
- An Unkey account (free at unkey.com)
1. Install the SDK#
go get github.com/unkeyed/sdks/api/go/v2@latest2. Set up your Unkey credentials#
- Create a keyspace in the Unkey Dashboard
- Create a root key at Settings → Root Keys
- Copy your API ID (looks like
api_xxxx)
Set your root key as an environment variable:
export UNKEY_ROOT_KEY="unkey_xxxx"3. Create middleware#
Here's how to verify API keys with standard library net/http:
package mainimport ( "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.Unkeyfunc init() { unkeyClient = unkey.New( unkey.WithSecurity(os.Getenv("UNKEY_ROOT_KEY")), )}// AuthMiddleware verifies API keys on incoming requestsfunc 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#
go run main.goTest it with a valid API key:
curl -H "Authorization: Bearer YOUR_API_KEY" http://localhost:8080/api/protectedUsing with Gin#
If you're using the Gin framework:
package mainimport ( "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.Unkeyfunc 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:
package mainimport ( "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.Unkeyfunc 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")}