Skip to content
Open
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
15 changes: 9 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ vendor/

# Docs
docs/
# Example binaries
examples/echo-example/echo
examples/gin-example/gin
examples/http-example/http
examples/http-jwks-example/http-jwks
examples/iris-example/iris

# Example binaries - ignore executables (not .go, .mod, .sum, .md files)
examples/*/echo
examples/*/gin
examples/*/iris
examples/*/http
examples/*/http-jwks
examples/*/http-dpop
examples/*/http-dpop-*
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ var handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
})

func main() {
keyFunc := func(ctx context.Context) (interface{}, error) {
keyFunc := func(ctx context.Context) (any, error) {
// Our token must be signed using this secret
return []byte("secret"), nil
}
Expand Down
46 changes: 46 additions & 0 deletions core/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type contextKey int

const (
claimsKey contextKey = iota
dpopContextKey
)

// GetClaims retrieves claims from the context with type safety using generics.
Expand Down Expand Up @@ -53,3 +54,48 @@ func SetClaims(ctx context.Context, claims any) context.Context {
func HasClaims(ctx context.Context) bool {
return ctx.Value(claimsKey) != nil
}

// SetDPoPContext stores DPoP context in the context.
// This is a helper function for adapters to set DPoP context after validation.
//
// DPoP context contains information about the validated DPoP proof, including
// the public key thumbprint, issued-at timestamp, and the raw proof JWT.
func SetDPoPContext(ctx context.Context, dpopCtx *DPoPContext) context.Context {
return context.WithValue(ctx, dpopContextKey, dpopCtx)
}

// GetDPoPContext retrieves DPoP context from the context.
// Returns nil if no DPoP context exists (e.g., for Bearer tokens).
//
// Example usage:
//
// dpopCtx := core.GetDPoPContext(ctx)
// if dpopCtx != nil {
// log.Printf("DPoP token from key: %s", dpopCtx.PublicKeyThumbprint)
// }
func GetDPoPContext(ctx context.Context) *DPoPContext {
val := ctx.Value(dpopContextKey)
if val == nil {
return nil
}

dpopCtx, ok := val.(*DPoPContext)
if !ok {
return nil
}

return dpopCtx
}

// HasDPoPContext checks if a DPoP context exists in the context.
// Returns true for DPoP-bound tokens, false for Bearer tokens.
//
// Example usage:
//
// if core.HasDPoPContext(ctx) {
// dpopCtx := core.GetDPoPContext(ctx)
// // Handle DPoP-specific logic...
// }
func HasDPoPContext(ctx context.Context) bool {
return ctx.Value(dpopContextKey) != nil
}
14 changes: 10 additions & 4 deletions core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ import (
"time"
)

// TokenValidator defines the interface for JWT validation.
// Implementations should validate the token and return the validated claims.
type TokenValidator interface {
// Validator defines the interface for JWT and DPoP validation.
// Implementations should validate tokens and DPoP proofs, returning the validated claims.
type Validator interface {
ValidateToken(ctx context.Context, token string) (any, error)
ValidateDPoPProof(ctx context.Context, proofString string) (DPoPProofClaims, error)
}

// Logger defines an optional logging interface for the core middleware.
Expand All @@ -28,9 +29,14 @@ type Logger interface {
// It contains the core logic for token validation without any dependency
// on specific transport protocols (HTTP, gRPC, etc.).
type Core struct {
validator TokenValidator
validator Validator
credentialsOptional bool
logger Logger

// DPoP fields
dpopMode DPoPMode
dpopProofOffset time.Duration
dpopIATLeeway time.Duration
}

// CheckToken validates a JWT token string and returns the validated claims.
Expand Down
12 changes: 10 additions & 2 deletions core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import (
"github.com/stretchr/testify/require"
)

// mockValidator is a mock implementation of TokenValidator for testing.
// mockValidator is a mock implementation of Validator for testing.
type mockValidator struct {
validateFunc func(ctx context.Context, token string) (any, error)
validateFunc func(ctx context.Context, token string) (any, error)
dpopValidateFunc func(ctx context.Context, proof string) (DPoPProofClaims, error)
}

func (m *mockValidator) ValidateToken(ctx context.Context, token string) (any, error) {
Expand All @@ -21,6 +22,13 @@ func (m *mockValidator) ValidateToken(ctx context.Context, token string) (any, e
return nil, errors.New("not implemented")
}

func (m *mockValidator) ValidateDPoPProof(ctx context.Context, proof string) (DPoPProofClaims, error) {
if m.dpopValidateFunc != nil {
return m.dpopValidateFunc(ctx, proof)
}
return nil, errors.New("not implemented")
}

// mockLogger is a mock implementation of Logger for testing.
type mockLogger struct {
debugCalls []logCall
Expand Down
Loading
Loading