Help us shape the future of Go! Take the 2026 Go Developer Survey

altiumauth

package module
v0.0.0-...-ef427eb Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: MIT Imports: 20 Imported by: 0

README

altium-auth for Go

Go Reference CI license

Altium 365 OAuth2 / OpenID Connect authentication library for Go. Supports both client types, and Commercial Cloud, GovCloud, and AES (on-prem) deployments:

  • Public clients (desktop, CLI, native) — browser sign-in with PKCE over Altium's ActionWait long-poll: Config.SignIn.
  • Confidential clients (web/server backends with a secret) — the standard authorization-code redirect flow: Config.CreateAuthorizationURL + Config.ExchangeCode.
  • Workspace tokens, token refresh, revocation, and first-class GovCloud and AES (on-prem) support.

Standard library only — no dependencies. Requires Go 1.25 or later.

Documentation

The library implements the flow described in these guides (protocol-level, independent of this package) — start here if you're new to Altium Identity:

The full API reference is on pkg.go.dev.

Installation

go get github.com/AltiumDeveloper/altium-auth/libs/go
import altiumauth "github.com/AltiumDeveloper/altium-auth/libs/go"

Quick Start

Only ClientID and Scopes are required — the endpoints default to the Altium 365 Commercial Cloud. Every network call takes a context.Context; cancel it to abandon the call.

Public apps (desktop / CLI — ActionWait sign-in)

For clients that can't host a public redirect. SignIn opens the browser, waits for the callback over Altium's ActionWait long-poll, and returns tokens. See Desktop apps.

cfg := altiumauth.Config{ClientID: "your-client-id", Scopes: "openid profile"}

// Opens a browser login page and waits for the callback (default timeout: 3 minutes).
tokens, err := cfg.SignIn(ctx, altiumauth.SignInOptions{})
if err != nil {
	return err
}
// Persist tokens yourself — e.g. the OS keychain or a file.

// Exchange the global token for a workspace-scoped token.
ws, err := cfg.SignIntoWorkspace(ctx, tokens.AccessToken, "workspace-id-here")

The library returns tokens and never stores them — persistence, refresh, and sign-out are yours to manage.

To open the URL through a host API instead of the OS browser, pass OpenBrowser. The library still owns PKCE, ActionWait polling, state correlation, CSRF validation, and the token exchange:

tokens, err := cfg.SignIn(ctx, altiumauth.SignInOptions{
	OpenBrowser: func(url string) error { return myHost.OpenExternal(url) },
})
Confidential apps (web / server — authorization-code redirect)

For backends that host their own redirect endpoint. Set ClientSecret to authenticate as a confidential client (HTTP Basic). See Web / server apps.

cfg := altiumauth.Config{
	ClientID:     "your-client-id",
	ClientSecret: os.Getenv("A365_CLIENT_SECRET"), // confidential client → HTTP Basic
	Scopes:       "openid profile offline_access",
}
const redirectURI = "https://my-service.example.com/oauth/callback"

// Login route: build the URL, stash State + CodeVerifier in the session, redirect.
authz, err := cfg.CreateAuthorizationURL(altiumauth.AuthorizationURLOptions{RedirectURI: redirectURI})
session.Save(authz.State, authz.CodeVerifier)
http.Redirect(w, r, authz.URL, http.StatusFound)

// Callback route: verify state, then exchange the code.
if r.URL.Query().Get("state") != session.State {
	http.Error(w, "state mismatch", http.StatusBadRequest)
	return
}
tokens, err := cfg.ExchangeCode(r.Context(), altiumauth.ExchangeCodeParams{
	Code:         r.URL.Query().Get("code"),
	CodeVerifier: session.CodeVerifier,
	RedirectURI:  redirectURI,
})

CreateAuthorizationURL does no network I/O. SignIntoWorkspace, RefreshToken, and RevokeRefreshToken also send the ClientSecret automatically when it's set.

GovCloud

Use GovCloudEndpoints. The library detects the Gov token endpoint and adds the required secure=1 to token requests automatically.

cfg := altiumauth.Config{
	ClientID:  "your-gov-client-id",
	Scopes:    "openid profile",
	Endpoints: altiumauth.GovCloudEndpoints,
}

