feat(integrations): show account names and add legal pages #152
No reviewers
Labels
No labels
auth
autorelease: pending
autorelease: tagged
bug
dependencies
devops
docker
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
javascript
maintenance
question
refactor
ui
wontfix
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
gitchegumi/GitchAlerts!152
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/oauth-account-names-legal-footer"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
AGENTS.mdguidance covering architecture, validation, security and OAuth invariants, documentation expectations, and categorized manual branch namesWhy
Connected accounts were exposing numeric provider identifiers that creators do not recognize. Twitch also only rendered the first linked account and offered no way to add another channel. The original partial-disconnect implementation rebuilt the entire workspace subscription set and could strand remaining broadcasters if Twitch retained an old subscription or rejected a replacement. The UI-only permission and channel-limit checks could also be bypassed by direct requests.
Concurrent OAuth callbacks created two additional races: a non-atomic count followed by activation could exceed the five-channel limit, and releasing the workspace lock before provisioning could let two first links generate different EventSub secrets. The lock now spans account commitment through provider provisioning, and Twitch disconnect uses that same workspace boundary, ensuring one limit decision and one shared-secret lifecycle operation per workspace at a time.
Public legal pages and a descriptive homepage are needed for a complete Google OAuth verification submission. The provider setup guide was previously buried at the end of the in-app documentation navigation, making the required Twitch and Google console configuration easy to miss. Repository guidance keeps future agent work aligned with the project's security boundaries and development workflow.
User impact
Workspace managers can identify linked accounts by name, connect multiple Twitch channels, and manage each connection separately. Disconnecting one broadcaster now removes only that broadcaster's tracked subscriptions. If Twitch rejects deletion, GitchAlerts keeps the account, tokens, shared secret, and local tracking state intact so the operation can be retried.
Concurrent OAuth completions and Twitch disconnects are serialized per workspace. Reconnecting an already-active account at the five-channel cap remains supported, simultaneous first links create all EventSub subscriptions with the same persisted webhook secret, and a last-account disconnect cannot clear shared state while another link is provisioning.
Public deployments receive same-domain legal URLs suitable for provider configuration after setting
LEGAL_OPERATOR_NAMEandLEGAL_CONTACT_EMAIL. Developers get direct setup instructions for the Twitch Developer Console and Google Cloud Console, including exact OAuth callbacks and the optional public-app verification process.Migration
provider_subscriptions.provider_account_idValidation
git diff --checkpassedThe friendly account identities, public homepage/legal pages, and basic shared-secret reuse path are sound. The public pages returned 200 in a production build and rendered cleanly; web build, monorepo typecheck, lint, 202 web tests, 69 database tests,
git diff --check, and hosted CI all pass.Two blockers remain in the new multi-Twitch lifecycle: the partial-disconnect path can silently disable every remaining broadcaster, and the five-account/manager restrictions exist only in the client. See the inline findings.
Baseline-only notes, not attributed to this PR:
pnpm -r teststill fails the unchanged shared version assertions (0.1.0versus root0.10.9), andpnpm format:checkstill reports 12 unchanged files. These same failures are present onmainand are not blockers for #152.@ -97,0 +144,4 @@if (account.channelId &&!(await deps.canManageChannelCredentials(session.user.id, session.user.role, account.channelId))) {Blocking: this destructive workspace-wide rebuild can silently break alerts for every Twitch account that remains linked.
teardownTwitchEventSubclears the shared secret and deletes local subscription records even when a remote DELETE throws or returns a non-2xx status; this route then provisions with a new secret and ignores eachTwitchProvisionResult.failed. If Twitch retained the old subscriptions, the replacement POSTs conflict (409), the endpoint still returns success, and the workspace is left with a new secret but zero usable subscriptions. I reproduced that exact sequence with an exit-successful fetch stub: the remaining broadcaster rebuilt withcreated: 0, failed: 7. Even after successful deletes, a transient POST failure takes working broadcasters offline. Do not rebuild all remaining broadcasters as a best-effort side effect. Track subscriptions by broadcaster and remove only the disconnected account's records, or otherwise preserve the shared secret and fail closed until remote deletion/recreation is confirmed. Add a route-level partial-disconnect test covering remote DELETE failure, POST failure, and preservation of the remaining accounts.@ -287,13 +288,27 @@ export function IntegrationsSection({<AdminNote provider="Twitch" envVars={['TWITCH_CLIENT_ID', 'TWITCH_CLIENT_SECRET']} />Blocking:
canManageandMAX_TWITCH_CHANNELSonly hide this button; neither restriction is enforced at the authoritative OAuth/API path./api/auth/linkaccepts any authenticated user, resolves any supplied workspace slug withoutcanManageChannelCredentials, and the sign-in callback upserts/provisions without rechecking permission or the five-active-account cap.DELETE /api/linked-accountslikewise checks account ownership but not current workspace credential-management authority, and now triggers the destructive rebuild above. A viewer/editor—or an owner demoted after linking—can call these endpoints directly, and a manager can link a sixth account by bypassing the UI. Enforce workspace credential permission at link initiation, callback commitment, and disconnect; enforce the active per-workspace Twitch count at callback time while still allowing an existing account to reconnect. Add direct-handler tests for unauthorized roles, changed permissions during OAuth, five-versus-six accounts, and reconnect-at-cap.The two commits pushed during review add repository guidance and OAuth-console documentation but do not change the multi-Twitch production paths covered by the inline blockers on the previous head. Those findings therefore remain current: partial disconnect can silently strand all remaining Twitch broadcasters after remote-delete or reprovision failure, and manager/five-account restrictions remain client-only rather than enforced at OAuth commitment and disconnect.
The new documentation is internally consistent: focused Prettier validation and local Markdown-link checks pass, and
git diff --checkremains clean. This review supersedes the same findings posted againstb0c8ff1and ties the requested-changes state to the current head.The partial-disconnect blocker is fixed: Twitch subscriptions are now tracked per broadcaster, selective teardown preserves the shared secret and other broadcasters, non-2xx remote deletion fails closed, and the route retains account/token state for retry. Link initiation, OAuth commitment, and disconnect also recheck credential-management authority, and the sequential five-versus-six/reconnect-at-cap cases work.
One part of the cap blocker remains: the callback performs a non-atomic count-then-upsert, so concurrent OAuth completions can exceed the server-side limit. See the inline reproduction.
Current-head validation otherwise passes: Prisma generation and schema validation, 212 web tests, 71 database tests, monorepo typecheck/lint, production web build,
git diff --check, and all hosted checks.@ -224,6 +228,185 @@ if (googleEnabled) {);Blocking: the cap check is still subject to a count-then-upsert race. Two managers can complete different Twitch OAuth callbacks concurrently; both can read four active accounts here, both pass the check, and both upsert, leaving six active accounts. I reproduced this against
handleLinkedOAuthSignInwith a barrier around the two count calls: both returnedtrueand the final active count was 6. Make the cap decision and LinkedAccount activation atomic per workspace (for example, a serializable transaction with conflict retry or a transaction-scoped PostgreSQL advisory lock), while preserving reconnect-at-cap behavior. Add a concurrent commitment test that starts at four and proves exactly one of two new links succeeds.The advisory transaction lock fixes the five-account count/upsert race: concurrent commitments are serialized, the fifth account succeeds, and the sixth is rejected. The new regression covers that outcome.
The lock boundary is still too narrow for Twitch's shared-secret lifecycle, however. It is released before auto-provisioning, so two accepted links below the cap can provision concurrently and create subscriptions under different secrets. See the inline reproduction.
Current-head validation otherwise passes: 213 web tests, monorepo typecheck/lint, production web build,
git diff --check, and all hosted checks.@ -224,6 +228,185 @@ if (googleEnabled) {);Blocking: releasing the workspace advisory lock before provisioning still permits a split-secret race. When two new Twitch links are both admissible (for example, a workspace starts below four accounts), their serialized account transactions complete and then both enter
provisionLinkedAccountconcurrently. If the workspace has no EventSub secret yet, bothprovisionTwitchEventSubcalls can readnull, generate different secrets, persist them in succession, and create remote subscriptions under both values; only the last persisted secret will verify callbacks. I reproduced both layers:handleLinkedOAuthSignInreached provisioning with max concurrency 2, and two concurrent first-broadcaster provisions produced two distinct transport secrets while both reported 7 created subscriptions. Keep Twitch secret selection/persistence and remote provisioning inside the same per-workspace serialization boundary (or add an equivalent provider-level lock that spans the full check/create sequence), and add a concurrent first-link test asserting every subscription uses the one persisted secret.The concurrent-link split-secret race is fixed: account commitment and provisioning now share the advisory-lock boundary, and the new regression proves two first links generate one persisted secret used by all 14 subscriptions.
One cross-operation race remains because disconnect does not acquire that workspace lock. A disconnect can still perform last-account teardown while a link is provisioning under the lock, clear the shared secret, and leave the newly committed account with subscriptions signed by a removed secret. See the inline reproduction.
Current-head validation otherwise passes: 214 web tests, monorepo typecheck/lint, production web build,
git diff --check, and all hosted checks.@ -97,0 +168,4 @@console.error('[linked-accounts] Twitch broadcaster teardown failed', {channelId: account.channelId,reason: err instanceof Error ? err.message : 'unknown_error',});Blocking: disconnect must participate in the same per-workspace advisory lock as link/provision. Right now a link can hold the lock, upsert its account invisibly inside the transaction, and provision subscriptions while this path runs concurrently without waiting. Disconnect can deactivate the previously visible last account, count zero because the new link is uncommitted, run full workspace teardown, and clear the shared secret; the link then commits an active account whose newly created subscriptions use the removed secret. I reproduced the missing serialization directly: while
handleLinkedOAuthSignInheld its workspace lock inside provisioning,handleDeleteLinkedAccountcompleted and invoked full teardown instead of waiting. Wrap the complete Twitch disconnect decision/teardown/update sequence in the same workspace lock and add a concurrent link-versus-last-disconnect test proving teardown cannot run against an in-flight link.@ -97,0 +168,4 @@console.error('[linked-accounts] Twitch broadcaster teardown failed', {channelId: account.channelId,reason: err instanceof Error ? err.message : 'unknown_error',});Addressed in
9a4ba80. Twitch disconnect now uses the same per-workspace advisory-lock boundary as OAuth commitment, re-reads the selected account after acquiring the lock, and performs broadcaster teardown, deactivation, the remaining-account count, and final shared-state teardown while that lock is held. Added a concurrent link-versus-last-disconnect regression that pauses link provisioning, confirms disconnect cannot start teardown, then verifies the completed link prevents workspace teardown. Validation: focused route tests (4/4), full web suite (215/215), web typecheck/lint, Prettier, andgit diff --checkall pass.All previously blocking multi-Twitch lifecycle issues are resolved. Link initiation, OAuth commitment, provisioning, and disconnect enforce current authority and share the same per-workspace advisory-lock boundary; the five-account cap is concurrency-safe; first-link provisioning uses one persisted EventSub secret; partial disconnect preserves other broadcasters; remote deletion fails closed; and last-account disconnect now waits for in-flight link provisioning before deciding whether to tear down shared state.
Validation at this head passes: Prisma generation/schema validation from the schema-bearing review round, 215 web tests, 71 database tests, monorepo typecheck/lint, production web build,
git diff --check, and all hosted checks. Approved.