safeguard

package module
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2026 License: Apache-2.0 Imports: 37 Imported by: 0

README

Go Reference Release License

safeguard-go

One Identity Safeguard SDK for Go.

safeguard-go is the standalone Go SDK for One Identity Safeguard for Privileged Passwords (SPP). It mirrors the SafeguardDotNet and PySafeguard SDKs with a Go-idiomatic API surface for authentication, Invoke-style API calls, Application-to-Application (A2A) workflows, and events.

The module is pre-1.0; the public API may change before the first v1.0.0 release. See docs/versioning.md for the versioning and release policy.

Features

  • Authentication — Resource Owner Grant username/password, certificate login over mutual TLS, PKCE headless (with MFA/secondary-factor support), existing user token, and anonymous access. Interactive external-browser and device-code logins ship as optional add-on packages so headless consumers keep a lean dependency graph.
  • Invoke and Service surfaceConnect, Get/Post/Put/Delete helpers, generic Invoke, typed InvokeTyped[T], query parameters, headers, per-request API-version and host overrides, Response, and Stream/Upload/Download for large payloads.
  • Application-to-Application (A2A) — password, SSH private-key, and API-key secret retrieval; password and private-key set (write-back); access-request brokering; retrievable-account discovery; and credential-change events.
  • Events — an owned SignalR-over-WebSocket implementation with one-shot and persistent (auto-reconnecting) listeners for both user sessions and A2A credential-change streams.
  • TLS secure by default — HTTPS-only appliance endpoints (a non-https host is rejected), system trust by default, custom CA bundles that replace the system trust store, an additive server-certificate validator callback, and a loud insecure override for bootstrap/test appliances.
  • Typed errors — API, authentication (401), authorization (403), not-found (404), transport, and sentinel errors designed for errors.Is/errors.As.
  • Secret hygiene — the Secret type wraps sensitive values, redacts them from strings/logs/errors/JSON, copies on construction and exposure, and best-effort zeroes on close.

The only third-party dependency is github.com/coder/websocket (used by the event listeners). The browser and devicecode add-ons are separate packages, so importing the root package alone stays dependency-light.

Installation

go get github.com/OneIdentity/safeguard-go

The SDK requires Go 1.21 or later.

Quick start

package main

import (
    "context"
    "log"

    "github.com/OneIdentity/safeguard-go"
)

func main() {
    ctx := context.Background()

    cred := safeguard.UsernamePassword(
        "local",
        "Admin",
        safeguard.NewSecretString("correct horse battery staple"),
    )

    client, err := safeguard.Connect(ctx, "safeguard.sample.corp", cred)
    if err != nil {
        log.Fatal(err)
    }
    defer client.Close()

    resp, err := client.Get(ctx, safeguard.Core, "Me")
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("GET Me -> %d\n%s\n", resp.StatusCode, resp.Body)
}

Recent Safeguard appliances commonly disable Resource Owner Grant (ROG). New applications should prefer PKCE, browser, device-code, certificate, or token-based flows as appropriate. PKCEHeadless is the recommended flow for automation because it does not depend on ROG.

To decode a JSON response into a Go value, use the generic helper:

type me struct {
    UserName    string `json:"UserName"`
    DisplayName string `json:"DisplayName"`
}

info, err := safeguard.InvokeTyped[me](ctx, client, safeguard.MethodGet, safeguard.Core, "Me", nil)
if err != nil {
    log.Fatal(err)
}
log.Printf("signed in as %s (%s)", info.UserName, info.DisplayName)

Samples

The samples directory has a small, standalone program for each flow — every authentication method, the A2A retrieval/set/broker workflows, and the event listeners. Each sample takes its inputs as flags; run one with, for example:

go run ./samples/pkce -appliance safeguard.example.com -username Admin -insecure

See samples/README.md for the full list and the shared flags.

API version

The SDK targets Safeguard API v4 by default for Safeguard 7.0 and later. Override it per client with WithAPIVersion or per request with WithAPIVersionOverride (for example "v3" for legacy compatibility); the SDK does not auto-detect the API version.

Support

One Identity open source projects are supported through GitHub issues and the One Identity Community. Open an issue in this repository to report a bug or request a feature.

License

Apache License 2.0. See LICENSE and NOTICE.

Documentation

Overview

Package safeguard is the One Identity Safeguard SDK for Go.

It provides a Go-idiomatic client for the Safeguard for Privileged Passwords Web API: authentication, Invoke-style API calls, Application-to-Application (A2A) credential retrieval, and event listeners.

Authentication

Connect establishes an authenticated Client from a Credential. Build a credential with UsernamePassword, Certificate, PKCEHeadless, Token, or Anonymous. PKCEHeadless is the recommended flow for automation because it does not depend on the Resource Owner Grant, which appliances commonly disable. Interactive browser and device-code logins live in the browser and devicecode subpackages so headless consumers keep a lean dependency graph. A client with no credential starts in an anonymous session, sufficient for the Notification service.

Making requests

Client.Invoke issues a call against a Service and returns a Response; Client.Get, Client.Post, Client.Put, and Client.Delete are shorthands. InvokeTyped decodes a JSON response straight into a Go value. Client.Stream, Client.Download, and Client.Upload move large payloads without buffering. Per-request behavior is set with ReqOption values such as WithQueryParam, WithHeader, WithAccept, and WithRequestTimeout.

Transport and authorization

A call's TLS identity and its authorization are chosen independently of the service: a Service only selects the base URL path and never implies which credential authorizes the request. Authorization is owned by the transport, so the reserved Authorization header cannot be set on a request; it is rejected with ErrReservedHeader.

Secrets

Secret holds sensitive bytes -- passwords, tokens, API keys, retrieved credentials -- and makes reading them a deliberate act: it redacts itself in fmt, JSON, and structured logs, so a credential is not disclosed by accident. Obtain the bytes only through Secret.Expose or Secret.ExposeString.

TLS trust

TLS verification is on by default, and appliance hosts must use https (a non-https host is rejected). Trust a privately issued appliance certificate with WithCABundle, which replaces the system trust store for server verification. WithInsecureTLS disables verification and exists only for bootstrapping development appliances; it must never be used in production.

A2A and events

NewA2AContext builds an A2AContext that retrieves credentials over the A2A service using a client certificate and per-account API keys, with no user session. Client.NewEventListener and Client.NewPersistentEventListener (and their A2AContext counterparts) deliver Safeguard events over a SignalR-over-WebSocket connection.

Example

Example shows the canonical flow: build a credential, Connect, make a call, and Close. The client owns an in-memory token that Close releases.

package main

import (
	"context"
	"fmt"
	"log"

	safeguard "github.com/OneIdentity/safeguard-go"
)

func main() {
	ctx := context.Background()

	password := safeguard.NewSecretString("correct horse battery staple")
	defer password.Zero()

	client, err := safeguard.Connect(ctx, "safeguard.example.com",
		safeguard.UsernamePassword("local", "Admin", password))
	if err != nil {
		log.Fatal(err)
	}
	defer func() { _ = client.Close() }()

	resp, err := client.Get(ctx, safeguard.Core, "Me")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("GET Me -> %d\n", resp.StatusCode)
}

Index

Examples

Constants

View Source
const DefaultAPIVersion = "v4"

DefaultAPIVersion is the Safeguard API version used when a caller does not override it. Safeguard's current default major version is v4.

Variables