secure=1 is driven by the token endpoint — Gov → sent, Commercial → omitted — so pointing TokenEndpoint at the Gov host is all it takes to exchange a Commercial token for a Gov workspace token. Set Config.Secure only to force it for a non-standard host.

Gov tokens must never be used against Commercial services, and vice versa. See the GovCloud guide.

AES (on-prem)

AES runs on a customer-controlled origin, so derive the endpoints with AESEndpoints. AES never uses secure=1. It hosts one workspace, so discover its scope and request it at sign-in (one trip):

const clientID = "your-aes-client-id"
endpoints := altiumauth.AESEndpoints("https://aes.server.example:9785")
scopes, err := altiumauth.GetClientScopes(ctx, nil, endpoints.ScopeEndpoint, clientID)
if err != nil {
	return err
}
cfg := altiumauth.Config{ClientID: clientID, Scopes: strings.Join(scopes, " "), Endpoints: endpoints}
tokens, err := cfg.SignIn(ctx, altiumauth.SignInOptions{})

See the AES (on-prem) guide.

TLS trust for AES

AES servers often use a certificate from a private or corporate CA. Go verifies TLS against the operating system's trust store (the Keychain on macOS, the certificate store on Windows, the system CA bundle on Linux), so a CA installed there is trusted with no extra configuration. On Linux, you can also point Go at a bundle with SSL_CERT_FILE or SSL_CERT_DIR.

To trust a CA only for this client, pass your own HTTP client:

pool, _ := x509.SystemCertPool()
pool.AppendCertsFromPEM(aesRootCAPEM)
cfg.HTTPClient = &http.Client{Transport: &http.Transport{
	Proxy:           http.ProxyFromEnvironment,
	TLSClientConfig: &tls.Config{RootCAs: pool},
}}

Never disable verification (InsecureSkipVerify): the token endpoint receives your authorization codes, and with a client secret configured, the secret as well. An untrusted certificate fails sign-in with ActionWait TLS error: ….

Custom deployments

Override individual endpoint fields; anything left empty falls back to the Commercial Cloud:

cfg := altiumauth.Config{ClientID: "your-client-id", Scopes: "openid profile"}
cfg.AuthEndpoint = "https://auth.my-custom-deployment.example/connect/authorize"
cfg.TokenEndpoint = "https://auth.my-custom-deployment.example/connect/token"

API

Function Description
(Config) SignIn(ctx, SignInOptions) ActionWait browser sign-in (public clients). Options: Timeout (default 3 min), SelectWorkspace, OpenBrowser.
(Config) CreateAuthorizationURL(AuthorizationURLOptions) Build the authorize URL with PKCE; returns URL, State, CodeVerifier. Options: RedirectURI, State, CodeVerifier, Scopes, SelectWorkspace.
(Config) ExchangeCode(ctx, ExchangeCodeParams) authorization_code grant: Code, CodeVerifier, RedirectURI.
(Config) SignIntoWorkspace(ctx, baseAccessToken, workspaceAuthID) Token exchange (RFC 8693) for a workspace-scoped token.
(Config) RefreshToken(ctx, refreshToken) refresh_token grant. Sends no scope, so global stays global and workspace stays workspace. Persist a rotated RefreshToken.
(Config) RevokeRefreshToken(ctx, refreshToken) RFC 7009 revocation at /connect/revocation; idempotent.
GetClientScopes(ctx, httpClient, scopeEndpoint, clientID) Scopes registered for a client (httpClient may be nil). Errors rather than returning [] on a bad response.
CommercialCloudEndpoints, GovCloudEndpoints, AESEndpoints(origin) Endpoint presets.

SelectWorkspace takes SelectWorkspaceStrict, SelectWorkspaceOptional, or SelectWorkspaceNone / empty (omitted) — see login-into-workspace mode.

TokenSet has AccessToken, RefreshToken, IDToken, TokenType, ExpiresIn, ExpiresAt (epoch seconds, computed as now + expires_in − 30), and Scope, plus an Expired() helper. The access token is a signed JWT — see Access token claims.

