package auth import ( "context" "crypto/rand" "fmt" "github.com/jackc/pgx/v5/pgxpool" ) // PostgresClientStore persists dynamically registered OAuth clients (RFC 7591) in PostgreSQL. type PostgresClientStore struct { pool *pgxpool.Pool } func NewPostgresClientStore(pool *pgxpool.Pool) *PostgresClientStore { return &PostgresClientStore{pool: pool} } func (s *PostgresClientStore) Register(name string, redirectURIs []string) (DynamicClient, error) { b := make([]byte, 16) if _, err := rand.Read(b); err != nil { return DynamicClient{}, err } clientID := fmt.Sprintf("%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]) var client DynamicClient row := s.pool.QueryRow(context.Background(), ` insert into oauth_clients (client_id, client_name, redirect_uris) values ($1, $2, $3) returning client_id, client_name, redirect_uris, created_at `, clientID, name, redirectURIs) if err := row.Scan(&client.ClientID, &client.ClientName, &client.RedirectURIs, &client.CreatedAt); err != nil { return DynamicClient{}, fmt.Errorf("register oauth client: %w", err) } return client, nil } func (s *PostgresClientStore) Lookup(clientID string) (DynamicClient, bool) { var client DynamicClient row := s.pool.QueryRow(context.Background(), ` select client_id, client_name, redirect_uris, created_at from oauth_clients where client_id = $1 `, clientID) if err := row.Scan(&client.ClientID, &client.ClientName, &client.RedirectURIs, &client.CreatedAt); err != nil { return DynamicClient{}, false } return client, true }