View Source
var (
	// ErrNotAuthenticated indicates an operation requires a session that has not
	// been established (or has been ended by Logout/Close).
	ErrNotAuthenticated = errors.New("safeguard: not authenticated")
	// ErrNotRefreshable indicates the current credential cannot be refreshed (for
	// example a bare user token supplied via Token).
	ErrNotRefreshable = errors.New("safeguard: token is not refreshable")
	// ErrReservedHeader indicates a request option attempted to set a reserved
	// header (Authorization) that the transport controls exclusively.
	ErrReservedHeader = errors.New("safeguard: Authorization is a reserved header and cannot be set on a request")
	// ErrClosed indicates the client has been closed and can no longer be used.
	ErrClosed = errors.New("safeguard: client is closed")
	// ErrAlreadyStarted indicates an event listener's Start was called more than
	// once. Create a new listener instead of restarting a stopped one.
	ErrAlreadyStarted = errors.New("safeguard: event listener already started")
)

Sentinel errors returned by the SDK. Compare with errors.Is.

View Source
var ErrSecondaryFactorFailed = auth.ErrSecondaryFactorFailed

ErrSecondaryFactorFailed indicates the appliance rejected the supplied secondary (multi-factor) authentication code. Compare with errors.Is.

View Source
var ErrSecondaryFactorRequired = auth.ErrSecondaryFactorRequired

ErrSecondaryFactorRequired indicates a PKCE headless login reached a secondary (multi-factor) authentication step but no secondary factor provider was supplied. Provide WithSecondaryFactor. Compare with errors.Is.

Functions

func InvokeTyped

func InvokeTyped[T any](ctx context.Context, c *Client, m HTTPMethod, s Service, relURL string, body any, opts ...ReqOption) (T, error)

InvokeTyped performs an Invoke and JSON-decodes a successful response body into a value of type T. An empty body yields the zero value of T with no error.

Example

ExampleInvokeTyped decodes a successful JSON response directly into a Go value.

package main

import (
	"context"
	"fmt"
	"log"

	safeguard "github.com/OneIdentity/safeguard-go"
)

func main() {
	var client *safeguard.Client // obtained from safeguard.Connect
	ctx := context.Background()

	type me struct {
		UserName    string `json:"UserName"`
		DisplayName string `json:"DisplayName"`
	}

	info, err := safeguard.InvokeTyped[me](ctx, client, safeguard.MethodGet, safeguard.Core, "Me", nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("signed in as %s (%s)\n", info.UserName, info.DisplayName)
}

Types

type A2AContext

type A2AContext struct {
	// contains filtered or unexported fields
}

A2AContext retrieves credentials with the Safeguard Application-to-Application (A2A) service. Unlike Client, it is not a token session: it authenticates with a client certificate over mutual TLS on every call, and each retrieval is authorized by a per-account API key passed as an argument. The API key identifies which registered account the call targets; the certificate proves the calling application's identity.

A2AContext is created with NewA2AContext, which parses the certificate in memory without contacting the appliance; networked and certificate-dependent state is validated on the first retrieval. It is safe for concurrent use, and its transports must be released with Close.

func NewA2AContext

func NewA2AContext(host string, certPEM []byte, password Secret, opts ...A2AOption) (*A2AContext, error)

NewA2AContext builds an A2AContext for host that authenticates with the client certificate in certPEM over mutual TLS. certPEM is concatenated PEM carrying the leaf certificate, any intermediate chain, and the private key; supply the key separately with WithA2APrivateKeyPEM when it lives in its own PEM input. password decrypts an encrypted PEM private key, whether it uses the modern encrypted PKCS#8 (PBES2) format or the legacy DEK-Info format. Like the Certificate credential, only PEM material is accepted: PKCS#12 (.pfx/.p12) input is rejected with a clear error, so convert it first (for example, `openssl pkcs12 -in cert.pfx -nodes -out cert.pem`).

The certificate is parsed and validated here, so a bad certificate or password surfaces immediately; no network call is made until the first retrieval.

func (*A2AContext) APIVersion

func (a *A2AContext) APIVersion() string

APIVersion returns the default API version the context uses.

func (*A2AContext) BrokerAccessRequest

func (a *A2AContext) BrokerAccessRequest(ctx context.Context, brokerAPIKey Secret, req BrokeredAccessRequest) (*AccessRequest, error)

BrokerAccessRequest creates an access request on behalf of another user over the A2A service, authorized by brokerAPIKey -- the API key of the registration's access request broker, which is distinct from an account's retrieval API key. The registration must list the context's certificate user among its broker users, and an access policy must grant the requested user access to the target, or the appliance rejects the call. The returned AccessRequest reports the request's identifier and state.

func (*A2AContext) Close

func (a *A2AContext) Close() error

Close is terminal: it releases the transport pools. After Close the context cannot be used. Close is idempotent.

func (*A2AContext) GetRetrievableAccounts

func (a *A2AContext) GetRetrievableAccounts(ctx context.Context, filter string) ([]A2ARetrievableAccount, error)

GetRetrievableAccounts lists the accounts the context's client certificate is registered to retrieve credentials for, across every A2A registration bound to the certificate. Unlike the Retrieve methods it is authorized by the client certificate alone -- no per-account API key -- because it enumerates all registrations for the certificate user. The optional filter is an OData filter expression applied to each registration's accounts; pass "" for no filter.

The returned entries carry the per-account APIKey, so a caller can discover an account here and pass its APIKey straight to RetrievePassword, RetrievePrivateKey, or RetrieveAPIKey. Because an entry does not record which credential type it was registered for, a caller that registered an account for more than one type must track that mapping itself.

Example

ExampleA2AContext_GetRetrievableAccounts discovers every account the context's client certificate is registered to retrieve, along with the API key that authorizes each retrieval.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	safeguard "github.com/OneIdentity/safeguard-go"
)

func main() {
	ctx := context.Background()

	certPEM, err := os.ReadFile("a2a-client.pem")
	if err != nil {
		log.Fatal(err)
	}
	a2a, err := safeguard.NewA2AContext("safeguard.example.com", certPEM, safeguard.Secret{})
	if err != nil {
		log.Fatal(err)
	}
	defer func() { _ = a2a.Close() }()

	accounts, err := a2a.GetRetrievableAccounts(ctx, "")
	if err != nil {
		log.Fatal(err)
	}
	for _, acct := range accounts {
		fmt.Printf("%s on %s\n", acct.AccountName, acct.AssetName)
		// acct.APIKey can be passed straight to RetrievePassword and friends.
	}
}

func (*A2AContext) Host

func (a *A2AContext) Host() string

Host returns the appliance host the context is bound to.

func (*A2AContext) NewEventListener

func (a *A2AContext) NewEventListener(apiKey Secret) *EventListener

NewEventListener returns a single-connection listener for A2A credential events authorized by apiKey. Events are delivered for the account the API key retrieves. Register handlers, then call Start.

func (*A2AContext) NewPersistentEventListener

func (a *A2AContext) NewPersistentEventListener(apiKey Secret) *PersistentEventListener

NewPersistentEventListener returns a reconnecting listener for A2A credential events authorized by apiKey. An A2A API key does not expire, so the listener simply reconnects after a dropped connection until Stop.

func (*A2AContext) RetrieveAPIKey

func (a *A2AContext) RetrieveAPIKey(ctx context.Context, apiKey Secret) ([]APIKey, error)

RetrieveAPIKey retrieves the API key credentials of the account identified by apiKey. The appliance returns one entry per configured API key.

func (*A2AContext) RetrievePassword

func (a *A2AContext) RetrievePassword(ctx context.Context, apiKey Secret) (Secret, error)

RetrievePassword retrieves the password of the account identified by apiKey. The returned Secret holds the password; it is empty when the account has no stored password.

Example

ExampleA2AContext_RetrievePassword retrieves an account password over the Application-to-Application service. An A2AContext authenticates with a client certificate on every call; the per-account API key selects which registered account the call targets.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	safeguard "github.com/OneIdentity/safeguard-go"
)