Pass your own *http.Client in Config.HTTPClient (proxies, custom TLS roots, tracing). Bound calls with the context rather than http.Client.Timeout, which would cut ActionWait long polls short.

Every request carries User-Agent: altium-auth-go/<version> (altiumauth.UserAgent). Keep it if you supply a custom transport, and set it on your own calls to Altium endpoints (e.g. /connect/userinfo).

Error handling

Situation Error
Token endpoint returns an OAuth error *TokenError — StatusCode, Code (e.g. access_denied, invalid_grant), Description, Body
OAuth error delivered via ActionWait (e.g. cross-partition access_denied) *AuthorizationError — Code, Description
Wait token gone (410) or ctx cancelled ErrSignInCancelled (wraps context.Canceled when cancelled)
Sign-in timed out wraps context.DeadlineExceeded
Returned state does not match (CSRF) ErrStateMismatch — the code is not exchanged
TLS failure during ActionWait message ActionWait TLS error:, wrapping the crypto/tls / crypto/x509 error
Missing ClientID/Scopes, invalid endpoint URL, empty token/code argument descriptive error, no request sent
var te *altiumauth.TokenError
if errors.As(err, &te) && te.Code == "invalid_grant" {
	// Refresh token expired or revoked — sign in again.
}

Development

go vet ./...
go test -race ./...   # unit tests + the shared conformance vectors (spec/conformance/vectors.json)
E2E sign-in test

Run the sign-in flow against a live Altium environment. It opens your browser, waits for the ActionWait callback, and prints the resulting tokens. It can also exercise workspace exchange, refresh, and revocation. Its CLI is identical to the TypeScript, .NET, and Python harnesses.

# Commercial Cloud, public client
go run ./tools/signintest YOUR_CLIENT_ID

# GovCloud — verifies the secure=1 two-token model
go run ./tools/signintest --env gov YOUR_GOV_CLIENT_ID

# AES (on-prem) — verifies the origin-derived endpoints, no secure=1
go run ./tools/signintest --env aes --aes-origin https://aes.server.example:9785 YOUR_AES_CLIENT_ID

# Also exchange a workspace token and exercise refresh
go run ./tools/signintest --workspace <authId> --refresh YOUR_CLIENT_ID

# Confidential client (HTTP Basic) — pass the secret via env, never on the CLI
A365_CLIENT_SECRET=... go run ./tools/signintest YOUR_CLIENT_ID

Options include --env prod|gov|aes, --aes-origin (required for --env aes), --workspace-env, --secure/--no-secure, --scopes, --workspace, --select-workspace, --refresh, --userinfo, --revoke, plus --authorize-url/--exchange-code/--code-verifier/--redirect-uri for confidential (custom-callback) clients — see the header of tools/signintest/main.go. The client secret is read from A365_CLIENT_SECRET so it never appears in shell history or the process list.

Releasing

Go modules are published by tagging; there is no registry upload. The module lives in a subdirectory, so the tag carries the path: libs/go/vX.Y.Z. Pushing it runs .github/workflows/go-release.yml, which gates on the conformance suite and then registers the version with proxy.golang.org (and so pkg.go.dev).

License

MIT — see LICENSE.

Documentation

Overview

Package altiumauth is the Altium 365 OAuth2 / OpenID Connect authentication library for Go.

It supports both client types, and Commercial Cloud, GovCloud, and AES (on-prem) deployments:

Only Config.ClientID and Config.Scopes are required; the endpoints default to CommercialCloudEndpoints. Use GovCloudEndpoints for GovCloud — the library detects the Gov token endpoint and sends secure=1 automatically — or AESEndpoints for an AES installation.

Storage is the caller's concern: the functions return a TokenSet and never persist it. The package has no dependencies outside the standard library.

The behavior is specified by the language-neutral spec at https://github.com/AltiumDeveloper/altium-auth/blob/main/spec/SPEC.md and verified by the shared conformance vectors.

Index

Examples

Constants

View Source
const DefaultSignInTimeout = 3 * time.Minute

DefaultSignInTimeout bounds the whole ActionWait reconnect loop when SignInOptions.Timeout is zero.

Variables

