Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions core/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package core

import "context"

// contextKey is an unexported type for context keys to prevent collisions.
// Using an unexported type ensures that only this package can create context keys,
// eliminating the risk of collisions with other packages.
type contextKey int

const (
claimsKey contextKey = iota
)

// GetClaims retrieves claims from the context with type safety using generics.
//
// This is a type-safe alternative to manually type-asserting the claims from the context.
// It returns an error if the claims are not found or if the type assertion fails.
//
// Example usage:
//
// claims, err := core.GetClaims[*validator.ValidatedClaims](ctx)
// if err != nil {
// return err
// }
// // Use claims...
func GetClaims[T any](ctx context.Context) (T, error) {
var zero T

val := ctx.Value(claimsKey)
if val == nil {
return zero, ErrClaimsNotFound
}

claims, ok := val.(T)
if !ok {
return zero, NewValidationError(
ErrorCodeClaimsNotFound,
"claims type assertion failed",
nil,
)
}

return claims, nil
}

// SetClaims stores claims in the context.
// This is a helper function for adapters to set claims after validation.
func SetClaims(ctx context.Context, claims any) context.Context {
return context.WithValue(ctx, claimsKey, claims)
}

// HasClaims checks if claims exist in the context without retrieving them.
func HasClaims(ctx context.Context) bool {
return ctx.Value(claimsKey) != nil
}
81 changes: 81 additions & 0 deletions core/core.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Package core provides framework-agnostic JWT validation logic that can be used
// across different transport layers (HTTP, gRPC, etc.).
//
// The Core type encapsulates the validation logic and can be wrapped by transport-specific
// adapters to provide JWT middleware functionality for various frameworks.
package core

import (
"context"
"time"
)

// TokenValidator defines the interface for JWT validation.
// Implementations should validate the token and return the validated claims.
type TokenValidator interface {
ValidateToken(ctx context.Context, token string) (any, error)
}

// Logger defines an optional logging interface for the core middleware.
type Logger interface {
Debug(msg string, args ...any)
Info(msg string, args ...any)
Warn(msg string, args ...any)
Error(msg string, args ...any)
}

// Core is the framework-agnostic JWT validation engine.
// It contains the core logic for token validation without any dependency
// on specific transport protocols (HTTP, gRPC, etc.).
type Core struct {
validator TokenValidator
credentialsOptional bool
logger Logger
}

// CheckToken validates a JWT token string and returns the validated claims.
//
// This is the core validation logic that is framework-agnostic:
// - If token is empty and credentialsOptional is true, returns (nil, nil)
// - If token is empty and credentialsOptional is false, returns ErrJWTMissing
// - Otherwise, validates the token using the configured validator
//
// The returned claims (any) should be type-asserted by the caller
// to the expected claims type (typically *validator.ValidatedClaims).
func (c *Core) CheckToken(ctx context.Context, token string) (any, error) {
// Handle empty token case
if token == "" {
if c.credentialsOptional {
if c.logger != nil {
c.logger.Debug("No token provided, but credentials are optional")
}
return nil, nil
}

if c.logger != nil {
c.logger.Warn("No token provided and credentials are required")
}

return nil, ErrJWTMissing
}

// Validate token
start := time.Now()
claims, err := c.validator.ValidateToken(ctx, token)
duration := time.Since(start)

if err != nil {
if c.logger != nil {
c.logger.Error("Token validation failed", "error", err, "duration", duration)
}

return nil, err
}

// Success
if c.logger != nil {
c.logger.Debug("Token validated successfully", "duration", duration)
}

return claims, nil
}
Loading
Loading