func main() {
	ctx := context.Background()

	certPEM, err := os.ReadFile("a2a-client.pem")
	if err != nil {
		log.Fatal(err)
	}
	a2a, err := safeguard.NewA2AContext("safeguard.example.com", certPEM, safeguard.Secret{})
	if err != nil {
		log.Fatal(err)
	}
	defer func() { _ = a2a.Close() }()

	apiKey := safeguard.NewSecretString(os.Getenv("A2A_API_KEY"))
	defer apiKey.Zero()

	password, err := a2a.RetrievePassword(ctx, apiKey)
	if err != nil {
		log.Fatal(err)
	}
	defer password.Zero()

	// Use password.Expose()/ExposeString() where the plaintext is required; do
	// not log it. Here we only report that a value was retrieved.
	fmt.Printf("retrieved a %d-byte password\n", password.Len())
}

func (*A2AContext) RetrievePrivateKey

func (a *A2AContext) RetrievePrivateKey(ctx context.Context, apiKey Secret, format KeyFormat) (Secret, error)

RetrievePrivateKey retrieves the SSH private key of the account identified by apiKey, encoded in the requested KeyFormat. An empty format selects KeyFormatOpenSSH. The returned Secret holds the private key.

func (*A2AContext) SetPassword

func (a *A2AContext) SetPassword(ctx context.Context, apiKey Secret, newPassword Secret) error

SetPassword stores newPassword as the password of the account identified by apiKey, writing it back to Safeguard over the A2A service. The account's A2A registration must have bidirectional (write-back) access enabled, or the appliance rejects the call.

func (*A2AContext) SetPrivateKey

func (a *A2AContext) SetPrivateKey(ctx context.Context, apiKey Secret, privateKeyPEM Secret, passphrase Secret, format KeyFormat) error

SetPrivateKey stores privateKeyPEM as the SSH private key of the account identified by apiKey. privateKeyPEM is the private key in PEM form and format declares its encoding; an empty format selects KeyFormatOpenSSH. passphrase decrypts an encrypted key and may be an empty Secret for an unencrypted key. Like SetPassword, the account's registration must have bidirectional access enabled.

type A2AOption

type A2AOption func(*a2aConfig) error

A2AOption configures an A2AContext. Options are applied in order and may return an error to reject an invalid configuration.

func WithA2AConnectionOptions

func WithA2AConnectionOptions(opts ...Option) A2AOption

WithA2AConnectionOptions applies standard connection Options (TLS trust, API version, timeouts, logger) to the A2A context. It reuses the same Option set as Connect so A2A callers configure their connection exactly like everyone else, for example WithCABundle, WithInsecureTLS, or WithAPIVersion.

func WithA2APrivateKeyPEM

func WithA2APrivateKeyPEM(keyPEM []byte) A2AOption

WithA2APrivateKeyPEM supplies the client certificate's private key as a separate PEM input when it is not concatenated with the certificate in the primary NewA2AContext argument, mirroring Certificate's WithPrivateKeyPEM.

type A2ARetrievableAccount

type A2ARetrievableAccount struct {
	// ApplicationName is the AppName of the registration this account belongs to.
	ApplicationName string
	// Description is the registration's description.
	Description string
	// Disabled reports whether the account or its registration is disabled.
	Disabled bool
	// APIKey is the A2A API key that authorizes retrieval of this account's
	// credential. Pass it to a Retrieve method.
	APIKey Secret
	// AssetID is the object ID of the account's asset.
	AssetID int
	// AssetName is the name of the account's asset.
	AssetName string
	// AssetNetworkAddress is the network address of the account's asset.
	AssetNetworkAddress string
	// AssetDescription is the description of the account's asset.
	AssetDescription string
	// AccountID is the account's object ID.
	AccountID int
	// AccountName is the account's name.
	AccountName string
	// DomainName is the account's domain, when it is a directory account.
	DomainName string
	// AccountType identifies the kind of account.
	AccountType string
	// AccountDescription is the account's description.
	AccountDescription string
}

A2ARetrievableAccount describes one account that the context's client certificate is registered to retrieve credentials for. The APIKey authorizes retrieval of that account's credential and is wrapped in a Secret; the surrounding metadata is not sensitive. The same account may appear more than once -- once per credential type it is registered for -- each with a distinct APIKey; the response does not record which credential type an entry is for.

type APIError

type APIError struct {
	// StatusCode is the HTTP status code returned by the appliance.
	StatusCode int
	// Code is the Safeguard error code from the response body, or 0 if absent.
	Code int
	// Message is the Safeguard error message from the response body, if any.
	Message string
	// RequestID is the appliance request/correlation id, or empty if absent.
	RequestID string
	// contains filtered or unexported fields
}

APIError is returned when a Safeguard API call completes with a non-2xx HTTP status. It carries the HTTP status, the Safeguard error Code and Message when the body was a recognizable Safeguard error object, and the appliance request identifier when present.

Error never includes the raw response body, which may contain returned credentials; use RawBody for a bounded, explicit diagnostic copy.

func (*APIError) Error

func (e *APIError) Error() string

Error implements error. It deliberately omits the raw response body.

func (*APIError) RawBody

func (e *APIError) RawBody() []byte

RawBody returns a copy of the bounded raw response body retained for diagnostics. It may contain sensitive data and is never logged automatically.

type APIKey

type APIKey struct {
	// ID is the API key's object identifier.
	ID int
	// Name is the API key's name.
	Name string
	// Description is the API key's description.
	Description string
	// ClientID is the OAuth client identifier.
	ClientID string
	// ClientSecret is the OAuth client secret. It is empty when the appliance
	// does not return the secret value for this key.
	ClientSecret Secret
	// ClientSecretID identifies the client secret.
	ClientSecretID string
}

APIKey is one API key credential retrieved for a registered account. The ClientSecret is wrapped in a Secret; the surrounding metadata is not sensitive.

type AccessRequest

type AccessRequest struct {
	// ID is the access request's identifier.
	ID string
	// State is the request's workflow state, for example "RequestAvailable" once
	// an auto-approved request is ready or "PendingApproval" when it awaits an
	// approver.
	State string
	// AccessRequestType is the kind of access that was requested.
	AccessRequestType AccessRequestType
	// AccountID is the target account's object ID.
	AccountID int
	// AccountName is the target account's name.
	AccountName string
	// AssetID is the target asset's object ID.
	AssetID int
	// AssetName is the target asset's name.
	AssetName string
	// Raw is the unmodified JSON body the appliance returned.
	Raw json.RawMessage
}

AccessRequest is the access request the broker created. It exposes the fields callers most commonly need; Raw holds the complete appliance response for anything not modeled here.

type AccessRequestType

type AccessRequestType string

AccessRequestType identifies the kind of access a brokered access request asks for. The values are the names the Safeguard API uses on the wire.

const (
	// AccessRequestPassword requests release of an account password.
	AccessRequestPassword AccessRequestType = "Password"
	// AccessRequestSSHKey requests release of an account SSH key.
	AccessRequestSSHKey AccessRequestType = "SshKey"
	// AccessRequestSSH requests an SSH session.
	AccessRequestSSH AccessRequestType = "Ssh"
	// AccessRequestRemoteDesktop requests a remote desktop (RDP) session.
	AccessRequestRemoteDesktop AccessRequestType = "RemoteDesktop"
	// AccessRequestRemoteDesktopApplication requests a remote desktop application session.
	AccessRequestRemoteDesktopApplication AccessRequestType = "RemoteDesktopApplication"
	// AccessRequestTelnet requests a Telnet session.
	AccessRequestTelnet AccessRequestType = "Telnet"
	// AccessRequestAPIKey requests release of an account API key.
	AccessRequestAPIKey AccessRequestType = "ApiKey"
	// AccessRequestFile requests release of a stored file.
	AccessRequestFile AccessRequestType = "File"
)

type AuthenticationError

type AuthenticationError struct{ *APIError }