View Source
var CommercialCloudEndpoints = Endpoints{
	AuthEndpoint:       "https://auth.altium.com/connect/authorize",
	TokenEndpoint:      "https://auth.altium.com/connect/token",
	ActionWaitEndpoint: "https://actionwait.altium.com/await",
	RedirectURI:        "https://auth.altium.com/api/AuthComplete",
}

CommercialCloudEndpoints are the Altium 365 Commercial Cloud (non-Gov) endpoints. They are the defaults for any endpoint a Config omits.

View Source
var ErrSignInCancelled = errors.New("altiumauth: Sign-in cancelled")

ErrSignInCancelled is returned by SignIn when the ActionWait wait token is no longer valid (410: consumed or superseded) or the caller's context is cancelled. Restart sign-in with a new token.

View Source
var ErrStateMismatch = errors.New("altiumauth: State mismatch during sign-in (possible CSRF attack)")

ErrStateMismatch is returned by SignIn when the state delivered with the authorization code does not match the one it generated (CSRF guard). The code is not exchanged.

View Source
var GovCloudEndpoints = Endpoints{
	AuthEndpoint:       "https://auth.365-gov.altium.com/connect/authorize",
	TokenEndpoint:      "https://auth.365-gov.altium.com/connect/token",
	ActionWaitEndpoint: "https://actionwait.altium.com/await",
	RedirectURI:        "https://auth.altium.com/api/AuthComplete",
}

GovCloudEndpoints are the Altium 365 GovCloud (Production) endpoints. The library detects the Gov token endpoint and adds secure=1 to token requests automatically.

