auth

package module
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jul 18, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

README

vex-auth-sdk-go

Typed Go SDK for vex-auth, generated from the canonical Smithy spec.

go get github.com/vextura/vex-auth-sdk-go

Usage

import "github.com/vextura/vex-auth-sdk-go"

func main() {
    client := auth.New("https://gate.example.com", "<bearer-token>")
    // typed method per Smithy operation
}

OAuth2 token exchange

For OAuth2 grant flows (password, refresh_token, client_credentials) prefer IssueTokenForm — it POSTs application/x-www-form-urlencoded per RFC 6749 §3.2 and decodes RFC 6749 §5.2 error responses into a typed *OAuth2Error.

tok, err := client.IssueTokenForm(ctx, auth.TokenRequest{
    GrantType:    "refresh_token",
    RefreshToken: current,
    ClientId:     "vexctl-cli",
})
if oe, ok := auth.IsOAuth2Error(err); ok && oe.Code == "invalid_grant" {
    // refresh token expired — prompt re-login
}

IssueToken (JSON) is retained for callers that need the Vextura internal envelope; both methods surface OAuth2 error text when the server emits it.

OAuth2 token revocation

For RFC 7009 revocation (logout, session invalidation, admin-triggered cleanup) prefer RevokeTokenForm — it POSTs application/x-www-form- urlencoded per RFC 7009 §2.1 and honours the §2.2 no-info-leak semantics (returns nil whether the server knew the token or not).

err := client.RevokeTokenForm(ctx, auth.RevokeRequest{
    Token:         refreshToken,
    TokenTypeHint: "refresh_token", // optional — helps server skip a lookup
    ClientID:      "vexctl-cli",    // optional — for public clients
})
if oe, ok := auth.IsOAuth2Error(err); ok && oe.Code == "invalid_client" {
    // client credentials rejected — surface to user
}

Because RFC 7009 forbids leaking token-existence, a nil return does not prove the token was live before the call. Do not use RevokeTokenForm as a validity check — use IssueTokenForm with grant_type=refresh_token and inspect the OAuth2 error for that purpose.

RevokeToken (JSON) is retained for callers on the Vextura internal envelope; new integrations should use RevokeTokenForm.

Generated

Most files (client.go, types.go, operations.go, errors.go, doc.go) are generated by vexctl clientgen — do not hand-edit; regenerate from the Smithy source when the API changes. oauth2.go is hand-written until codegen learns the @httpFormRequest and OAuth2 error traits.

License

Apache 2.0 — see LICENSE.

Documentation

Overview

Package auth is the typed Go SDK for AuthService.

Usage:

c := auth.New("", "<bearer-token>")
out, err := c.OperationName(ctx, auth.OperationNameInput{...})
if err != nil {
    var nf auth.ErrorEnvelope
    if errors.As(err, &nf) { ... typed error ... }
}

The SDK shares its wire types with the server's generated handlers by construction — both sides emit from the same Smithy spec, so they cannot drift apart. Add new operations to the .smithy and regenerate both halves.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrValidation   = errors.New("validation failed")
	ErrUnauthorized = errors.New("unauthorized")
	ErrForbidden    = errors.New("forbidden")
	ErrNotFound     = errors.New("not found")
	ErrConflict     = errors.New("conflict")
	ErrInternal     = errors.New("internal error")
)

Sentinel errors callers can match with errors.Is. Decoded from ErrorEnvelope.Code on every non-2xx response.

Functions

This section is empty.

Types

type AssumeRoleInput

type AssumeRoleInput struct {
	RoleSlug        string `json:"role_slug"`
	SessionName     string `json:"session_name,omitempty"`
	DurationSeconds int    `json:"duration_seconds,omitempty"`
}

AssumeRoleInput mirrors the Smithy structure of the same name.

type AssumeRoleOutput

type AssumeRoleOutput struct {
	AccessToken   string         `json:"access_token"`
	ExpiresAt     string         `json:"expires_at"`
	AssumedClaims *AssumedClaims `json:"assumed_claims"`
}

AssumeRoleOutput mirrors the Smithy structure of the same name.

type AssumedClaims

type AssumedClaims struct {
	Iss         string `json:"iss"`
	Sub         string `json:"sub"`
	Exp         int64  `json:"exp"`
	Iat         int64  `json:"iat"`
	Tenant      string `json:"tenant"`
	Role        string `json:"role"`
	AssumedBy   string `json:"assumed_by"`
	SessionName string `json:"session_name,omitempty"`
}

