feat(web): web-initiated YouTube OAuth connect flow (ADR-006)
Add GET /oauth/youtube/connect and /oauth/youtube/callback, mounted inside the login + registration guard so CurrentUserID is always set and every connection binds to the authenticated tapir user. - connect: generate a per-user CSRF state (single-use, short TTL, bound to the user), redirect to Google consent with access_type=offline and prompt=consent so a refresh token comes back. - callback: verify the state belongs to this user, exchange the code via the existing auth.Exchange, persist the refresh token under a PER-USER ref (web.YouTubeTokenRef = "youtube/<userID>/refresh_token") so tenants never collide, then UpsertConnection (provider=youtube, status=active). Any failure renders a clean error page and leaves no half-written state. Reuses auth.Exchange and adds auth.AuthCodeURL (offline + consent) rather than the CLI's listener/terminal flow (ADR-006: web flow, not CLI). The ConnectHandler depends on a narrow web.Connections port, not the concrete store. Wired in cmdServe only when YT client credentials are present; TAPIR_YT_CONNECT_REDIRECT_URL configures the callback URL. Per-user token-ref scheme documented in docs/homelab-integration.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -187,6 +187,20 @@ func cmdServe(ctx context.Context, log *slog.Logger) error {
|
||||
}
|
||||
|
||||
app := &web.App{Store: st, Identity: st, Auth: authn, Log: log}
|
||||
|
||||
// Web-initiated YouTube connect (ADR-006). Mounted only when the OAuth client
|
||||
// credentials are present; the refresh token persists through the SecretStore
|
||||
// under a per-user ref (web.YouTubeTokenRef). Live connect also needs the
|
||||
// callback URL registered in the Google OAuth client's authorized redirects.
|
||||
if cfg.YTClientID != "" && cfg.YTClientSecret != "" {
|
||||
app.Connect = web.NewConnectHandler(auth.Config{
|
||||
ClientID: cfg.YTClientID,
|
||||
ClientSecret: cfg.YTClientSecret,
|
||||
RedirectURL: cfg.YTConnectRedirectURL,
|
||||
}, secrets.NewFileStore(cfg.SecretsFile), st, log)
|
||||
log.Info("web youtube connect enabled", "redirect", cfg.YTConnectRedirectURL)
|
||||
}
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: cfg.HTTPAddr,
|
||||
Handler: app.Router(),
|
||||
|
||||
Reference in New Issue
Block a user