Only AuthEndpoint and TokenEndpoint are Gov-specific. ActionWait has no Gov DNS: both its poll endpoint and its AuthComplete callback are the Commercial Cloud hosts for the tier (a Gov-host callback is not registered and won't authorize).

View Source
var UserAgent = "altium-auth-go/" + moduleVersion()

UserAgent is the User-Agent header sent on every request, e.g. "altium-auth-go/v0.2.0", so Altium services can identify the client.

Functions

func GetClientScopes

func GetClientScopes(ctx context.Context, httpClient *http.Client, scopeEndpoint, clientID string) ([]string, error)

GetClientScopes fetches the scopes registered for a client from the ClientScopes endpoint ({base}/api/ClientScopes). The endpoint itself answers an empty list for an unknown client ID. Anything else unexpected — a non-200 status, or a body that is not a JSON array of strings — is an error: an empty list is a meaningful answer, so a failed lookup must not look like one.

On Commercial/GovCloud this returns only the client's static scopes (e.g. openid, profile). On an AES installation, which hosts exactly one workspace, it also includes that workspace's a365:workspace:{id} scope.

httpClient may be nil to use http.DefaultClient.

Types

type AuthorizationError

type AuthorizationError struct {
	Code        string
	Description string
}

AuthorizationError is an OAuth error delivered through the ActionWait callback instead of an authorization code (e.g. "access_denied" when a workspace scope is requested at sign-in on the wrong partition).

func (*AuthorizationError) Error

func (e *AuthorizationError) Error() string

type AuthorizationRequest

type AuthorizationRequest struct {
	// URL is the authorization URL to send the user's browser to.
	URL string
	// State must be persisted and compared with the callback's state (CSRF).
	State string
	// CodeVerifier must be persisted and passed to ExchangeCode.
	CodeVerifier string
}

AuthorizationRequest is a prepared authorization request: where to send the user, and what to keep for the callback.

type AuthorizationURLOptions

type AuthorizationURLOptions struct {
	// RedirectURI overrides Config.RedirectURI (e.g. your own callback route).
	RedirectURI string
	// State overrides the generated state (default: a random UUID).
	State string
	// CodeVerifier overrides the generated PKCE code verifier.
	CodeVerifier string
	// Scopes overrides Config.Scopes for this request.
	Scopes string
	// SelectWorkspace enables login-into-workspace mode. Empty or
	// SelectWorkspaceNone omits the parameter.
	SelectWorkspace WorkspaceSelection
}

AuthorizationURLOptions customizes Config.CreateAuthorizationURL. The zero value uses the configured redirect URI and scopes, and a fresh state and PKCE verifier.

type Config

type Config struct {
	// ClientID is the OAuth2 client ID registered with the Altium identity
	// provider.
	ClientID string

	// Scopes is the space-delimited list of scopes to request (must include
	// "openid profile").
	Scopes string

	// ClientSecret makes this a confidential client (e.g. a backend): token and
	// revocation requests then authenticate with HTTP Basic. Leave it empty for
	// public clients (desktop/native), which rely on PKCE. Never embed a secret
	// in a public client.
	ClientSecret string

	// Secure overrides GovCloud detection. By default secure=1 is sent on token
	// requests when TokenEndpoint is a Gov host and omitted otherwise, so leave
	// this nil. Set it only to force behavior for a non-standard host.
	Secure *bool

	// Endpoints holds the endpoint URLs; empty fields default to
	// CommercialCloudEndpoints.
	Endpoints

	// HTTPClient is used for every request. Defaults to http.DefaultClient.
	// Bound request lifetimes with the context passed to each call rather than
	// Client.Timeout, which would cut ActionWait long polls short.
	HTTPClient *http.Client
}

Config is the OAuth2 client configuration. Only ClientID and Scopes are required; every endpoint defaults to CommercialCloudEndpoints. Set Endpoints to GovCloudEndpoints or AESEndpoints for those deployments, or override individual fields for a custom one. Any endpoint that is set must be an absolute URL.

func (Config) CreateAuthorizationURL

func (c Config) CreateAuthorizationURL(opts AuthorizationURLOptions) (AuthorizationRequest, error)

CreateAuthorizationURL builds an OAuth2 authorization URL with PKCE for the redirect-based (authorization code) flow used by web/server apps that host their own redirect endpoint. It does no network I/O.

Redirect the user to the returned URL and persist State and CodeVerifier (e.g. in the session). On the callback, verify the returned state matches, then call Config.ExchangeCode with the code and the stored verifier.

Example

A web/server app (confidential client) uses the redirect flow.

package main

import (
	"net/http"

	altiumauth "github.com/AltiumDeveloper/altium-auth/libs/go"
)

func main() {
	cfg := altiumauth.Config{
		ClientID:     "your-client-id",
		ClientSecret: "your-client-secret", // confidential client → HTTP Basic
		Scopes:       "openid profile offline_access",
	}
	redirectURI := "https://my-service.example.com/oauth/callback"

	http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		authz, err := cfg.CreateAuthorizationURL(altiumauth.AuthorizationURLOptions{RedirectURI: redirectURI})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		// Stash authz.State and authz.CodeVerifier in the user's session, then:
		http.Redirect(w, r, authz.URL, http.StatusFound)
	})

	http.HandleFunc("/oauth/callback", func(w http.ResponseWriter, r *http.Request) {
		state, verifier := "from-session", "from-session"
		if r.URL.Query().Get("state") != state {
			http.Error(w, "state mismatch", http.StatusBadRequest)
			return
		}
		tokens, err := cfg.ExchangeCode(r.Context(), altiumauth.ExchangeCodeParams{
			Code:         r.URL.Query().Get("code"),
			CodeVerifier: verifier,
			RedirectURI:  redirectURI,
		})
		if err != nil {
			http.Error(w, err.Error(), http.StatusBadGateway)
			return
		}
		_ = tokens // persist, then discover/exchange workspaces as usual
	})
}

func (Config) ExchangeCode

func (c Config) ExchangeCode(ctx context.Context, p ExchangeCodeParams) (*TokenSet, error)

ExchangeCode exchanges an authorization code (from your redirect callback) for tokens via the authorization_code grant. Pair it with Config.CreateAuthorizationURL for the redirect-based flow.

func (Config) RefreshToken

func (c Config) RefreshToken(ctx context.Context, refreshToken string) (*TokenSet, error)

RefreshToken exchanges a refresh token for a fresh TokenSet. No scope is sent, so the grant keeps its original scope (a global token stays global, a workspace token stays workspace-scoped). If the response carries a new refresh token (rotation), persist it and discard the old one.

Example

Refresh an expired access token, and handle a revoked or expired refresh token by signing in again.

package main