AuthenticationError is an APIError for an HTTP 401 response. Detect it with errors.As; errors.As(&APIError) also matches via Unwrap.

func (*AuthenticationError) Unwrap

func (e *AuthenticationError) Unwrap() error

Unwrap returns the embedded *APIError.

type AuthorizationError

type AuthorizationError struct{ *APIError }

AuthorizationError is an APIError for an HTTP 403 response.

func (*AuthorizationError) Unwrap

func (e *AuthorizationError) Unwrap() error

Unwrap returns the embedded *APIError.

type BrokeredAccessRequest

type BrokeredAccessRequest struct {
	// AccessRequestType is the kind of access requested; it is required.
	AccessRequestType AccessRequestType
	// ForUserID identifies the user the request is made for by object ID. It
	// takes precedence over ForUser.
	ForUserID int
	// ForUser identifies the user the request is made for by name. It is ignored
	// when ForUserID is set.
	ForUser string
	// ForProvider names the identity provider that resolves ForUser, for example
	// "local"; it is ignored when ForUserID is set.
	ForProvider string
	// AssetID identifies the target asset by object ID. It takes precedence over
	// AssetName.
	AssetID int
	// AssetName identifies the target asset by name. It is ignored when AssetID
	// is set.
	AssetName string
	// AccountID identifies the target account by object ID. It takes precedence
	// over AccountName.
	AccountID int
	// AccountName identifies the target account by name; omit it to request
	// access to the asset itself. It is ignored when AccountID is set.
	AccountName string
	// AccountDomainName disambiguates AccountName when the account is directory
	// managed. It is ignored when AccountID is set.
	AccountDomainName string
	// IsEmergency marks the request as an emergency access request.
	IsEmergency bool
	// ReasonCodeID selects a predefined reason code by object ID.
	ReasonCodeID int
	// ReasonCode selects a predefined reason code by name; it is ignored when
	// ReasonCodeID is set.
	ReasonCode string
	// ReasonComment is a free-text justification for the request.
	ReasonComment string
	// TicketNumber associates the request with an external ticket.
	TicketNumber string
	// RequestedFor is when the access should begin; a zero time requests access
	// immediately.
	RequestedFor time.Time
	// RequestedDurationDays, RequestedDurationHours, and RequestedDurationMinutes
	// set how long the access should last; leave them zero for the policy
	// default.
	RequestedDurationDays    int
	RequestedDurationHours   int
	RequestedDurationMinutes int
}

BrokeredAccessRequest describes an access request the broker creates on behalf of another user. Identify the account by AccountID (or AccountName, optionally with AccountDomainName) and the asset by AssetID (or AssetName). Identify the user the request is for by ForUserID, or by ForUser with an optional ForProvider to disambiguate the identity provider. Zero-valued fields are omitted from the request.

type CertOption

type CertOption func(*certConfig) error

CertOption configures certificate login.

func WithCertificateProvider

func WithCertificateProvider(provider string) CertOption

WithCertificateProvider overrides the authentication provider used for certificate login. The default is the built-in certificate provider.

func WithPrivateKeyPEM

func WithPrivateKeyPEM(keyPEM []byte) CertOption

WithPrivateKeyPEM supplies the private key as a separate PEM input when it is not concatenated with the certificate in the primary Certificate argument.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a concurrency-safe Safeguard API client. It is safe for use by multiple goroutines: token state is immutable and swapped atomically, and the transport pools are internally synchronized.

A freshly built client starts in an anonymous session, which is sufficient for the Notification service and other anonymous endpoints; Connect establishes an authenticated session from a credential.

func Connect

func Connect(ctx context.Context, host string, cred Credential, opts ...Option) (*Client, error)

Connect authenticates to the Safeguard appliance at host using cred and returns a ready client bound to a fresh session. Connection options (TLS trust, API version, timeouts, logger) are applied before authentication runs, so certificate parsing and the login exchange happen under the caller's TLS policy. On any failure Connect releases the client it built and returns the error; on success the returned client owns transports and an in-memory token that must be released with Close.

Connect is the only way to obtain an authenticated client. Reconnecting means calling Connect again for a new client with a new session epoch; there is no in-place reconnect.

Example (Certificate)

ExampleConnect_certificate authenticates with a client certificate over mutual TLS. The PEM material carries the leaf certificate, any chain, and the private key; password decrypts an encrypted key and may be empty otherwise.

package main

import (
	"context"
	"log"
	"os"

	safeguard "github.com/OneIdentity/safeguard-go"
)

func main() {
	ctx := context.Background()

	certPEM, err := os.ReadFile("client.pem")
	if err != nil {
		log.Fatal(err)
	}
	keyPassword := safeguard.NewSecretString("key-passphrase")
	defer keyPassword.Zero()

	client, err := safeguard.Connect(ctx, "safeguard.example.com",
		safeguard.Certificate(certPEM, keyPassword))
	if err != nil {
		log.Fatal(err)
	}
	defer func() { _ = client.Close() }()
}
Example (Pkce)

ExampleConnect_pkce authenticates with the PKCE non-interactive ("headless") flow, which does not depend on the Resource Owner Grant and is the recommended flow for automation.

package main

import (
	"context"
	"log"

	safeguard "github.com/OneIdentity/safeguard-go"
)

func main() {
	ctx := context.Background()

	password := safeguard.NewSecretString("correct horse battery staple")
	defer password.Zero()

	client, err := safeguard.Connect(ctx, "safeguard.example.com",
		safeguard.PKCEHeadless("local", "Admin", password))
	if err != nil {
		log.Fatal(err)
	}
	defer func() { _ = client.Close() }()
}
Example (Token)

ExampleConnect_token reuses an existing Safeguard user token. Such a session is not refreshable, so a 401 is surfaced rather than silently retried.

package main

import (
	"context"
	"log"
	"os"

	safeguard "github.com/OneIdentity/safeguard-go"
)

func main() {
	ctx := context.Background()

	userToken := safeguard.NewSecretString(os.Getenv("SAFEGUARD_TOKEN"))
	defer userToken.Zero()

	client, err := safeguard.Connect(ctx, "safeguard.example.com",
		safeguard.Token(userToken))
	if err != nil {
		log.Fatal(err)
	}
	defer func() { _ = client.Close() }()
}

func (*Client) APIVersion

func (c *Client) APIVersion() string

APIVersion returns the default API version the client uses.

func (*Client) Close

func (c *Client) Close() error

Close is terminal: it releases the transport pools and clears the in-memory session. Close installs a terminal epoch so an in-flight refresh cannot resurrect the session (doRefresh publishes only on the observed epoch, and epoch 0 never matches). The displaced token is released to the garbage collector rather than zeroed in place, because a concurrent request may still be reading its bytes and zeroing an aliased backing array under a reader is a data race. After Close the client cannot be used. Close is idempotent.

func (*Client) Delete

func (c *Client) Delete(ctx context.Context, s Service, relURL string, opts ...ReqOption) (Response, error)

Delete performs a DELETE request.

func (*Client) Download

func (c *Client) Download(ctx context.Context, s Service, relURL string, w io.Writer, opts ...ReqOption) (Response, error)

Download performs a GET and streams the response body to w, returning a Response that carries the status, headers, and request id but a nil Body (the payload went to w). On a non-2xx status the bounded error payload is read into Response.Body and returned with a typed *APIError. The default Accept is application/octet-stream; override it with WithAccept.

Example

ExampleClient_Download streams a response body to an io.Writer without buffering it in memory, which suits large payloads such as backups or reports.

package main

import (
	"context"
	"log"
	"os"

	safeguard "github.com/OneIdentity/safeguard-go"
)

