From 03827704ec8cf3a2f372422114da277c08135024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Sibiril?= <81782+aureliensibiril@users.noreply.github.com> Date: Thu, 4 Jun 2026 14:45:08 +0200 Subject: [PATCH] Register Okta connector and wire API-key settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register the Okta provider (SupportsAPIKey, APIKeyAuthScheme SSWS, a required "domain" extra setting, and the driver/name-resolver factories) and add it to the builtin registry. The create-API-key resolver normalizes and validates oktaDomain into OktaConnectorSettings, returning a static INVALID error that never echoes operator input, and stamps the SSWS scheme onto the connection. No picker, OAuth metadata, or probe URL: the token plus domain identify exactly one org and the host is per-connection. Signed-off-by: Aurélien Sibiril <81782+aureliensibiril@users.noreply.github.com> --- pkg/connector/provider/builtin.go | 1 + pkg/connector/provider/okta.go | 77 +++++++++++++++++++ .../api/console/v1/connector_resolvers.go | 1 + .../api/console/v1/connector_settings.go | 16 ++++ 4 files changed, 95 insertions(+) create mode 100644 pkg/connector/provider/okta.go diff --git a/pkg/connector/provider/builtin.go b/pkg/connector/provider/builtin.go index f0c8d9c48..93fc5ad7b 100644 --- a/pkg/connector/provider/builtin.go +++ b/pkg/connector/provider/builtin.go @@ -45,6 +45,7 @@ func NewBuiltinRegistry() *Registry { mondayRegistration(), netlifyRegistration(), notionRegistration(), + oktaRegistration(), onePasswordRegistration(), openaiRegistration(), posthogRegistration(), diff --git a/pkg/connector/provider/okta.go b/pkg/connector/provider/okta.go new file mode 100644 index 000000000..badeff689 --- /dev/null +++ b/pkg/connector/provider/okta.go @@ -0,0 +1,77 @@ +// Copyright (c) 2026 Probo Inc . +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +package provider + +import ( + "context" + "fmt" + "net/http" + + "go.gearno.de/kit/log" + "go.probo.inc/probo/pkg/accessreview/drivers" + "go.probo.inc/probo/pkg/connector" + "go.probo.inc/probo/pkg/coredata" +) + +// oktaRegistration wires the Okta access-review connector. Okta is a +// per-tenant IdP with no central API gateway, so a one-click OAuth flow is not +// possible — it authenticates with a read-only API token presented under the +// `SSWS` Authorization scheme (APIKeyAuthScheme), plus the customer's org +// domain. The token + domain identify exactly one org, so there is no picker +// and no OAuth metadata. ProbeURL is empty because the API host is per-org and +// there is no static URL to probe; a dead token surfaces on the first +// ListAccounts. +func oktaRegistration() *Registration { + return &Registration{ + Provider: coredata.ConnectorProviderOkta, + DisplayName: "Okta", + SupportsAPIKey: true, + APIKeyAuthScheme: "SSWS", + ExtraSettings: []ExtraSetting{ + {Key: "domain", Label: "Okta Domain", Required: true}, + }, + NewDriver: func(_ context.Context, c *http.Client, conn *coredata.Connector, _ *log.Logger) (drivers.Driver, error) { + s, err := coredata.ConnectorSettings[coredata.OktaConnectorSettings](conn) + if err != nil { + return nil, fmt.Errorf("cannot read okta connector settings: %w", err) + } + + // Re-validate the stored domain at the construction site + // (defense-in-depth): the create-connector resolver validates on + // write, but pinning the host invariant here keeps the driver safe + // regardless of how the connector row was populated. An empty + // domain also fails this check. + if !connector.IsValidOktaDomain(s.Domain) { + return nil, fmt.Errorf("cannot create okta driver: invalid or missing domain") + } + + return drivers.NewOktaDriver(c, s.Domain), nil + }, + NewNameResolver: func(ctx context.Context, c *http.Client, conn *coredata.Connector, logger *log.Logger) drivers.NameResolver { + s, err := coredata.ConnectorSettings[coredata.OktaConnectorSettings](conn) + if err != nil { + logger.ErrorCtx(ctx, "cannot read okta connector settings", log.Error(err)) + return nil + } + + if !connector.IsValidOktaDomain(s.Domain) { + logger.ErrorCtx(ctx, "invalid okta domain in connector settings") + return nil + } + + return drivers.NewOktaNameResolver(c, s.Domain) + }, + } +} diff --git a/pkg/server/api/console/v1/connector_resolvers.go b/pkg/server/api/console/v1/connector_resolvers.go index b7e7d28c9..44af0f2eb 100644 --- a/pkg/server/api/console/v1/connector_resolvers.go +++ b/pkg/server/api/console/v1/connector_resolvers.go @@ -44,6 +44,7 @@ func (r *mutationResolver) CreateAPIKeyConnector(ctx context.Context, input type APIKey: input.APIKey, Header: r.providerRegistry.APIKeyHeader(input.Provider), BasicAuth: r.providerRegistry.APIKeyUsesBasicAuth(input.Provider), + Scheme: r.providerRegistry.APIKeyAuthScheme(input.Provider), }, } diff --git a/pkg/server/api/console/v1/connector_settings.go b/pkg/server/api/console/v1/connector_settings.go index e32b5fa57..a7fe21ed7 100644 --- a/pkg/server/api/console/v1/connector_settings.go +++ b/pkg/server/api/console/v1/connector_settings.go @@ -20,6 +20,7 @@ import ( "net/url" "go.probo.inc/probo/pkg/accessreview/drivers" + "go.probo.inc/probo/pkg/connector" "go.probo.inc/probo/pkg/coredata" "go.probo.inc/probo/pkg/server/api/console/v1/types" ) @@ -127,6 +128,21 @@ func apiKeyConnectorSettings(input types.CreateAPIKeyConnectorInput) (json.RawMe default: return nil, fmt.Errorf("cannot create posthog connector: posthogRegion or posthogInstanceUrl is required") } + case coredata.ConnectorProviderOkta: + if input.OktaDomain == nil || *input.OktaDomain == "" { + return nil, fmt.Errorf("cannot create okta connector: oktaDomain is required") + } + + // NormalizeOktaDomain strips scheme/path and validates the host; the + // stored value is the bare org domain (e.g. "acme.okta.com"). Use a + // static error message — this string is surfaced verbatim to the + // client and must never echo the operator-supplied input. + domain, err := connector.NormalizeOktaDomain(*input.OktaDomain) + if err != nil { + return nil, fmt.Errorf("cannot create okta connector: oktaDomain must be a valid Okta domain (e.g. acme.okta.com)") + } + + return json.Marshal(&coredata.OktaConnectorSettings{Domain: domain}) } return nil, nil