122 lines
3.7 KiB
Go
122 lines
3.7 KiB
Go
package auth
|
|
|
|
import (
|
|
"net/http"
|
|
"sync"
|
|
)
|
|
|
|
// Reference: P2E §3 — Webhook authentication registry
|
|
// Each channel provider stores an "identifier" (bot_token, website_token, phone_number_id)
|
|
// which is used to route and validate incoming webhook requests.
|
|
|
|
// WebhookTokenRegistry manages webhook authentication tokens per channel.
|
|
// Tokens are loaded from DB on startup and refreshed when inboxes are created/updated.
|
|
type WebhookTokenRegistry struct {
|
|
mu sync.RWMutex
|
|
tokens map[string]map[string]WebhookTokenEntry // channel_type → identifier → entry
|
|
}
|
|
|
|
// WebhookTokenEntry stores a channel's webhook authentication data.
|
|
type WebhookTokenEntry struct {
|
|
InboxID uint
|
|
AccountID uint
|
|
Secret string // HMAC secret for verification (varies by channel)
|
|
Identifier string // unique identifier for routing (bot_token, website_token, etc.)
|
|
}
|
|
|
|
// NewWebhookTokenRegistry creates an empty token registry.
|
|
func NewWebhookTokenRegistry() *WebhookTokenRegistry {
|
|
return &WebhookTokenRegistry{
|
|
tokens: make(map[string]map[string]WebhookTokenEntry),
|
|
}
|
|
}
|
|
|
|
// Register adds a webhook token entry for a channel.
|
|
func (r *WebhookTokenRegistry) Register(channelType, identifier string, entry WebhookTokenEntry) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
if r.tokens[channelType] == nil {
|
|
r.tokens[channelType] = make(map[string]WebhookTokenEntry)
|
|
}
|
|
r.tokens[channelType][identifier] = entry
|
|
}
|
|
|
|
// Unregister removes a webhook token entry.
|
|
func (r *WebhookTokenRegistry) Unregister(channelType, identifier string) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
|
|
if r.tokens[channelType] != nil {
|
|
delete(r.tokens[channelType], identifier)
|
|
}
|
|
}
|
|
|
|
// Lookup finds a webhook token entry by channel type and identifier.
|
|
func (r *WebhookTokenRegistry) Lookup(channelType, identifier string) (*WebhookTokenEntry, bool) {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
|
|
if r.tokens[channelType] == nil {
|
|
return nil, false
|
|
}
|
|
entry, ok := r.tokens[channelType][identifier]
|
|
if !ok {
|
|
return nil, false
|
|
}
|
|
return &entry, true
|
|
}
|
|
|
|
// Validate checks a webhook request against the stored token entry.
|
|
// The validation strategy depends on the channel type:
|
|
// - Telegram: X-Telegram-Bot-Api-Secret-Token header
|
|
// - Web Widget: HMAC token verification
|
|
// - Facebook/WhatsApp: app secret + signature verification
|
|
func (r *WebhookTokenRegistry) Validate(channelType, identifier string, req *http.Request) (bool, error) {
|
|
entry, found := r.Lookup(channelType, identifier)
|
|
if !found {
|
|
return false, nil // unknown identifier → reject
|
|
}
|
|
|
|
switch channelType {
|
|
case "telegram":
|
|
// Telegram validates via X-Telegram-Bot-Api-Secret-Token header
|
|
token := req.Header.Get("X-Telegram-Bot-Api-Secret-Token")
|
|
if entry.Secret == "" {
|
|
return true, nil // no secret configured → accept all
|
|
}
|
|
return token == entry.Secret, nil
|
|
|
|
case "web_widget":
|
|
// Web widget HMAC verification (handled separately in WebSocket auth)
|
|
return true, nil
|
|
|
|
case "facebook":
|
|
// Facebook webhook validation: app secret + X-Hub-Signature-256
|
|
// TODO: implement Facebook signature verification (P8)
|
|
return true, nil
|
|
|
|
case "whatsapp":
|
|
// WhatsApp webhook validation: Bearer token verification
|
|
// TODO: implement WhatsApp token verification (P8)
|
|
return true, nil
|
|
|
|
default:
|
|
return true, nil // unknown channel type → accept (will be filtered by registry)
|
|
}
|
|
}
|
|
|
|
// GetAllIdentifiers returns all registered identifiers for a channel type.
|
|
func (r *WebhookTokenRegistry) GetAllIdentifiers(channelType string) []WebhookTokenEntry {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
|
|
entries := []WebhookTokenEntry{}
|
|
if r.tokens[channelType] == nil {
|
|
return entries
|
|
}
|
|
for _, entry := range r.tokens[channelType] {
|
|
entries = append(entries, entry)
|
|
}
|
|
return entries
|
|
} |