func main() {
	var client *safeguard.Client // obtained from safeguard.Connect
	ctx := context.Background()

	f, err := os.Create("backup.sgb")
	if err != nil {
		log.Fatal(err)
	}
	defer func() { _ = f.Close() }()

	if _, err := client.Download(ctx, safeguard.Appliance, "Backups/current/Download", f); err != nil {
		log.Fatal(err)
	}
}

func (*Client) Get

func (c *Client) Get(ctx context.Context, s Service, relURL string, opts ...ReqOption) (Response, error)

Get performs a GET request.

Example

ExampleClient_Get retrieves a resource and reads the raw response body. Get is shorthand for Invoke with the GET method and no request body.

package main

import (
	"context"
	"fmt"
	"log"

	safeguard "github.com/OneIdentity/safeguard-go"
)

func main() {
	var client *safeguard.Client // obtained from safeguard.Connect
	ctx := context.Background()

	resp, err := client.Get(ctx, safeguard.Core, "Users",
		safeguard.WithQueryParam("filter", "Disabled eq false"))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%d: %s\n", resp.StatusCode, resp.Body)
}

func (*Client) Host

func (c *Client) Host() string

Host returns the appliance host the client is bound to.

func (*Client) Invoke

func (c *Client) Invoke(ctx context.Context, m HTTPMethod, s Service, relURL string, body any, opts ...ReqOption) (Response, error)

Invoke performs a Safeguard API call and returns the Response. The body is encoded by type: nil is an empty body; string and json.RawMessage are sent as application/json; []byte and io.Reader are sent as application/octet-stream; any other value is JSON-marshaled. A caller may override the content type with WithHeader. On a non-2xx status Invoke returns the populated Response along with a typed *APIError (or its 401/403/404 specializations). The response body is always read and closed.

func (*Client) Logout

func (c *Client) Logout(ctx context.Context) error

Logout ends this client's session. It makes a best-effort appliance-side Token/Logout call to revoke the user token, then clears the local session and invalidates its epoch so an in-flight refresh cannot resurrect it. A failure of the appliance call is ignored: the local session is cleared regardless. Logout is idempotent and is a no-op for an anonymous session.

func (*Client) NewEventListener

func (c *Client) NewEventListener() *EventListener

NewEventListener returns a single-connection listener for this client's user events. Register handlers, then call Start. The listener stops if the connection ends; use NewPersistentEventListener for automatic reconnect.

func (*Client) NewPersistentEventListener

func (c *Client) NewPersistentEventListener() *PersistentEventListener

NewPersistentEventListener returns a reconnecting listener for this client's user events. It stops permanently if the client logs out or re-authenticates as a different identity.

Example

ExampleClient_NewPersistentEventListener subscribes to Safeguard events and keeps the subscription alive across reconnects until the listener is stopped.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"

	safeguard "github.com/OneIdentity/safeguard-go"
)

func main() {
	var client *safeguard.Client // obtained from safeguard.Connect
	ctx := context.Background()

	listener := client.NewPersistentEventListener()
	listener.RegisterEventHandler("AssetAccountPasswordUpdated", func(name string, data json.RawMessage) {
		fmt.Printf("event %s: %s\n", name, data)
	})

	if err := listener.Start(ctx); err != nil {
		log.Fatal(err)
	}
	defer listener.Stop()

	// Block until the listener stops (context cancelled, or a terminal error).
	<-listener.Done()
	if err := listener.Err(); err != nil {
		log.Printf("listener stopped: %v", err)
	}
}

func (*Client) Post

func (c *Client) Post(ctx context.Context, s Service, relURL string, body any, opts ...ReqOption) (Response, error)

Post performs a POST request with the given body.

func (*Client) Put

func (c *Client) Put(ctx context.Context, s Service, relURL string, body any, opts ...ReqOption) (Response, error)

Put performs a PUT request with the given body.

func (*Client) RefreshToken

func (c *Client) RefreshToken(ctx context.Context) error

RefreshToken exchanges the current credential for a fresh user token by re-running the full login exchange (single flight: concurrent callers share one refresh). It reports ErrNotAuthenticated for an anonymous or absent session and ErrNotRefreshable when the credential cannot mint a replacement token (a bare user token, or an OAuth authorization-code flow such as PKCE, browser, or device code).

func (*Client) Stream

func (c *Client) Stream(ctx context.Context, m HTTPMethod, s Service, relURL string, body any, opts ...ReqOption) (io.ReadCloser, Response, error)

Stream performs a request and returns the response body as an io.ReadCloser that the caller must Close. The response body is streamed, not buffered. If the appliance answers 401 before the body is delivered, Stream may refresh the token and replay the request once, but only when the request body is replayable (nil, a string, []byte, a json.RawMessage, or a marshaled value); a caller-supplied io.Reader body is consumed on first send and is never replayed. Once Stream returns a reader, that stream itself is never retried. On a non-2xx status Stream closes the body, returns a nil reader, and reports a typed *APIError along with a Response whose Body holds the (bounded) error payload. Unlike Invoke, Stream does not apply WithRequestTimeout; a streaming caller controls cancellation through ctx.

func (*Client) TokenLifetimeRemaining

func (c *Client) TokenLifetimeRemaining(ctx context.Context) (time.Duration, error)

TokenLifetimeRemaining reports the remaining lifetime of the current user token. The appliance is consulted once via the Core LoginMessage endpoint, whose X-TokenLifetimeRemaining header carries the remaining minutes. It returns ErrNotAuthenticated for an anonymous or absent session, and zero with no error when the lifetime cannot be determined.

func (*Client) Upload

func (c *Client) Upload(ctx context.Context, s Service, relURL string, r io.Reader, opts ...ReqOption) (Response, error)

Upload sends the contents of r as an application/octet-stream POST body without buffering it, and returns the (fully read) response. The content type may be overridden with WithHeader.

type Credential

type Credential interface {
	// contains filtered or unexported methods
}

Credential is a sealed Safeguard authentication strategy produced by one of this package's credential constructors: UsernamePassword, Certificate, PKCEHeadless, Token, Anonymous, or AuthorizedSession (the seam the browser and devicecode add-on packages use). Its only method is unexported, so the set of credentials is closed and callers cannot implement their own. Pass a Credential to Connect.

func Anonymous

func Anonymous() Credential

Anonymous returns a credential that establishes a session carrying no user token, sufficient for the Notification service and other anonymous endpoints.

func AuthorizedSession

func AuthorizedSession(login LoginFunc) Credential

AuthorizedSession returns a credential that completes an interactive OAuth login by running login and adopting the Safeguard user token it produces. It is the seam the optional browser and devicecode add-on packages use to hand a finished authorization back to Connect: because Credential is sealed, an add-on cannot implement its own credential, so it supplies its interactive flow as a LoginFunc and lets this credential install the result.

The resulting session is intentionally not refreshable, matching SafeguardDotNet and PySafeguard, which treat browser and device-code logins as existing-token connections that cannot silently re-authenticate: RefreshToken reports ErrNotRefreshable and a 401 is surfaced rather than retried. To obtain a new token, run the interactive flow again. For a bare, caller-supplied user token with no interactive step, use Token instead.

func Certificate

func Certificate(certPEM []byte, password Secret, opts ...CertOption) Credential

Certificate returns a credential that authenticates with a client certificate over mutual TLS. certPEM is a concatenated PEM byte slice carrying the leaf certificate, any intermediate chain, and the private key; supply the key separately with WithPrivateKeyPEM when it lives in its own PEM input. password decrypts an encrypted PEM private key, whether it uses the modern encrypted PKCS#8 (PBES2) format that current OpenSSL produces by default or the legacy DEK-Info format. Like PySafeguard, this SDK accepts PEM material only: PKCS#12 (.pfx/.p12) input is rejected with a clear error, so convert it first (for example, `openssl pkcs12 -in cert.pfx -nodes -out cert.pem`). The certificate material is parsed and validated at Connect time, so a bad certificate or password surfaces as a Connect error rather than a panic. The resulting session is refreshable.

