72 lines
2.1 KiB
Go
72 lines
2.1 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
firebase "firebase.google.com/go/v4"
|
|
fbauth "firebase.google.com/go/v4/auth"
|
|
"google.golang.org/api/option"
|
|
"watch-party-backend/internal/config"
|
|
)
|
|
|
|
// TokenVerifier hides Firebase client behind a small interface for testing.
|
|
type TokenVerifier interface {
|
|
Verify(ctx context.Context, token string) (*fbauth.Token, error)
|
|
}
|
|
|
|
// AuthClient can verify tokens and mutate Firebase custom claims.
|
|
type AuthClient interface {
|
|
TokenVerifier
|
|
SetAdminClaim(ctx context.Context, uid string) (map[string]interface{}, error)
|
|
}
|
|
|
|
// FirebaseAuth verifies Firebase ID tokens.
|
|
type FirebaseAuth struct {
|
|
client *fbauth.Client
|
|
}
|
|
|
|
// NewFirebaseAuth builds the Firebase client from config.
|
|
func NewFirebaseAuth(ctx context.Context, cfg config.FirebaseConfig) (*FirebaseAuth, error) {
|
|
creds, err := cfg.CredentialsBytes()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(creds) == 0 {
|
|
return nil, errors.New("firebase credentials empty")
|
|
}
|
|
|
|
app, err := firebase.NewApp(ctx, &firebase.Config{ProjectID: cfg.ProjectID}, option.WithCredentialsJSON(creds))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("init firebase app: %w", err)
|
|
}
|
|
client, err := app.Auth(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("init firebase auth client: %w", err)
|
|
}
|
|
return &FirebaseAuth{client: client}, nil
|
|
}
|
|
|
|
// Verify checks a Firebase ID token and returns its claims.
|
|
func (f *FirebaseAuth) Verify(ctx context.Context, token string) (*fbauth.Token, error) {
|
|
return f.client.VerifyIDToken(ctx, token)
|
|
}
|
|
|
|
// SetAdminClaim sets the "admin" custom claim while preserving existing claims.
|
|
func (f *FirebaseAuth) SetAdminClaim(ctx context.Context, uid string) (map[string]interface{}, error) {
|
|
user, err := f.client.GetUser(ctx, uid)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get user: %w", err)
|
|
}
|
|
claims := make(map[string]interface{}, len(user.CustomClaims)+1)
|
|
for k, v := range user.CustomClaims {
|
|
claims[k] = v
|
|
}
|
|
claims["admin"] = true
|
|
if err := f.client.SetCustomUserClaims(ctx, uid, claims); err != nil {
|
|
return nil, fmt.Errorf("set custom claims: %w", err)
|
|
}
|
|
return claims, nil
|
|
}
|