refactor: move Firebase implementation to adapters/firebase, drop pexels interface

- Create internal/adapters/firebase/auth.go with Auth, noopAuth, NewAuthOrNoop
  (renamed from FirebaseAuth, noopTokenVerifier, NewFirebaseAuthOrNoop)
- Reduce internal/auth/firebase.go to TokenVerifier interface only
- Remove PhotoSearcher interface from adapters/pexels (belongs to consumers)
- Update wire.go and wire_gen.go to use firebase.NewAuthOrNoop

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dbastrikin
2026-03-15 21:40:18 +02:00
parent fee240da7d
commit 5dc398f0d6
5 changed files with 93 additions and 89 deletions

View File

@@ -1,89 +1,8 @@
package auth
import (
"context"
"encoding/json"
"fmt"
"log/slog"
"os"
firebase "firebase.google.com/go/v4"
firebaseAuth "firebase.google.com/go/v4/auth"
"google.golang.org/api/option"
)
import "context"
// TokenVerifier abstracts Firebase token verification for testability.
type TokenVerifier interface {
VerifyToken(ctx context.Context, idToken string) (uid, email, name, avatarURL string, err error)
}
type FirebaseAuth struct {
client *firebaseAuth.Client
}
// noopTokenVerifier is used in local development when Firebase credentials are not available.
// It rejects all tokens with a clear error message.
type noopTokenVerifier struct{}
func (n *noopTokenVerifier) VerifyToken(_ context.Context, _ string) (uid, email, name, avatarURL string, err error) {
return "", "", "", "", fmt.Errorf("Firebase auth is not configured (running in noop mode)")
}
// NewFirebaseAuthOrNoop tries to initialize Firebase auth from the credentials file.
// If the file is missing, empty, or not a valid service account, it logs a warning
// and returns a noop verifier that rejects all tokens.
func NewFirebaseAuthOrNoop(credentialsFile string) (TokenVerifier, error) {
data, err := os.ReadFile(credentialsFile)
if err != nil || len(data) == 0 {
slog.Warn("firebase credentials not found, running without Firebase auth", "file", credentialsFile)
return &noopTokenVerifier{}, nil
}
var creds map[string]interface{}
if err := json.Unmarshal(data, &creds); err != nil || creds["type"] == nil {
slog.Warn("firebase credentials invalid, running without Firebase auth", "file", credentialsFile)
return &noopTokenVerifier{}, nil
}
fa, err := NewFirebaseAuth(credentialsFile)
if err != nil {
return nil, err
}
return fa, nil
}
func NewFirebaseAuth(credentialsFile string) (*FirebaseAuth, error) {
opt := option.WithCredentialsFile(credentialsFile)
app, err := firebase.NewApp(context.Background(), nil, opt)
if err != nil {
return nil, err
}
client, err := app.Auth(context.Background())
if err != nil {
return nil, err
}
return &FirebaseAuth{client: client}, nil
}
func (f *FirebaseAuth) VerifyToken(ctx context.Context, idToken string) (uid, email, name, avatarURL string, err error) {
token, err := f.client.VerifyIDToken(ctx, idToken)
if err != nil {
return "", "", "", "", err
}
uid = token.UID
if v, ok := token.Claims["email"].(string); ok {
email = v
}
if v, ok := token.Claims["name"].(string); ok {
name = v
}
if v, ok := token.Claims["picture"].(string); ok {
avatarURL = v
}
return uid, email, name, avatarURL, nil
}