DEV Community

Cover image for Implementing authorization in a Go API with AuthAction
AuthAction Developer for AuthAction

Posted on • Edited on

Implementing authorization in a Go API with AuthAction

AuthAction is a flexible auth platform for both frontend and M2M apps. It supports OAuth2, social logins, passkeys, and includes user, role, and org management. Scalable up to 1M MAUs for free, it's ideal for startups and enterprises alike.

In this blog, we'll explore how to authorise Go APIs using AuthAction.

Prerequisites

Before you begin, ensure you have:

  1. Go 1.21 or later: Download from golang.org
  2. AuthAction Account: You'll need your AuthAction tenant domain and API identifier

Configuration

1. Install Required Packages

go get -u github.com/golang-jwt/jwt/v5
go get -u github.com/lestrrat-go/jwx/v2/jwk
go get -u github.com/gin-gonic/gin
Enter fullscreen mode Exit fullscreen mode

2. Configure AuthAction Settings

Create a .env file in your project root:

AUTHACTION_DOMAIN=your-authaction-tenant-domain
AUTHACTION_AUDIENCE=your-authaction-api-identifier
Enter fullscreen mode Exit fullscreen mode

3. Implement JWT Middleware

Create a middleware file middleware/auth.go:

type JWKSMiddleware struct {
    JWKSUri   string
    Issuer    string
    Audience  string
    Cache     *jwk.Cache
}

func NewJWKSMiddleware(jwksUri, issuer, audience string) (*JWKSMiddleware, error) {
    cache := jwk.NewCache(context.Background())
    return &JWKSMiddleware{
        JWKSUri:  jwksUri,
        Issuer:   issuer,
        Audience: audience,
        Cache:    cache,
    }, nil
}

func (m *JWKSMiddleware) ValidateToken() gin.HandlerFunc {
    return func(c *gin.Context) {
        // Get token from Authorization header
        tokenString := strings.Split(c.GetHeader("Authorization"), " ")[1]

        // Fetch JWKS from AuthAction with caching
        keySet, _ := m.Cache.Get(context.Background(), m.JWKSUri)

        // Parse and validate token
        token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
            kid := token.Header["kid"].(string)
            key, _ := keySet.LookupKeyID(kid)

            var rawkey interface{}
            key.Raw(&rawkey)
            return rawkey, nil
        })

        // Validate claims
        if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
            if aud, ok := claims["aud"].(string); ok && aud == m.Audience {
                c.Set("claims", claims)
                c.Next()
                return
            }
        }

        c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
        c.Abort()
    }
}
Enter fullscreen mode Exit fullscreen mode

Usage

1. Create Your API

Create your main API file main.go:

func main() {
    // Load environment variables
    domain := os.Getenv("AUTHACTION_DOMAIN")
    audience := os.Getenv("AUTHACTION_AUDIENCE")

    // Construct JWKS URI and issuer
    jwksUri := fmt.Sprintf("https://%s/.well-known/jwks.json", domain)
    issuer := fmt.Sprintf("https://%s/", domain)

    // Initialize middleware
    authMiddleware, _ := middleware.NewJWKSMiddleware(jwksUri, issuer, audience)

    r := gin.Default()

    // Public endpoint
    r.GET("/health", func(c *gin.Context) {
        c.JSON(200, gin.H{"status": "healthy"})
    })

    // Protected endpoint
    protected := r.Group("/api")
    protected.Use(authMiddleware.ValidateToken())
    {
        protected.GET("/protected", func(c *gin.Context) {
            claims, _ := c.Get("claims")
            c.JSON(200, gin.H{
                "message": "Protected endpoint",
                "claims": claims,
            })
        })
    }

    r.Run(":8080")
}
Enter fullscreen mode Exit fullscreen mode

2. Testing the API

  1. Obtain an Access Token:
curl --request POST \
--url https://your-authaction-tenant-domain/oauth2/m2m/token \
--header 'content-type: application/json' \
--data '{
  "client_id": "your-authaction-m2m-app-clientid",
  "client_secret": "your-authaction-m2m-app-client-secret",
  "audience": "your-authaction-api-identifier",
  "grant_type": "client_credentials"
}'
Enter fullscreen mode Exit fullscreen mode
  1. Call Protected Endpoints:
curl --request GET \
--url http://localhost:8080/api/protected \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
Enter fullscreen mode Exit fullscreen mode

Security Features

The implementation includes:

  • JWT token validation using AuthAction's JWKS endpoint
  • Automatic JWKS caching with periodic refresh
  • RS256 algorithm for token signing
  • Secure configuration management using environment variables
  • HTTPS support in production

Common Issues

Invalid Token Errors

  • Ensure your token is signed with RS256 algorithm
  • Verify the token contains correct issuer and audience claims
  • Check that environment variables are correctly set

Public Key Fetching Errors

  • Verify your application can reach AuthAction's JWKS endpoint
  • The JWKS URI should be: https://your-authaction-tenant-domain/.well-known/jwks.json

Unauthorized Access

If requests to protected endpoints fail, check:

  • The JWT token is included in the Authorization header
  • The token is valid and not expired
  • The token's audience matches your API identifier
  • The token's issuer matches your AuthAction domain

Conclusion

Integrating authorization into a Go API application using AuthAction is a straightforward process. This example helps streamline the setup, offering developers a robust foundation to build secure applications with minimal effort.

If you run into any issues, double-check your configurations to ensure everything is set up correctly. Happy coding!

Feel free to leave your thoughts and questions in the comments below!

Top comments (0)