Register Okta connector and wire API-key settings

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>
This commit is contained in:
Aurélien Sibiril
2026-06-04 14:45:08 +02:00
parent 06603372d7
commit 03827704ec
4 changed files with 95 additions and 0 deletions

View File

@@ -45,6 +45,7 @@ func NewBuiltinRegistry() *Registry {
mondayRegistration(),
netlifyRegistration(),
notionRegistration(),
oktaRegistration(),
onePasswordRegistration(),
openaiRegistration(),
posthogRegistration(),

View File

@@ -0,0 +1,77 @@
// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
//
// 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)
},
}
}

View File

@@ -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),
},
}

View File

@@ -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