func PKCEHeadless

func PKCEHeadless(provider, username string, password Secret, opts ...PKCEOption) Credential

PKCEHeadless returns a credential that authenticates with the PKCE non-interactive ("headless") OAuth flow: the SDK drives the appliance's RSTS form controller directly, with no browser. An empty provider selects the default local provider; a non-default provider may be given as its display name, its RSTS provider id, or a unique substring of that id, and is resolved against the appliance's authentication providers the same way SafeguardDotNet and safeguard-ps resolve it. The resulting session is intentionally not refreshable, matching the reference SDKs' treatment of OAuth authorization-code flows, so RefreshToken reports ErrNotRefreshable and a 401 is surfaced rather than silently retried. Supply WithSecondaryFactor to satisfy multi-factor authentication. The password is copied into the credential; the caller retains ownership of the supplied Secret.

PKCEHeadless is the recommended flow for test automation because it does not depend on the Resource Owner Grant, which appliances commonly disable.

func Token

func Token(userToken Secret) Credential

Token returns a credential that uses an existing Safeguard user token directly, skipping the RSTS/LoginResponse exchange. Such a session is intentionally not refreshable: the SDK has no credential with which to mint a replacement, so RefreshToken reports ErrNotRefreshable. The token is copied into the credential.

func UsernamePassword

func UsernamePassword(provider, username string, password Secret) Credential

UsernamePassword returns a credential that authenticates with the Resource Owner Grant (username and password). An empty provider selects the default local provider. The resulting session is refreshable. The password is copied into the credential; the caller retains ownership of the supplied Secret.

type EventHandlerFunc

type EventHandlerFunc func(name string, data json.RawMessage)

EventHandlerFunc receives an event it was registered for: name is the resolved event name (with the numeric-name workaround applied) and data is the raw JSON payload for the caller to decode. Handlers run on a dispatcher goroutine off the read loop; a panic in a handler is recovered and does not stop the listener. A handler should not block indefinitely, as a slow handler applies backpressure to later events.

type EventListener

type EventListener struct {
	// contains filtered or unexported fields
}

EventListener is a single-connection Safeguard event listener. It negotiates a SignalR-over-WebSocket connection, dispatches events to registered handlers, and stops when the connection ends. Register handlers before calling Start. EventListener is safe for concurrent handler registration before Start; use PersistentEventListener when automatic reconnect is required.

func (*EventListener) Done

func (l *EventListener) Done() <-chan struct{}

Done returns a channel closed when the listener has stopped, whether from Stop, a cancelled context, or a connection error. It returns nil before Start.

func (*EventListener) Err

func (l *EventListener) Err() error

Err returns the error that stopped the listener, or nil if it stopped cleanly or is still running.

func (*EventListener) RegisterEventHandler

func (l *EventListener) RegisterEventHandler(name string, h EventHandlerFunc)

RegisterEventHandler registers h to receive events named name. Registering the same name more than once adds an additional handler. Matching is case-insensitive.

func (*EventListener) Start

func (l *EventListener) Start(ctx context.Context) error

Start connects the listener and begins dispatching events. It performs the negotiate and handshake synchronously, returning an error if the connection cannot be established, then services the stream on a background goroutine. The listener stops when the connection ends; Done reports that, and Err reports the cause. ctx bounds the whole listener lifetime, not just the connect.

func (*EventListener) Stop

func (l *EventListener) Stop()

Stop ends the listener and waits for it to stop. It cancels the listener, which tears down the connection and closes Done. It is safe to call more than once, before Start returns, and from within an event handler (a handler that calls Stop does not deadlock: the read loop that closes Done runs on a separate goroutine and is not blocked by the handler).

type HTTPMethod

type HTTPMethod string

HTTPMethod is an HTTP verb used with the Invoke surface.

const (
	// MethodGet is the HTTP GET method.
	MethodGet HTTPMethod = http.MethodGet
	// MethodPost is the HTTP POST method.
	MethodPost HTTPMethod = http.MethodPost
	// MethodPut is the HTTP PUT method.
	MethodPut HTTPMethod = http.MethodPut
	// MethodDelete is the HTTP DELETE method.
	MethodDelete HTTPMethod = http.MethodDelete
)

Supported HTTP methods.

type KeyFormat

type KeyFormat string

KeyFormat selects the encoding of an SSH private key returned by RetrievePrivateKey. The values match the Safeguard API's PascalCase names.

const (
	// KeyFormatOpenSSH requests the OpenSSH private-key format. It is the default
	// when RetrievePrivateKey is given an empty KeyFormat.
	KeyFormatOpenSSH KeyFormat = "OpenSsh"
	// KeyFormatSSH2 requests the SSH2 (RFC 4716) private-key format.
	KeyFormatSSH2 KeyFormat = "Ssh2"
	// KeyFormatPuTTY requests the PuTTY private-key format.
	KeyFormatPuTTY KeyFormat = "Putty"
)

type LoginFunc

type LoginFunc func(ctx context.Context, t LoginTransport) (userToken Secret, err error)

LoginFunc runs an interactive OAuth login against the appliance using t and returns the resulting Safeguard user token. It is implemented by the browser and devicecode add-on packages and handed to AuthorizedSession; end users call those packages' Connect functions rather than writing a LoginFunc themselves. The returned Secret is owned by the SDK after a nil error; the login must not retain or zero it.

type LoginTransport

type LoginTransport interface {
	// Do issues an HTTP request on the client's server-trust transport.
	Do(req *http.Request) (*http.Response, error)
	// Host is the appliance host the client is bound to.
	Host() string
	// APIVersion is the default Core API version segment, for example "v4".
	APIVersion() string
}

LoginTransport is the appliance connection handed to an interactive add-on login flow (the browser and devicecode packages). It carries the client's server-trust HTTP transport, which already honors the connection's TLS policy and timeouts, together with the appliance coordinates a flow needs to build RSTS and Core URLs. Its Do method satisfies the internal broker's transport interface, so an add-on can pass a LoginTransport straight into the internal auth helpers without building its own transport.

type NotFoundError

type NotFoundError struct{ *APIError }

NotFoundError is an APIError for an HTTP 404 response.

func (*NotFoundError) Unwrap

func (e *NotFoundError) Unwrap() error

Unwrap returns the embedded *APIError.

type Option

type Option func(*clientConfig) error

Option configures a client connection. Options are applied in order and may return an error to reject an invalid configuration.

func WithAPIVersion

func WithAPIVersion(version string) Option

WithAPIVersion overrides the default Safeguard API version for every request.

func WithCABundle

func WithCABundle(pemBytes []byte) Option

WithCABundle trusts the certificates in pemBytes (an internal PKI/appliance CA) for server verification, replacing the system trust store rather than adding to it: once a bundle is set, only the certificates it contains are trusted to verify the appliance's certificate chain. It is the secure way to trust a self-signed or privately issued appliance certificate.

func WithHTTPTimeouts

func WithHTTPTimeouts(t Timeouts) Option

WithHTTPTimeouts overrides the granular transport timeouts. Zero-valued fields keep their defaults.

func WithInsecureTLS

func WithInsecureTLS() Option

WithInsecureTLS disables TLS chain and hostname verification on every transport, including the event WebSocket. It exists only for bootstrapping self-signed appliances in development and test; it is loud and dangerous and must never be used in production. It cannot be combined with WithServerCertValidator, and it never enables HTTP redirect following.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger attaches a structured logger. Nothing is logged by default and no global logger is used; secrets never appear in logs.

func WithServerCertValidator

func WithServerCertValidator(v ServerCertValidator) Option

WithServerCertValidator adds an additive validation callback that runs after normal chain and hostname verification. It cannot be combined with WithInsecureTLS.