AssumedClaims mirrors the Smithy structure of the same name.

type Client

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

Client is the typed SDK for AuthService.

Construct with New(); every operation is a typed method on Client. Default HTTPClient has a 30s timeout and follows redirects; override with WithHTTPClient(...) when you need different behavior.

func New

func New(endpoint, token string, opts ...Option) *Client

New constructs a Client. endpoint is the service base URL (e.g. "http://vex-vault:8200"); token is the bearer credential sent on every request. Either may be empty for tests against httptest.NewServer.

func (*Client) AssumeRole

func (c *Client) AssumeRole(ctx context.Context, in AssumeRoleInput) (AssumeRoleOutput, error)

AssumeRole calls POST /auth/assume-role.

func (*Client) CreateServiceAccount

CreateServiceAccount calls POST /auth/service-accounts.

func (*Client) DeleteServiceAccount

func (c *Client) DeleteServiceAccount(ctx context.Context, in ServiceAccountIdInput) error

DeleteServiceAccount calls DELETE /auth/service-accounts/{clientId}.

func (*Client) Endpoint

func (c *Client) Endpoint() string

Endpoint returns the service base URL the client is targeting.

func (*Client) GetHealth

func (c *Client) GetHealth(ctx context.Context) (HealthOutput, error)

GetHealth calls GET /health.

func (*Client) GetJWKS

func (c *Client) GetJWKS(ctx context.Context) (JWKSet, error)

GetJWKS calls GET /.well-known/jwks.json.

func (*Client) GetOpenIDConfiguration

func (c *Client) GetOpenIDConfiguration(ctx context.Context) (OpenIDConfiguration, error)

GetOpenIDConfiguration calls GET /.well-known/openid-configuration.

func (*Client) GetServiceAccount

func (c *Client) GetServiceAccount(ctx context.Context, in ServiceAccountIdInput) (ServiceAccount, error)

GetServiceAccount calls GET /auth/service-accounts/{clientId}.

func (*Client) GetSession

func (c *Client) GetSession(ctx context.Context, in SessionIdInput) (SessionDetail, error)

GetSession calls GET /auth/sessions/{sessionId}.

func (*Client) IssueToken

func (c *Client) IssueToken(ctx context.Context, in TokenRequest) (TokenResponse, error)

IssueToken calls POST /auth/token.

NOTE: this is the JSON path retained for callers that speak the Vextura internal wire shape. OAuth2 spec-compliant clients should prefer IssueTokenForm (application/x-www-form-urlencoded per RFC 6749 §3.2). Both methods route errors through decodeOAuth2ErrorOrFallback so callers see actionable OAuth2 error text ({error, error_description}) regardless of which envelope the server chose to emit.

func (*Client) IssueTokenForm added in v0.3.0

func (c *Client) IssueTokenForm(ctx context.Context, in TokenRequest) (TokenResponse, error)

IssueTokenForm posts application/x-www-form-urlencoded to /auth/token per RFC 6749 §3.2. Prefer this over IssueToken for any OAuth2 grant flow (password, refresh_token, client_credentials, …); it matches the wire shape every OAuth2 spec-compliant server (including vex-auth) expects and unlocks the RFC 6749 §5.2 error decoder on failure.

The JSON path (IssueToken) is retained for callers who want the internal Vextura envelope shape or need to send fields that don't fit the form encoding. Both methods share TokenRequest and TokenResponse.

