Documentation
¶
Index ¶
- Constants
- func BodyReader(body []byte) io.ReadCloser
- func BufferRequestBody(req *http.Request) ([]byte, error)
- func PostOAuthToken(ctx context.Context, httpClient *http.Client, coreURL string, form url.Values) (accessToken string, expiresIn int, err error)
- func TokenExchangeForm(subjectToken, audience, scope string) url.Values
- type OAuthError
Constants ¶
const ( GrantTypeTokenExchange = "urn:ietf:params:oauth:grant-type:token-exchange" //nolint:gosec // G101: an OAuth grant_type URN, not a credential TokenTypeAccessToken = "urn:ietf:params:oauth:token-type:access_token" //nolint:gosec // G101: an RFC 8693 token-type URN, not a credential )
RFC 8693 grant + token-type URNs. Re-export the literals so callers composing /oauth/token forms don't keep parallel copies. Lifted out of core/repoadmin and core/api during COR-337 cleanup.
const OAuthClientID = "entire-cli"
OAuthClientID is the public OAuth client_id the CLI identifies as on /oauth/token. Lifted into Basic auth by PostOAuthToken.
Variables ¶
This section is empty.
Functions ¶
func BodyReader ¶
func BodyReader(body []byte) io.ReadCloser
BodyReader wraps a buffered request body so http.Request.Body / GetBody can replay it across a retry. Pair with BufferRequestBody.
func BufferRequestBody ¶
BufferRequestBody reads the request body once so a fallback retry can replay it. http.NoBody (and nil) short-circuits — both signal "no body" but only the latter is a runtime nil, so the explicit identity check keeps the cloned request's Content-Length correct on the wire. Returns (nil, nil) for no-body requests; the caller can safely forward without replay state.
func PostOAuthToken ¶
func PostOAuthToken(ctx context.Context, httpClient *http.Client, coreURL string, form url.Values) (accessToken string, expiresIn int, err error)
PostOAuthToken posts a form-encoded request to coreURL+"/oauth/token" and parses the standard {access_token, expires_in} response. Callers build the form (grant_type, subject_token, audience, etc.) so the helper stays neutral about which OAuth grant is being exercised.
If the form carries client_id (and optionally client_secret), the helper lifts both into an HTTP Basic Authorization header and drops them from the form body. zitadel/oidc's token endpoint reads client credentials only from Basic auth, so form-only client_id produces invalid_client even when the form is otherwise well-formed. Both values are url.QueryEscaped per RFC 6749 §2.3.1 because pkg/op QueryUnescapes them on the other side — a raw '+'/'%xx' would round-trip to a different value and fail invalid_client (matches core/api/token_endpoint.go).
coreURL must already be trimmed of any trailing slash. A non-200 response is surfaced as *OAuthError (RFC 6749 defines token-endpoint success as 200 only); transport and decode failures are wrapped plain errors.
func TokenExchangeForm ¶ added in v0.8.0
TokenExchangeForm builds the RFC 8693 token-exchange form every CLI exchange POSTs to /oauth/token: subjectToken (the login JWT) is traded for an access token pinned to audience with the given scope. The one form shape serves repo-scoped and jurisdiction tokens alike — only audience and scope differ — so keep call sites on this builder rather than open-coding the fields.
Types ¶
type OAuthError ¶
type OAuthError struct {
Status int
Code string // RFC 6749 `error` code from the response body; "" when not present
Description string // RFC 6749 `error_description` from the response body; "" when not present
Body string
}
OAuthError is returned by PostOAuthToken when the OAuth endpoint responds with a non-200 status. Callers can errors.As it to surface status-specific UX (e.g. a friendly 403 message) or branch on the RFC 6749 error code.
func (*OAuthError) Error ¶
func (e *OAuthError) Error() string