type PKCEOption

type PKCEOption func(*pkceConfig) error

PKCEOption configures a PKCE headless login.

func WithSecondaryFactor

func WithSecondaryFactor(fn SecondaryFactorFunc) PKCEOption

WithSecondaryFactor supplies the callback used to satisfy multi-factor authentication during a PKCE headless login. Without it, a login that reaches a secondary factor fails with ErrSecondaryFactorRequired.

type PersistentEventListener

type PersistentEventListener struct {
	// contains filtered or unexported fields
}

PersistentEventListener is a Safeguard event listener that reconnects automatically with exponential backoff and jitter, preserving its registered handlers across reconnects. For a user session it stops permanently once the owning session is logged out or replaced, rather than reconnecting under a different identity.

func (*PersistentEventListener) Done

func (l *PersistentEventListener) Done() <-chan struct{}

Done returns a channel closed when the listener has permanently stopped. It returns nil before Start.

func (*PersistentEventListener) Err

func (l *PersistentEventListener) Err() error

Err returns the terminal error that stopped the listener, if any.

func (*PersistentEventListener) RegisterEventHandler

func (l *PersistentEventListener) RegisterEventHandler(name string, h EventHandlerFunc)

RegisterEventHandler registers h to receive events named name. Handlers survive reconnects. Matching is case-insensitive.

func (*PersistentEventListener) Start

Start begins the listener's connect-and-reconnect loop on a background goroutine and returns immediately. The loop retries failed connects with backoff and reconnects after a dropped connection, until Stop is called, ctx is cancelled, or the owning session ends. Done reports a terminal stop and Err reports its cause.

func (*PersistentEventListener) Stop

func (l *PersistentEventListener) Stop()

Stop ends the listener and waits for its loop to finish. It is idempotent and is safe to call from within an event handler (a handler that calls Stop does not deadlock).

type ReqOption

type ReqOption func(*requestConfig) error

ReqOption configures a single request. Options are applied in order and may return an error to reject an invalid request configuration.

func WithAPIVersionOverride

func WithAPIVersionOverride(version string) ReqOption

WithAPIVersionOverride overrides the API version for this request only.

func WithAccept

func WithAccept(mediaType string) ReqOption

WithAccept overrides the Accept header (for example "text/csv" for CSV report endpoints). The default is application/json.

func WithHeader

func WithHeader(key, value string) ReqOption

WithHeader sets an additional request header. Setting the reserved Authorization header is rejected with ErrReservedHeader: authorization is controlled exclusively by the transport axis and must never ride the wrong transport.

func WithHost

func WithHost(host string) ReqOption

WithHost overrides the target host for this request only (parity with PySafeguard host_override). The alternate host uses the same TLS configuration and must use the https scheme (a non-https host is rejected, as Safeguard is https-only).

func WithQueryParam

func WithQueryParam(key, value string) ReqOption

WithQueryParam adds a single query parameter to the request.

func WithQueryParams

func WithQueryParams(params map[string]string) ReqOption

WithQueryParams adds multiple query parameters to the request.

func WithRequestTimeout

func WithRequestTimeout(d time.Duration) ReqOption

WithRequestTimeout applies a timeout to this request by deriving a context deadline. It does not set a global client timeout and is safe for non-stream calls; streaming callers should manage cancellation through their own context.

type Response

type Response struct {
	// StatusCode is the HTTP status code of the response.
	StatusCode int
	// Headers holds the response headers. Multi-value headers are preserved.
	Headers http.Header
	// Body is the fully buffered response body for non-streaming calls; it is nil
	// for streaming responses.
	Body []byte
	// RequestID is the appliance request/correlation identifier, or empty when the
	// response did not include one.
	RequestID string
}

Response is the complete result of an Invoke-style call: the HTTP status, the response headers, the fully read (non-streaming) body, and the appliance request identifier when one is present.

For streaming calls the body is delivered separately and Body is nil; the headers and RequestID still describe the response.

func (Response) BodyString

func (r Response) BodyString() string

BodyString returns the response body as a string. It is a convenience for callers that expect a textual (JSON/CSV) body. Response deliberately does not implement fmt.Stringer: bodies can contain retrieved credentials, so exposing the body is an explicit call rather than something an implicit %v/%s format or a log of a Response would trigger.

func (Response) IsSuccess

func (r Response) IsSuccess() bool

IsSuccess reports whether the status code is in the 2xx range.

type SecondaryFactorFunc

type SecondaryFactorFunc func(ctx context.Context, prompt string) (Secret, error)

SecondaryFactorFunc supplies a multi-factor one-time code given the appliance's prompt. It is invoked only when the primary login step reports that a secondary factor is required. Returning an error aborts the login.

type Secret

type Secret struct {
	// contains filtered or unexported fields
}

Secret holds sensitive bytes (passwords, tokens, API keys, retrieved credentials) and makes reading them a deliberate act. Its purpose is to prevent accidental disclosure, not to provide cryptographic or memory-hardened protection; set expectations accordingly.

What it guards against (the common ways an SDK leaks a credential):

  • fmt: String, GoString, and Format render "[REDACTED]" for every verb, so %v/%s/%+v/%#v, prints, and error messages never reveal the bytes.
  • Serialization: MarshalJSON and MarshalText emit "[REDACTED]", so a Secret embedded in a struct is not written out in cleartext.
  • Logging: LogValue (slog.LogValuer) redacts the value in structured logs.

Hygiene it adds: NewSecret copies its input and Expose returns a copy, so no backing array is aliased; Equal compares in constant time; Zero best-effort wipes the buffer to shrink the in-memory plaintext window.

What it deliberately does NOT provide:

  • No encryption at rest in memory: the bytes are plaintext on the Go heap while held. It is not a defense against an adversary who can read process memory, a core dump, swap, or an attached debugger.
  • Once exposed, derived values cannot be reclaimed: an ExposeString result is an immutable Go string that cannot be zeroed, and a parsed key (for example a tls.Certificate) is out of Secret's control.
  • Zero is best-effort only: Go's runtime may copy or move the backing array, so disposal is not a hard guarantee.

Callers obtain the underlying bytes only through the explicit Expose or ExposeString methods. Secret is a value type.

func NewSecret

func NewSecret(b []byte) Secret

NewSecret returns a Secret that copies b. The caller retains ownership of b and may zero it after this call without affecting the Secret.

func NewSecretString

func NewSecretString(s string) Secret

NewSecretString returns a Secret containing a copy of the bytes of s.

func (*Secret) Close

func (s *Secret) Close() error

Close wipes the Secret and always returns nil; it lets a Secret satisfy io.Closer for use with defer.

func (Secret) Equal

func (s Secret) Equal(other Secret) bool

Equal reports whether s and other hold identical bytes. It uses a constant-time comparison (crypto/subtle) so it does not leak, through timing, where two same-length secrets first differ. A length mismatch returns false.

func (Secret) Expose

func (s Secret) Expose() []byte

Expose returns a copy of the secret's bytes. The caller owns the returned slice and may zero it when finished. Returns nil for an empty Secret.

func (Secret) ExposeString

func (s Secret) ExposeString() string

ExposeString returns the secret's bytes as a string copy. Prefer Expose when the caller can zero the bytes afterward; a string cannot be zeroed.

func (Secret) Format

func (s Secret) Format(f fmt.State, _ rune)

Format implements fmt.Formatter so every verb renders the redaction placeholder instead of the underlying bytes.

func (Secret) GoString

func (s Secret) GoString() string

GoString implements fmt.GoStringer so %#v does not reveal the bytes.

func (Secret) IsZero

func (s Secret) IsZero() bool

IsZero reports whether the Secret holds no bytes.

func (Secret) Len

func (s Secret) Len() int

Len returns the number of bytes held by the Secret.

