Complete browser extension gRPC flow

This commit is contained in:
Joe Julian
2026-04-11 23:45:48 -07:00
parent 2f2338f6f2
commit d522af7d51
24 changed files with 2744 additions and 191 deletions
@@ -0,0 +1,109 @@
package main
import (
"context"
"flag"
"fmt"
"net"
"os"
"strings"
"time"
keepassgov1 "git.julianfamily.org/keepassgo/proto/keepassgo/v1"
"google.golang.org/grpc"
)
type validationServer struct {
keepassgov1.UnimplementedVaultServiceServer
statePath string
pageURL string
}
func readState(path string) string {
data, err := os.ReadFile(path)
if err != nil {
return "idle"
}
return strings.TrimSpace(string(data))
}
func writeState(path, value string) {
_ = os.WriteFile(path, []byte(value), 0o644)
}
func (s *validationServer) GetSessionStatus(context.Context, *keepassgov1.GetSessionStatusRequest) (*keepassgov1.GetSessionStatusResponse, error) {
pending := uint32(0)
if readState(s.statePath) == "pending" {
pending = 1
}
return &keepassgov1.GetSessionStatusResponse{
Locked: false,
EntryCount: 1,
PendingApprovalCount: pending,
TokenPendingApprovalCount: pending,
}, nil
}
func (s *validationServer) FindBrowserLogins(context.Context, *keepassgov1.FindBrowserLoginsRequest) (*keepassgov1.FindBrowserLoginsResponse, error) {
return &keepassgov1.FindBrowserLoginsResponse{
Matches: []*keepassgov1.BrowserLoginMatch{
{
Id: "vault-console",
Title: "Vault Console",
Username: "dannyocean",
Url: s.pageURL,
Path: []string{"Root", "Crew"},
Quality: "exact-host",
},
},
}, nil
}
func (s *validationServer) GetBrowserCredential(ctx context.Context, req *keepassgov1.GetBrowserCredentialRequest) (*keepassgov1.GetBrowserCredentialResponse, error) {
writeState(s.statePath, "pending")
ticker := time.NewTicker(200 * time.Millisecond)
defer ticker.Stop()
timeout := time.After(20 * time.Second)
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-timeout:
return nil, fmt.Errorf("timed out waiting for browser-approval state")
case <-ticker.C:
if readState(s.statePath) == "approved" {
writeState(s.statePath, "done")
return &keepassgov1.GetBrowserCredentialResponse{
Id: req.GetId(),
Username: "dannyocean",
Password: "token-1",
Url: s.pageURL,
}, nil
}
}
}
}
func main() {
listenAddr := flag.String("listen", "127.0.0.1:47779", "listen address")
statePath := flag.String("state", "", "path to mutable validation state file")
pageURL := flag.String("page-url", "http://127.0.0.1:18080/login.html", "login page URL returned by the stub")
flag.Parse()
if strings.TrimSpace(*statePath) == "" {
panic("validation state file is required")
}
listener, err := net.Listen("tcp", strings.TrimSpace(*listenAddr))
if err != nil {
panic(err)
}
server := grpc.NewServer()
keepassgov1.RegisterVaultServiceServer(server, &validationServer{
statePath: strings.TrimSpace(*statePath),
pageURL: strings.TrimSpace(*pageURL),
})
if err := server.Serve(listener); err != nil {
panic(err)
}
}