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
51 changes: 51 additions & 0 deletions adapters/events/watermill_publisher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package events

import (
"context"
"encoding/json"
"fmt"

"github.com/ThreeDotsLabs/watermill/message"
"github.com/layer-3/barong/ports"
)

// LogoutEvent represents a logout event
type LogoutEvent struct {
Address string `json:"address"`
TokenID string `json:"token_id"`
}

// WatermillPublisher implements the EventPublisher interface using Watermill
type WatermillPublisher struct {
publisher message.Publisher
topic string
}

// NewWatermillPublisher creates a new Watermill publisher
func NewWatermillPublisher(publisher message.Publisher) ports.EventPublisher {
return &WatermillPublisher{
publisher: publisher,
topic: "barong.logout",
}
}

// PublishLogout publishes a logout event
func (p *WatermillPublisher) PublishLogout(ctx context.Context, address string, tokenID string) error {
event := LogoutEvent{
Address: address,
TokenID: tokenID,
}

payload, err := json.Marshal(event)
if err != nil {
return fmt.Errorf("failed to marshal event: %w", err)
}

msg := message.NewMessage(tokenID, payload)

if err := p.publisher.Publish(p.topic, msg); err != nil {
return fmt.Errorf("failed to publish event: %w", err)
}

return nil
}
64 changes: 64 additions & 0 deletions adapters/store/memory_store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package store

import (
"context"
"sync"
"time"

"github.com/layer-3/barong/ports"
)

// MemoryStore is an in-memory implementation of the Store interface
type MemoryStore struct {
invalidatedTokens map[string]time.Time
mu sync.RWMutex
}

// NewMemoryStore creates a new in-memory store
func NewMemoryStore() ports.Store {
return &MemoryStore{
invalidatedTokens: make(map[string]time.Time),
}
}

// InvalidateToken marks a token as invalidated
func (s *MemoryStore) InvalidateToken(ctx context.Context, tokenID string, expiry time.Duration) error {
s.mu.Lock()
defer s.mu.Unlock()

expiryTime := time.Now().Add(expiry)
s.invalidatedTokens[tokenID] = expiryTime

// Start a cleanup goroutine
go func() {
time.Sleep(expiry)

s.mu.Lock()
defer s.mu.Unlock()

// Only delete if the expiry time hasn't changed
if storedExpiry, exists := s.invalidatedTokens[tokenID]; exists && !storedExpiry.After(expiryTime) {
delete(s.invalidatedTokens, tokenID)
}
}()

return nil
}

// IsTokenInvalidated checks if a token is invalidated
func (s *MemoryStore) IsTokenInvalidated(ctx context.Context, tokenID string) (bool, error) {
s.mu.RLock()
defer s.mu.RUnlock()

expiryTime, exists := s.invalidatedTokens[tokenID]
if !exists {
return false, nil
}

// Check if the token invalidation has expired
if time.Now().After(expiryTime) {
return false, nil
}

return true, nil
}
49 changes: 49 additions & 0 deletions adapters/store/redis_store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package store

import (
"context"
"fmt"
"time"

"github.com/layer-3/barong/ports"
"github.com/redis/go-redis/v9"
)

// RedisStore is a Redis implementation of the Store interface
type RedisStore struct {
client *redis.Client
prefix string
}

// NewRedisStore creates a new Redis store
func NewRedisStore(client *redis.Client) ports.Store {
return &RedisStore{
client: client,
prefix: "barong:invalidated:",
}
}

// InvalidateToken marks a token as invalidated in Redis
func (s *RedisStore) InvalidateToken(ctx context.Context, tokenID string, expiry time.Duration) error {
key := s.prefix + tokenID

// Set key with expiration
if err := s.client.Set(ctx, key, "1", expiry).Err(); err != nil {
return fmt.Errorf("failed to invalidate token: %w", err)
}

return nil
}

// IsTokenInvalidated checks if a token is invalidated in Redis
func (s *RedisStore) IsTokenInvalidated(ctx context.Context, tokenID string) (bool, error) {
key := s.prefix + tokenID

// Check if key exists
val, err := s.client.Exists(ctx, key).Result()
if err != nil {
return false, fmt.Errorf("failed to check token invalidation: %w", err)
}

return val > 0, nil
}
20 changes: 20 additions & 0 deletions adapters/tokenizer/claims.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package tokenizer

import "github.com/golang-jwt/jwt/v5"

// ChallengeClaims combines standard claims with challenge-specific ones
type ChallengeClaims struct {
jwt.RegisteredClaims
Nonce string `json:"nonce"`
}

// AccessClaims combines standard claims with access-specific ones
type AccessClaims struct {
jwt.RegisteredClaims
RefreshID string `json:"rid"` // ID of the refresh token
}

// RefreshClaims are just the standard claims for refresh tokens
type RefreshClaims struct {
jwt.RegisteredClaims
}
Loading