func (Secret) LogValue

func (s Secret) LogValue() slog.Value

LogValue implements slog.LogValuer so structured logs render the placeholder.

func (Secret) MarshalJSON

func (s Secret) MarshalJSON() ([]byte, error)

MarshalJSON renders the Secret as the redaction placeholder string so it is never serialized in cleartext.

func (Secret) MarshalText

func (s Secret) MarshalText() ([]byte, error)

MarshalText renders the Secret as the redaction placeholder.

func (Secret) String

func (s Secret) String() string

String implements fmt.Stringer and always returns the redaction placeholder.

func (*Secret) Zero

func (s *Secret) Zero()

Zero best-effort wipes the Secret's internal buffer in place. Any copies made via Expose are unaffected.

type ServerCertValidator

type ServerCertValidator func(leaf *x509.Certificate, verifiedChains [][]*x509.Certificate) error

ServerCertValidator is an additive server-certificate check. It runs only after the normal chain and hostname verification succeeds and can therefore only further restrict trust, never relax it (parity with the .NET RemoteCertificateValidationCallback). Returning a non-nil error fails the handshake. leaf is the server's leaf certificate; verifiedChains are the chains the standard verifier accepted.

type Service

type Service string

Service identifies a Safeguard API service that a request is routed to.

Authorization is never inferred from the Service or the URL; the service only selects the base URL path. See the package overview's "Transport and authorization" section for how TLS identity and authorization are chosen independently of the service.

const (
	// Core is the primary Safeguard for Privileged Passwords API service.
	Core Service = "core"
	// Appliance is the appliance-management API service.
	Appliance Service = "appliance"
	// Notification is the anonymous/notification API service (no token required).
	Notification Service = "notification"
	// A2A is the Application-to-Application credential-retrieval service.
	A2A Service = "a2a"
	// Event is the SignalR event service.
	Event Service = "event"
	// RSTS is the embedded secure token service; it has no version path segment.
	RSTS Service = "rsts"
	// Management is the appliance management service.
	Management Service = "management"
)

The Safeguard API services.

type Timeouts

type Timeouts struct {
	// Dial bounds establishing the TCP connection.
	Dial time.Duration
	// TLSHandshake bounds completing the TLS handshake.
	TLSHandshake time.Duration
	// ResponseHeader bounds waiting for the first response header byte after the
	// request is written. It does not bound reading the response body.
	ResponseHeader time.Duration
}

Timeouts configures the granular transport timeouts. None of these is a global request deadline: a global timeout would break long-lived streams and event connections. Per-request deadlines are set through context and WithRequestTimeout.

type TransportError

type TransportError struct {
	// Op is a short description of the operation that failed (for example "dial"
	// or "request").
	Op string
	// Err is the underlying error.
	Err error
}

TransportError wraps a network, TLS, or protocol failure that occurred before a usable HTTP response was received.

func (*TransportError) Error

func (e *TransportError) Error() string

Error implements error.

func (*TransportError) Unwrap

func (e *TransportError) Unwrap() error

Unwrap returns the underlying error so errors.Is/As reach the cause.

Directories

Path Synopsis
Package browser provides an interactive Safeguard login that opens the user's system web browser to complete OAuth authorization (the authorization-code flow with PKCE).
Package browser provides an interactive Safeguard login that opens the user's system web browser to complete OAuth authorization (the authorization-code flow with PKCE).
Package devicecode provides an interactive Safeguard login using the OAuth device authorization grant: the appliance issues a short user code and a verification URL, the user visits the URL on any device and enters the code, and the SDK polls until authorization completes.
Package devicecode provides an interactive Safeguard login using the OAuth device authorization grant: the appliance issues a short user code and a verification URL, the user visits the URL on any device and enters the code, and the SDK polls until authorization completes.
internal
auth
Package auth is the internal Safeguard authentication broker.
Package auth is the internal Safeguard authentication broker.
livetest
Package livetest provides shared helpers for the SDK's live-appliance end-to-end tests.
Package livetest provides shared helpers for the SDK's live-appliance end-to-end tests.
samples
a2a-apikey command
Command a2a-apikey demonstrates Application-to-Application (A2A) API-key retrieval.
Command a2a-apikey demonstrates Application-to-Application (A2A) API-key retrieval.
a2a-broker command
Command a2a-broker demonstrates brokering an access request on behalf of another user.
Command a2a-broker demonstrates brokering an access request on behalf of another user.
a2a-discover command
Command a2a-discover demonstrates GetRetrievableAccounts, which lists every account the context's client certificate is registered to retrieve, across all of its A2A registrations.
Command a2a-discover demonstrates GetRetrievableAccounts, which lists every account the context's client certificate is registered to retrieve, across all of its A2A registrations.
a2a-events command
Command a2a-events demonstrates an A2A credential-change event listener.
Command a2a-events demonstrates an A2A credential-change event listener.
a2a-password command
Command a2a-password demonstrates Application-to-Application (A2A) password retrieval.
Command a2a-password demonstrates Application-to-Application (A2A) password retrieval.
a2a-privatekey command
Command a2a-privatekey demonstrates Application-to-Application (A2A) SSH private-key retrieval.
Command a2a-privatekey demonstrates Application-to-Application (A2A) SSH private-key retrieval.
a2a-set-password command
Command a2a-set-password demonstrates SetPassword, the A2A write-back that stores a new password for an account.
Command a2a-set-password demonstrates SetPassword, the A2A write-back that stores a new password for an account.
anonymous command
Command anonymous demonstrates an anonymous session, which carries no user token and is sufficient for the Notification service and other unauthenticated endpoints such as the appliance status.
Command anonymous demonstrates an anonymous session, which carries no user token and is sufficient for the Notification service and other unauthenticated endpoints such as the appliance status.
browser command
Command browser demonstrates an interactive external-browser (PKCE) login.
Command browser demonstrates an interactive external-browser (PKCE) login.
certificate command
Command certificate demonstrates a client-certificate login over mutual TLS.
Command certificate demonstrates a client-certificate login over mutual TLS.
devicecode command
Command devicecode demonstrates a device authorization grant login.
Command devicecode demonstrates a device authorization grant login.
download command
Command download demonstrates Download, which streams a response body straight to an io.Writer without buffering it in memory — suited to large payloads such as backups or reports.
Command download demonstrates Download, which streams a response body straight to an io.Writer without buffering it in memory — suited to large payloads such as backups or reports.
events command
Command events demonstrates a one-shot SignalR event listener.
Command events demonstrates a one-shot SignalR event listener.
events-persistent command
Command events-persistent demonstrates a reconnecting SignalR event listener.
Command events-persistent demonstrates a reconnecting SignalR event listener.
invoke command
Command invoke is a small general-purpose client that issues one request with any HTTP method against any Safeguard service, demonstrating the generic Invoke surface (Get/Post/Put/Delete).
Command invoke is a small general-purpose client that issues one request with any HTTP method against any Safeguard service, demonstrating the generic Invoke surface (Get/Post/Put/Delete).
invoke-typed command
Command invoke-typed demonstrates InvokeTyped, which decodes a successful JSON response directly into a Go value instead of returning the raw body.
Command invoke-typed demonstrates InvokeTyped, which decodes a successful JSON response directly into a Go value instead of returning the raw body.
password command
Command password demonstrates a Resource Owner Grant (username/password) login and a single Core API call.
Command password demonstrates a Resource Owner Grant (username/password) login and a single Core API call.
pkce command
Command pkce demonstrates a PKCE non-interactive ("headless") login.
Command pkce demonstrates a PKCE non-interactive ("headless") login.
token command
Command token demonstrates reusing an existing Safeguard user token instead of performing a login exchange.
Command token demonstrates reusing an existing Safeguard user token instead of performing a login exchange.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL