
Add OAuth Provider
Adds an OAuth provider with its code changes and approval steps.
Install
npx skills add superdesigndev/treg --skill add-oauth-provider -g -a claude-code -y- Repository
- superdesigndev/treg7k
- Category
- Engineering
- Updated
- 3d ago
- License
- See repository
- Context cost
- Mediumabout 2,360 tokens
- Runs scripts
- No, instructions only
What it does
Add a provider to treg's OAuth registry (the ones treg holds its own approved app for). Use when asked to "add YouTube/Notion/Meta OAuth", "support connecting X", "add a new OAuth provider", or when a connect flow, capability picker, channel/account picker, or provider health probe needs building. Covers the code changes, the platform-side approval steps, and the pitfalls that don't announce themselves.
Adding an OAuth provider
Two connect modes exist. BYO (POST /oauth/start with a caller-supplied
client_id/secret) already works for any OAuth2 provider and needs no code. This
skill is the registry path: treg owns the registered app, so the user picks a
provider and supplies nothing. Only add a provider here if treg holds, or is
willing to go get, the platform approval behind it.
Read src/treg/oauth_providers.py's module docstring first. The rule it states is
the one most easily broken: scopes are per capability, never per provider.
The code changes
All of these, in order. Skipping any one produces a provider that looks fine in
/oauth/providers and fails somewhere the tests don't reach.
1. Credentials → src/treg/config.py
Add <name>_client_id / <name>_client_secret to Settings, loaded from
TREG_<NAME>_CLIENT_ID / _SECRET. Reuse an existing pair when the platform
uses one app for several products, all four Google providers plus YouTube
share google_client_id, because Google verification and API audits are scoped to
the Cloud project, not to the OAuth client. A second client in the same project
isolates nothing.
2. The provider → src/treg/oauth_providers.py
Add the OAuthProvider and register it in REGISTRY (easy to forget; the
provider silently doesn't exist until you do).
Capabilities are cumulative supersets, never swaps:
scopes={
"read": [READ_SCOPES],
"post": [*READ_SCOPES, UPLOAD], # post CONTAINS read
"manage": [*READ_SCOPES, UPLOAD, WRITE], # manage CONTAINS post
}
satisfied_capabilities() is set-containment, so a non-cumulative write yields a
connection that can write but reports "no read". default_capability is the
broadest (most scopes), deliberately, see its docstring.
