DEV Community

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

Posted on

Implementing authorization in a Go API with AuthAction

AuthAction is a powerful authentication and authorization platform that offers a range of features, including support for single-page applications (SPA) and machine-to-machine (M2M) applications. It provides an easy-to-use interface for managing users, roles, and organizations, and supports OAuth2, social logins and passkey authentication. Best of all, AuthAction is scalable, allowing up to 1 Million monthly active users for free. Whether you're developing an app for a startup or a large enterprise, AuthAction provides a flexible and secure solution for your authentication needs.

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)