Encoding rules:

  • Empty string fields are omitted (OAuth2 servers reject empty grant_type etc. more clearly when the field is missing than when it's blank).
  • Scope is joined with spaces per RFC 6749 §3.3.

Errors are decoded through decodeOAuth2ErrorOrFallback so both OAuth2 and Vextura envelopes surface actionable information.

func (*Client) ListServiceAccounts

ListServiceAccounts calls GET /auth/service-accounts.

func (*Client) Login

func (c *Client) Login(ctx context.Context, in LoginInput) (LoginOutput, error)

Login calls POST /auth/login.

func (*Client) Logout

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

Logout calls POST /auth/logout.

func (*Client) RefreshSession

func (c *Client) RefreshSession(ctx context.Context, in RefreshInput) (LoginOutput, error)

RefreshSession calls POST /auth/refresh.

func (*Client) RevokeToken added in v0.2.0

RevokeToken calls POST /auth/revoke.

func (*Client) RevokeTokenForm added in v0.4.0

func (c *Client) RevokeTokenForm(ctx context.Context, in RevokeRequest) error

RevokeTokenForm posts application/x-www-form-urlencoded to /auth/revoke per RFC 7009 §2.1. Prefer this over RevokeToken for any OAuth2 grant flow client (vexctl logout, vex-mcp-server auth-revoke, etc.) — it matches the spec wire shape every RFC 7009 server expects and unlocks the RFC 6749 §5.2 error decoder on failure.

The JSON path (RevokeToken) is retained for backwards compatibility with callers on the internal Vextura envelope. Both target the same endpoint.

Semantics (per RFC 7009 §2.2):

  • 200 is returned whether the token was known or unknown. The server MUST NOT distinguish, so this method returns nil in both cases. Callers cannot use RevokeTokenForm to probe token validity.
  • 400 invalid_request / 401 invalid_client → *OAuth2Error.
  • 503 unsupported_token_type → *OAuth2Error per §2.2.1.

Encoding rules mirror IssueTokenForm: empty fields are omitted so the server sees "missing" rather than "blank".

func (*Client) SignUp added in v0.2.0

func (c *Client) SignUp(ctx context.Context, in SignUpInput) (SignUpOutput, error)

SignUp calls POST /auth/signup.

type CreateServiceAccountInput

type CreateServiceAccountInput struct {
	ClientId    string   `json:"client_id"`
	Name        string   `json:"name"`
	Tenant      string   `json:"tenant"`
	Description string   `json:"description,omitempty"`
	Scope       []string `json:"scope,omitempty"`
}

CreateServiceAccountInput mirrors the Smithy structure of the same name.

type CreateServiceAccountOutput

type CreateServiceAccountOutput struct {
	ClientId     string `json:"client_id"`
	ClientSecret string `json:"client_secret"`
	Name         string `json:"name"`
	Tenant       string `json:"tenant"`
}

CreateServiceAccountOutput mirrors the Smithy structure of the same name.

type ErrorEnvelope

type ErrorEnvelope struct {
	Code      string         `json:"code"`
	Message   string         `json:"message"`
	RequestID string         `json:"request_id,omitempty"`
	TraceID   string         `json:"trace_id,omitempty"`
	Details   map[string]any `json:"details,omitempty"`
	// Status is the HTTP status code observed on the wire. Not part of the
	// JSON shape; set by the decoder so callers can switch on it.
	Status int `json:"-"`
}

ErrorEnvelope is the wire shape every server returns on error.

func (ErrorEnvelope) Error

func (e ErrorEnvelope) Error() string

type HealthOutput

type HealthOutput struct {
	Status string `json:"status"`
}

HealthOutput mirrors the Smithy structure of the same name.

type JWK

type JWK struct {
	Kty string `json:"kty"`
	Use string `json:"use"`
	Alg string `json:"alg"`
	Kid string `json:"kid"`
	N   string `json:"n,omitempty"`
	E   string `json:"e,omitempty"`
	X   string `json:"x,omitempty"`
	Y   string `json:"y,omitempty"`
	Crv string `json:"crv,omitempty"`
}

JWK mirrors the Smithy structure of the same name.

type JWKSet

type JWKSet struct {
	Keys []*JWK `json:"keys"`
}

JWKSet mirrors the Smithy structure of the same name.

type ListServiceAccountsInput

type ListServiceAccountsInput struct {
	Tenant string `json:"tenant,omitempty"`
}

ListServiceAccountsInput mirrors the Smithy structure of the same name.

type ListServiceAccountsOutput

type ListServiceAccountsOutput struct {
	Accounts []*ServiceAccount `json:"accounts"`
}

ListServiceAccountsOutput mirrors the Smithy structure of the same name.

type LoginInput

type LoginInput struct {
	Username string `json:"username"`
	Password string `json:"password"`
	Tenant   string `json:"tenant,omitempty"`
}

LoginInput mirrors the Smithy structure of the same name.

type LoginOutput

type LoginOutput struct {
	SessionId string   `json:"session_id"`
	Token     string   `json:"token"`
	ExpiresAt string   `json:"expires_at"`
	Tenant    string   `json:"tenant"`
	Roles     []string `json:"roles,omitempty"`
}

LoginOutput mirrors the Smithy structure of the same name.

type LogoutInput

type LogoutInput struct {
	SessionId string `json:"session_id"`
}

LogoutInput mirrors the Smithy structure of the same name.

type OAuth2Error added in v0.3.0

type OAuth2Error struct {
	// Code is one of the RFC 6749 §5.2 error codes: "invalid_request",
	// "invalid_client", "invalid_grant", "unauthorized_client",
	// "unsupported_grant_type", "invalid_scope", plus any extension codes
	// the server chooses to emit (e.g. "server_error").
	Code string
	// Description is the human-readable explanation (error_description).
	// May be empty when the server does not populate it.
	Description string
	// URI is an optional link to a page explaining the error (error_uri).
	// Rarely populated in practice.
	URI string
	// HTTPStatus is the originating HTTP status code (400/401/…). Preserved
	// for callers who care about the transport-level status.
	HTTPStatus int
}

OAuth2Error is the RFC 6749 §5.2 error response returned by /auth/token (and any other OAuth2 endpoint) when the request is rejected.

Callers who need to branch on the OAuth2 code (e.g. surface "invalid_grant" to the user as "your refresh token expired, please log in again") should use IsOAuth2Error or errors.As with *OAuth2Error.

func IsOAuth2Error added in v0.3.0

func IsOAuth2Error(err error) (*OAuth2Error, bool)

IsOAuth2Error extracts an *OAuth2Error from any error, if present. Returns (nil, false) when the error is not an OAuth2Error.

Prefer this over a bare errors.As call at the call site — it keeps the idiom terse:

if oe, ok := auth.IsOAuth2Error(err); ok && oe.Code == "invalid_grant" {
    // prompt re-login
}

func (*OAuth2Error) Error added in v0.3.0

func (e *OAuth2Error) Error() string

Error implements error. Format matches OAuth2 tooling conventions: "invalid_grant: refresh token has expired".

type OpenIDConfiguration

type OpenIDConfiguration struct {
	Issuer                           string   `json:"issuer"`
	AuthorizationEndpoint            string   `json:"authorization_endpoint"`
	TokenEndpoint                    string   `json:"token_endpoint"`
	JwksUri                          string   `json:"jwks_uri"`
	ResponseTypesSupported           []string `json:"response_types_supported"`
	SubjectTypesSupported            []string `json:"subject_types_supported"`
	IdTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
	GrantTypesSupported              []string `json:"grant_types_supported,omitempty"`
	ScopesSupported                  []string `json:"scopes_supported,omitempty"`
}

OpenIDConfiguration mirrors the Smithy structure of the same name.

type Option

type Option func(*Client)

Option configures a Client at construction time.

func WithHTTPClient

func WithHTTPClient(h *http.Client) Option

WithHTTPClient overrides the default http.Client. Use this to plug in custom transports, retry middleware, or test doubles.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the per-request timeout on the default http.Client. Ignored if WithHTTPClient already supplied a fully-configured client.

type RefreshInput

type RefreshInput struct {
	SessionId string `json:"session_id"`
}

RefreshInput mirrors the Smithy structure of the same name.

type RevokeRequest added in v0.4.0

type RevokeRequest struct {
	// Token is the credential to revoke. Required. Either an access token
	// or a refresh token — the server determines the type from the value
	// itself (with the optional TokenTypeHint helping short-circuit the
	// lookup per RFC 7009 §2.1).
	Token string
	// TokenTypeHint is one of "access_token" or "refresh_token" per RFC
	// 7009 §2.1. Optional; the server MUST still process the request when
	// the hint is missing or wrong. Empty means "no hint".
	TokenTypeHint string
	// ClientID identifies the OAuth2 client (RFC 6749 §2.3.1). Optional
	// when the client is already authenticated at the transport layer
	// (e.g. via Bearer on the SDK). Send it when acting as a public
	// client that has no other credentials.
	ClientID string
	// ClientSecret authenticates the client for confidential clients
	// (RFC 6749 §2.3.1). Optional. Empty for public clients.
	ClientSecret string
}

RevokeRequest is the RFC 7009 §2.1 token revocation request.

Unlike RevokeTokenRequest (the codegen struct which mirrors the internal Smithy shape), this type carries the optional client-authentication fields clients need when calling /auth/revoke as a public OAuth2 client. It stays hand-written for the same reason IssueTokenForm's inputs do — codegen does not yet emit the RFC 6749/7009 form-encoded shape.

type RevokeTokenRequest added in v0.2.0

type RevokeTokenRequest struct {
	Token         string `json:"token"`
	TokenTypeHint string `json:"token_type_hint,omitempty"`
}

RevokeTokenRequest mirrors the Smithy structure of the same name.

type RevokeTokenResponse added in v0.2.0

type RevokeTokenResponse struct {
}

RevokeTokenResponse mirrors the Smithy structure of the same name.

type ServiceAccount

type ServiceAccount struct {
	ClientId    string   `json:"client_id"`
	Name        string   `json:"name"`
	Tenant      string   `json:"tenant"`
	Description string   `json:"description,omitempty"`
	Scope       []string `json:"scope,omitempty"`
	CreatedAt   string   `json:"created_at"`
}

ServiceAccount mirrors the Smithy structure of the same name.

type ServiceAccountIdInput

type ServiceAccountIdInput struct {
	ClientId string `json:"client_id"`
}

ServiceAccountIdInput mirrors the Smithy structure of the same name.

type ServiceClaims

type ServiceClaims struct {
	Iss      string   `json:"iss"`
	Sub      string   `json:"sub"`
	Aud      []string `json:"aud"`
	Exp      int64    `json:"exp"`
	Iat      int64    `json:"iat"`
	Tenant   string   `json:"tenant"`
	Roles    []string `json:"roles,omitempty"`
	Scope    []string `json:"scope,omitempty"`
	ClientId string   `json:"client_id,omitempty"`
}

ServiceClaims mirrors the Smithy structure of the same name.

type SessionDetail

type SessionDetail struct {
	SessionId string   `json:"session_id"`
	Subject   string   `json:"subject"`
	Tenant    string   `json:"tenant"`
	IssuedAt  string   `json:"issued_at"`
	ExpiresAt string   `json:"expires_at"`
	Roles     []string `json:"roles,omitempty"`
}

SessionDetail mirrors the Smithy structure of the same name.

type SessionIdInput

type SessionIdInput struct {
	SessionId string `json:"session_id"`
}

SessionIdInput mirrors the Smithy structure of the same name.

type SignUpInput added in v0.2.0

type SignUpInput struct {
	CompanyName string `json:"company_name"`
	Tenant      string `json:"tenant"`
	Username    string `json:"username"`
	Email       string `json:"email"`
	Password    string `json:"password"`
	DisplayName string `json:"display_name,omitempty"`
}

SignUpInput mirrors the Smithy structure of the same name.

type SignUpOutput added in v0.2.0

type SignUpOutput struct {
	Tenant           string   `json:"tenant"`
	Username         string   `json:"username"`
	AccessToken      string   `json:"access_token"`
	TokenType        string   `json:"token_type"`
	ExpiresIn        int      `json:"expires_in"`
	ProvisionedRoles []string `json:"provisioned_roles"`
	RootRole         string   `json:"root_role"`
}

SignUpOutput mirrors the Smithy structure of the same name.

type TokenRequest

type TokenRequest struct {
	GrantType    string   `json:"grant_type"`
	ClientId     string   `json:"client_id,omitempty"`
	ClientSecret string   `json:"client_secret,omitempty"`
	Username     string   `json:"username,omitempty"`
	Password     string   `json:"password,omitempty"`
	RefreshToken string   `json:"refresh_token,omitempty"`
	Tenant       string   `json:"tenant,omitempty"`
	Scope        []string `json:"scope,omitempty"`
}

TokenRequest mirrors the Smithy structure of the same name.

type TokenResponse

type TokenResponse struct {
	AccessToken           string   `json:"access_token"`
	TokenType             string   `json:"token_type"`
	ExpiresIn             int      `json:"expires_in"`
	RefreshToken          string   `json:"refresh_token,omitempty"`
	RefreshTokenExpiresIn int      `json:"refresh_token_expires_in,omitempty"`
	Scope                 []string `json:"scope,omitempty"`
}

TokenResponse mirrors the Smithy structure of the same name.

Jump to

Keyboard shortcuts

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