import (
	"context"
	"errors"
	"log"

	altiumauth "github.com/AltiumDeveloper/altium-auth/libs/go"
)

func main() {
	cfg := altiumauth.Config{ClientID: "your-client-id", Scopes: "openid profile offline_access"}
	tokens := &altiumauth.TokenSet{ /* loaded from storage */ }

	if tokens.Expired() && tokens.RefreshToken != "" {
		refreshed, err := cfg.RefreshToken(context.Background(), tokens.RefreshToken)
		var te *altiumauth.TokenError
		if errors.As(err, &te) && te.Code == "invalid_grant" {
			// Refresh token expired or revoked: sign in again.
		} else if err != nil {
			log.Fatal(err)
		}
		_ = refreshed // persist, including a rotated refresh token
	}
}

func (Config) RevokeRefreshToken

func (c Config) RevokeRefreshToken(ctx context.Context, refreshToken string) error

RevokeRefreshToken revokes a refresh token at the revocation endpoint (RFC 7009), derived from the token endpoint (/connect/token → /connect/revocation). Only refresh tokens can be revoked. The call is idempotent: the endpoint answers 200 for known and unknown tokens alike. After revoking, RefreshToken with the same token fails with invalid_grant.

func (Config) SignIn

func (c Config) SignIn(ctx context.Context, opts SignInOptions) (*TokenSet, error)

SignIn performs an OAuth2 PKCE sign-in over Altium's ActionWait long poll — the flow for public clients (desktop/CLI) that cannot host a redirect:

  1. Generate a PKCE verifier and a connection token, used as the OAuth state.
  2. Start the ActionWait long poll, then open the browser to the authorize URL.
  3. Wait for the code, verify the state (CSRF), and exchange it for tokens.

Cancel ctx to abandon sign-in early (ErrSignInCancelled). Persisting the returned TokenSet is the caller's concern. For redirect-based apps, use Config.CreateAuthorizationURL and Config.ExchangeCode instead.

Example

A desktop or CLI app (public client) signs in through the browser over ActionWait, then exchanges the global token for a workspace token.

package main

import (
	"context"
	"fmt"
	"log"

	altiumauth "github.com/AltiumDeveloper/altium-auth/libs/go"
)

func main() {
	cfg := altiumauth.Config{ClientID: "your-client-id", Scopes: "openid profile offline_access"}
	ctx := context.Background()

	tokens, err := cfg.SignIn(ctx, altiumauth.SignInOptions{})
	if err != nil {
		log.Fatal(err)
	}
	// Persist tokens yourself (keychain, file, …).

	ws, err := cfg.SignIntoWorkspace(ctx, tokens.AccessToken, "workspace-auth-id")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(ws.AccessToken != "")
}

func (Config) SignIntoWorkspace

func (c Config) SignIntoWorkspace(ctx context.Context, baseAccessToken, workspaceAuthID string) (*TokenSet, error)

SignIntoWorkspace exchanges a global access token (from SignIn or ExchangeCode) for a workspace-scoped token via the token-exchange grant (RFC 8693). Point TokenEndpoint at the Gov host to exchange a Commercial token for a Gov workspace token (secure=1 is added automatically).

type Endpoints

type Endpoints struct {
	// AuthEndpoint is the authorization endpoint (/connect/authorize).
	AuthEndpoint string
	// TokenEndpoint is the token endpoint (/connect/token). A Gov host here
	// switches on secure=1 for every token request.
	TokenEndpoint string
	// ActionWaitEndpoint is the ActionWait long-poll endpoint used by SignIn.
	ActionWaitEndpoint string
	// RedirectURI is the fixed redirect URI registered with the auth client.
	RedirectURI string
	// ScopeEndpoint is the client scope introspection endpoint
	// ({base}/api/ClientScopes). Optional; see GetClientScopes.
	ScopeEndpoint string
}

Endpoints is the set of URLs a client talks to. Any field left empty in a Config falls back to CommercialCloudEndpoints.

func AESEndpoints

func AESEndpoints(origin string) Endpoints

AESEndpoints derives the endpoints of an AES (on-prem) installation from its server origin (scheme + host, plus port if non-default), e.g. AESEndpoints("https://aes.example.com:9785").

