Add PKCE, token-body extras, AuthURL templating to OAuth2 → Add settings structs for Pattern-2 connector providers

- Add PKCE, token-body extras, AuthURL templating to OAuth2
- Add 13 connector provider enum values
- Add scopes, display names, name resolvers for 13 providers
- Add settings structs for Pattern-2 connector providers

Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com>
This commit is contained in:
Aurélien Sibiril
2026-05-17 17:22:48 +02:00
parent 3ff66757ad
commit 0147acd9f0
10 changed files with 1334 additions and 5 deletions

View File

@@ -14,7 +14,12 @@
package connector
import "go.gearno.de/kit/httpclient"
import (
"maps"
"strings"
"go.gearno.de/kit/httpclient"
)
// CallbackPath is the HTTP path for the OAuth2 callback endpoint.
const CallbackPath = "/api/console/v1/connectors/complete"
@@ -30,6 +35,14 @@ type providerDefinition struct {
ExtraAuthParams map[string]string
TokenEndpointAuth string // "post-form" (default), "basic-form", or "basic-json"
SupportsIncrementalAuth bool
// RequiresPKCE enables RFC 7636 PKCE (S256) on the authorization
// request and replays the verifier on the token exchange. Default
// false; existing providers are unaffected.
RequiresPKCE bool
// TokenExtraParams are merged into the token-exchange request body
// (form-encoded for "post-form"/"basic-form", JSON for "basic-json").
// Used by providers like Lever that require an `audience` parameter.
TokenExtraParams map[string]string
}
// providerDefinitions maps provider names to their static OAuth2 definitions.
@@ -91,6 +104,83 @@ var (
AuthURL: "https://linear.app/oauth/authorize",
TokenURL: "https://api.linear.app/oauth/token",
},
"GITLAB": {
AuthURL: "https://gitlab.com/oauth/authorize",
TokenURL: "https://gitlab.com/oauth/token",
},
// Bitbucket scopes are pinned on the OAuth consumer at registration
// time (`account` for workspace membership). They are not passed in
// the authorize URL and not configured here.
"BITBUCKET": {
AuthURL: "https://bitbucket.org/site/oauth2/authorize",
TokenURL: "https://bitbucket.org/site/oauth2/access_token",
},
"HEROKU": {
AuthURL: "https://id.heroku.com/oauth/authorize",
TokenURL: "https://id.heroku.com/oauth/token",
},
"PAGERDUTY": {
AuthURL: "https://identity.pagerduty.com/oauth/authorize",
TokenURL: "https://identity.pagerduty.com/oauth/token",
RequiresPKCE: true,
},
"ASANA": {
AuthURL: "https://app.asana.com/-/oauth_authorize",
TokenURL: "https://app.asana.com/-/oauth_token",
},
"SNYK": {
AuthURL: "https://app.snyk.io/oauth2/authorize",
TokenURL: "https://api.snyk.io/oauth2/token",
RequiresPKCE: true,
},
"NETLIFY": {
AuthURL: "https://app.netlify.com/authorize",
TokenURL: "https://api.netlify.com/oauth/token",
},
"RAMP": {
AuthURL: "https://app.ramp.com/v1/authorize",
TokenURL: "https://api.ramp.com/developer/v1/token",
TokenEndpointAuth: "basic-form",
},
"CLICKUP": {
AuthURL: "https://app.clickup.com/api",
TokenURL: "https://api.clickup.com/api/v2/oauth/token",
},
// Vercel uses a templated AuthURL: the operator supplies an
// `integration-slug` config field which is resolved into the
// "{integration_slug}" placeholder by ApplyProviderDefaults.
// Vercel does not use OAuth scopes — capabilities are pinned on
// the integration registration in the Vercel dashboard.
"VERCEL": {
AuthURL: "https://vercel.com/integrations/{integration_slug}/new",
TokenURL: "https://api.vercel.com/v2/oauth/access_token",
},
"MONDAY": {
AuthURL: "https://auth.monday.com/oauth2/authorize",
TokenURL: "https://auth.monday.com/oauth2/token",
},
// Lever runs on Auth0: the `audience` parameter is required in
// BOTH the authorize URL and the token-exchange POST body. The
// trailing slash on the audience value is mandatory.
"LEVER": {
AuthURL: "https://auth.lever.co/authorize",
TokenURL: "https://auth.lever.co/oauth/token",
ExtraAuthParams: map[string]string{
"audience": "https://api.lever.co/v1/",
"prompt": "consent",
},
TokenExtraParams: map[string]string{
"audience": "https://api.lever.co/v1/",
},
},
// Deel: the token endpoint path is "/oauth2/tokens" (plural) —
// Deel's docs are inconsistent on the singular vs plural form.
// The API base host (api.letsdeel.com) differs from the auth host
// (app.deel.com).
"DEEL": {
AuthURL: "https://app.deel.com/oauth2/authorize",
TokenURL: "https://app.deel.com/oauth2/tokens",
},
}
)
@@ -108,5 +198,22 @@ func ApplyProviderDefaults(provider string, redirectURI string, c *OAuth2Connect
c.ExtraAuthParams = def.ExtraAuthParams
c.TokenEndpointAuth = def.TokenEndpointAuth
c.SupportsIncrementalAuth = def.SupportsIncrementalAuth
c.RequiresPKCE = def.RequiresPKCE
// Deep copy TokenExtraParams so per-connector mutations cannot
// alias back into the shared providerDefinitions map.
if len(def.TokenExtraParams) > 0 {
tokenExtra := make(map[string]string, len(def.TokenExtraParams))
maps.Copy(tokenExtra, def.TokenExtraParams)
c.TokenExtraParams = tokenExtra
}
// Resolve operator-supplied placeholders in the static AuthURL
// (for example Vercel's "{integration_slug}"). Providers without
// placeholders are unaffected; the loop is a no-op when
// AuthURLParams is empty.
for k, v := range c.AuthURLParams {
c.AuthURL = strings.ReplaceAll(c.AuthURL, "{"+k+"}", v)
}
}
}