AES hosts its own ActionWait and AuthComplete callback (unlike Gov, which shares Commercial's) and never uses secure=1 (same rule as Commercial).

Example

AES (on-prem): derive the endpoints from the server origin, discover the installation's workspace scope, and request it at sign-in (one trip).

package main

import (
	"context"
	"fmt"
	"log"

	altiumauth "github.com/AltiumDeveloper/altium-auth/libs/go"
)

func main() {
	ctx := context.Background()
	endpoints := altiumauth.AESEndpoints("https://aes.server.example:9785")
	scopes, err := altiumauth.GetClientScopes(ctx, nil, endpoints.ScopeEndpoint, "your-aes-client-id")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(scopes)
}

type ExchangeCodeParams

type ExchangeCodeParams struct {
	// Code is the authorization code delivered to your redirect callback.
	Code string
	// CodeVerifier is the PKCE verifier from CreateAuthorizationURL
	// (recommended).
	CodeVerifier string
	// RedirectURI must match the one used in the authorize request. Defaults to
	// Config.RedirectURI.
	RedirectURI string
}

ExchangeCodeParams are the inputs to Config.ExchangeCode.

type SignInOptions

type SignInOptions struct {
	// Timeout bounds the whole ActionWait wait (the user's time to sign in),
	// across reconnects. Default: DefaultSignInTimeout.
	Timeout time.Duration

	// SelectWorkspace enables login-into-workspace mode (SPEC §3.1). See
	// AuthorizationURLOptions.SelectWorkspace.
	SelectWorkspace WorkspaceSelection

	// OpenBrowser overrides how the authorization URL is opened (e.g. a host
	// application's own API). By default the library launches the OS browser
	// and prints the URL to stderr for manual copy-and-paste. An error aborts
	// sign-in.
	OpenBrowser func(url string) error
}

SignInOptions customizes Config.SignIn.

type TokenError

type TokenError struct {
	StatusCode  int
	Code        string // OAuth "error"; empty if the body was not an OAuth error
	Description string // OAuth "error_description"; may be empty
	Body        string // raw response body (truncated)
}

TokenError is a non-success response from the token endpoint. Code carries the OAuth error (e.g. "access_denied" for a cross-partition workspace exchange, "invalid_grant" for an expired or revoked refresh token).

func (*TokenError) Error

func (e *TokenError) Error() string

type TokenSet

type TokenSet struct {
	AccessToken  string `json:"access_token"`
	RefreshToken string `json:"refresh_token,omitempty"`
	IDToken      string `json:"id_token,omitempty"`
	TokenType    string `json:"token_type,omitempty"`
	ExpiresIn    int64  `json:"expires_in,omitempty"`
	// ExpiresAt is the epoch second at which the access token expires,
	// computed by the library as now + expires_in - 30 (clock-skew buffer).
	ExpiresAt int64  `json:"expires_at,omitempty"`
	Scope     string `json:"scope,omitempty"`
}

TokenSet is the token endpoint response (RFC 6749 / OIDC).

func (*TokenSet) Expired

func (t *TokenSet) Expired() bool

Expired reports whether the access token has expired (per ExpiresAt). A TokenSet without an expiry is never reported as expired.

type WorkspaceSelection

type WorkspaceSelection string

WorkspaceSelection controls whether the user is prompted to select a workspace during authorization (login-into-workspace mode, SPEC §3.1).

const (
	// SelectWorkspaceNone skips workspace selection; the flow issues a global
	// access token. Equivalent to leaving the option empty.
	SelectWorkspaceNone WorkspaceSelection = "none"
	// SelectWorkspaceStrict makes workspace selection mandatory; the issued
	// token is workspace-scoped, so no separate exchange is needed.
	SelectWorkspaceStrict WorkspaceSelection = "strict"
	// SelectWorkspaceOptional offers workspace selection; the user may skip it.
	SelectWorkspaceOptional WorkspaceSelection = "optional"
)

Directories

Path Synopsis
tools
signintest command
Command signintest verifies sign-in end to end against a live Altium environment.
Command signintest verifies sign-in end to end against a live Altium environment.

Jump to

Keyboard shortcuts

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