fapi

package module
v0.15.0 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: MIT Imports: 5 Imported by: 0

README

FAPIgo

Release CI FAPI2 Conformance

Hardened, separately conformant FAPI 2.0 client, authorization-server and resource-server engines for Go, built on one rigorously tested protocol core.

⚠ Work in progress. FAPIgo is under active development. APIs, package structure, and behavior may change without notice. We recommend waiting for the v1.0 release before considering it for production use.

Status: all three roles (client, server, resource), the shared internal protocol core, keys, storage (including a reusable storage contract test suite for downstream backends), extension, the hardened fapihttp transport, client-side AS discovery (client.Discover + keys.NewJWKSIssuerKeySource) and the fapitest real-HTTP interop harness are all implemented and covered by tests, including end-to-end authorization flows — both hand-configured and fully discovery-driven — under the FAPI 2.0 baseline and message-signing profiles. Both the server (authorization server) and client (relying party) roles have been run clean against the OpenID Foundation conformance suite's FAPI2 baseline and message-signing test plans, run under server's default JWT access-token format; the opaque alternative is covered by unit/integration tests and cmd/conformance-as's own smoke test under both formats, not by a continuous live-suite run — see conformance/. resource has not been run against a dedicated OIDF plan (only indirectly, as a stand-in the AS plan's own happy-flow module calls).

import (
    "github.com/idfoundry/fapigo/client"
    "github.com/idfoundry/fapigo/server"
    "github.com/idfoundry/fapigo/resource"
)

client (relying party), server (authorization server) and resource (resource server / token verification) are independent public packages with distinct constructors, configuration and workflow APIs — there is no generic API that tries to behave as more than one role. They share a rigorously tested internal protocol core (JOSE, DPoP, PAR, PKCE, JARM, request objects, client assertions, canonicalization) without sharing role-level types or behaviour.

storage/memstore and keys/ephemeral provide in-memory, non-durable implementations of every interface server needs (client repository, transaction/grant/replay/access-token stores, key manager, client key source) — for local development and testing only, never production — so integrating server doesn't require writing real persistence and key management from scratch just to see it run. server.RecommendedLimits() and server.RecommendedAlgorithms() do the same for Config's algorithm and duration fields, each grounded in a specific FAPI 2.0 Security Profile Final or RFC 9449 requirement where one exists — see GETTING_STARTED.md for a walkthrough of standing up an authorization server and resource server end to end.

Access tokens can be issued as self-contained JWTs (RFC 9068 — the default, server.JWTAccessTokens/resource.JWTAccessTokens) or as opaque, storage-backed values (server.OpaqueAccessTokens/ resource.OpaqueAccessTokens) — FAPI 2.0 doesn't mandate a format, so this is a deployment's own choice, not something the library imposes.

See ARCHITECTURE.md for the full design rationale and package layout, and conformance/ for how each role is tested against the OpenID Foundation conformance suite.

Relevant specifications

License

MIT — see LICENSE.

Documentation

Overview

Package fapi holds value types shared across the client, server and resource packages because their semantics are identical regardless of role: identifiers, algorithm enums and other data with a single, stable meaning on the wire. This includes Secret (a self-redacting wrapper for any token, code or credential value — String, GoString and MarshalText all redact; Reveal is the only way out) and URL (constructed only via ParseIssuerURL/ParseEndpointURL, never a bare string, enforcing HTTPS, no fragment, no embedded credentials and a normalized host).

It must never hold workflow types, configuration, or anything whose meaning differs between an authorization request a client is about to send and one a server has already validated. Those stay in their respective role packages — see ARCHITECTURE.md, "Shared public value types only where semantics match".

Index

Constants

This section is empty.

Variables

View Source
var ErrSecretSerialization = errors.New("fapi: secret values cannot be marshaled")

ErrSecretSerialization is returned by Secret.MarshalText, so a Secret can never be silently serialized (into a log line, a JSON response, a debug dump) by generic tooling that doesn't know it's handling one.

Functions

This section is empty.

Types

type ClientID

type ClientID string

ClientID identifies an OAuth client. It has one meaning regardless of role — the client-generated instruction and the server-validated record both refer to the same client by the same ID.

func (ClientID) String

func (id ClientID) String() string

String returns the wire value of id.

type ContentEncryptionAlgorithm added in v0.4.0

type ContentEncryptionAlgorithm uint8

ContentEncryptionAlgorithm is a closed set of JWE content-encryption algorithms this module supports — how the payload itself is encrypted once a CEK has been established (RFC 7518 §5).

const (

	// A256GCM is AES-256 in Galois/Counter Mode (RFC 7518 §5.3), an AEAD
	// cipher — no separate integrity algorithm is layered on top the way
	// the CBC-HMAC family requires.
	A256GCM ContentEncryptionAlgorithm

	// A256CBCHS512 is AES_256_CBC_HMAC_SHA_512 (RFC 7518 §5.2.3): a
	// 64-octet CEK split into a 32-octet HMAC-SHA-512 key and a
	// 32-octet AES-256 key, PKCS #7-padded CBC encryption under a
	// 128-bit IV, and an authentication tag computed as HMAC-SHA-512
	// over AAD || IV || ciphertext || AL (AL being the AAD's bit length
	// as a 64-bit big-endian integer), truncated to the first 32
	// octets. Unlike A256GCM, this is encrypt-then-MAC rather than a
	// single AEAD primitive, so the tag must be verified before the
	// ciphertext is ever decrypted or unpadded — see internal/jwe's own
	// implementation notes for why that ordering is load-bearing, not
	// stylistic.
	A256CBCHS512
)

func ParseContentEncryptionAlgorithm added in v0.4.0

func ParseContentEncryptionAlgorithm(enc string) (ContentEncryptionAlgorithm, error)

ParseContentEncryptionAlgorithm maps a JOSE "enc" header value to a ContentEncryptionAlgorithm. It rejects every value outside the closed set this module supports, for the same reason ParseKeyManagementAlgorithm does.

func (ContentEncryptionAlgorithm) IsValid added in v0.4.0

func (a ContentEncryptionAlgorithm) IsValid() bool

IsValid reports whether a is one of the algorithms this module supports.

func (ContentEncryptionAlgorithm) String added in v0.4.0

String returns the JOSE "enc" header value for a, or "" if a is not a recognized algorithm.

type KeyManagementAlgorithm added in v0.4.0

type KeyManagementAlgorithm uint8

KeyManagementAlgorithm is a closed set of JWE key-management algorithms this module supports — how the content-encryption key (CEK) for one encrypted token is delivered to its recipient (RFC 7518 §4). As with SignatureAlgorithm, a JOSE "alg" header is untrusted input: a caller states which KeyManagementAlgorithm it expects before a JWE is processed, rather than trusting whatever the header claims.

const (

	// RSAOAEP256 is RSAES OAEP using SHA-256 and MGF1 with SHA-256
	// (RFC 7518 §4.3), with a minimum 2048-bit modulus — the same
	// modulus floor PS256 already requires. Go's standard library
	// implements OAEP directly (crypto/rsa.EncryptOAEP/DecryptOAEP), so
	// this algorithm needs no hand-rolled cryptographic primitives.
	RSAOAEP256 KeyManagementAlgorithm

	// ECDHESA256KW is ECDH-ES using Concat KDF to derive a key-wrapping
	// key, which then wraps the CEK with AES-256 Key Wrap (RFC 7518
	// §4.6-4.7, RFC 3394). Unlike RSAOAEP256, this module implements
	// both the Concat KDF and AES Key Wrap itself — neither is provided
	// by Go's standard library — so an EC-only deployment (no RSA key
	// management infrastructure) has a supported option.
	ECDHESA256KW
)

func ParseKeyManagementAlgorithm added in v0.4.0

func ParseKeyManagementAlgorithm(alg string) (KeyManagementAlgorithm, error)

ParseKeyManagementAlgorithm maps a JOSE "alg" header value to a KeyManagementAlgorithm. It rejects every value outside the closed set this module supports, including algorithms that are valid JOSE algorithms in general (e.g. "RSA-OAEP", "ECDH-ES", "dir"), for the same reason ParseSignatureAlgorithm does: accepting one outside this module's own closed set would silently downgrade the guarantees the rest of this module assumes.

func (KeyManagementAlgorithm) IsValid added in v0.4.0

func (a KeyManagementAlgorithm) IsValid() bool

IsValid reports whether a is one of the algorithms this module supports.

func (KeyManagementAlgorithm) String added in v0.4.0

func (a KeyManagementAlgorithm) String() string

String returns the JOSE "alg" header value for a, or "" if a is not a recognized algorithm.

type RegisteredRedirectURI

type RegisteredRedirectURI string

RegisteredRedirectURI is a redirect URI exactly as registered for a client. Equal performs OAuth registration-semantics comparison — exact string equality, no normalization, no wildcard matching — never generic URL equivalence. A candidate redirect_uri that differs only in trailing slash, percent-encoding case, or default-port presence is not a match.

func (RegisteredRedirectURI) Equal

func (r RegisteredRedirectURI) Equal(candidate string) bool

Equal reports whether candidate is exactly this registered redirect URI.

func (RegisteredRedirectURI) String

func (r RegisteredRedirectURI) String() string

String returns the wire value of r.

type Secret

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

Secret wraps a value — a token, code, or credential — that must never leak into a log line, error message or debug dump by accident. String, GoString and MarshalText all redact; Reveal is the only way to get the raw value back out, so a caller has to opt in explicitly at the point it's actually needed (building a header, an HTTP body).

func NewSecret

func NewSecret(value string) Secret

NewSecret wraps value as a Secret.

func (Secret) GoString

func (s Secret) GoString() string

GoString implements fmt.GoStringer so %#v also redacts.

func (Secret) MarshalText

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

MarshalText always fails, so a Secret embedded in a struct can't be silently serialized by encoding/json or encoding/xml.

func (Secret) Reveal

func (s Secret) Reveal() string

Reveal returns the wrapped value.

func (Secret) String

func (s Secret) String() string

String returns a fixed redacted placeholder, never the wrapped value.

type SignatureAlgorithm

type SignatureAlgorithm uint8

SignatureAlgorithm is a closed set of JWS signature algorithms this module supports. It exists so a JWT/JWS "alg" header — untrusted input — is never treated as policy: callers state which SignatureAlgorithm they expect before a signature is checked, rather than trusting whatever the token header claims.

const (

	// ES256 is ECDSA using the P-256 curve and SHA-256 (RFC 7518 §3.4).
	ES256 SignatureAlgorithm

	// PS256 is RSASSA-PSS using SHA-256 and MGF1 with SHA-256
	// (RFC 7518 §3.5), with a minimum 2048-bit modulus.
	PS256

	// EdDSA is pure EdDSA using the Ed25519 variant (RFC 8037 §3.1) —
	// the third algorithm FAPI 2.0 Security Profile Final §5.4.1 item
	// 1.b permits, alongside ES256 and PS256. Unlike those two, EdDSA
	// signs the JWS Signing Input directly rather than a digest of it
	// (RFC 8037 §3.1: "the JWS Signing Input (as message)") — this
	// module's own SignatureAlgorithm doesn't encode that difference
	// itself, but every caller that produces or checks a signature
	// (internal/jose, keys.KeyManager) must not pre-hash for this
	// algorithm the way it does for ES256/PS256.
	EdDSA
)

func ParseSignatureAlgorithm

func ParseSignatureAlgorithm(alg string) (SignatureAlgorithm, error)

ParseSignatureAlgorithm maps a JOSE "alg" header value to a SignatureAlgorithm. It rejects every value outside the closed set this module supports, including algorithms that are valid JOSE algorithms in general — "none", "HS256", "RS256" and so on — since accepting one of those would silently downgrade the sender-constraint and integrity guarantees the rest of this module assumes.

func (SignatureAlgorithm) IsValid

func (a SignatureAlgorithm) IsValid() bool

IsValid reports whether a is one of the algorithms this module supports.

func (SignatureAlgorithm) String

func (a SignatureAlgorithm) String() string

String returns the JOSE "alg" header value for a, or "" if a is not a recognized algorithm.

type URL

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

URL is a validated, security-sensitive URL — an issuer identifier or an endpoint. It can only be constructed via ParseIssuerURL or ParseEndpointURL, which enforce: absolute, HTTPS (except an explicitly enabled loopback development exception), no embedded credentials, no fragment, and a normalized (lowercased) scheme and host.

func ParseEndpointURL

func ParseEndpointURL(raw string, opts ...URLOption) (URL, error)

ParseEndpointURL parses and validates raw as an endpoint URL (e.g. a PAR, authorization or token endpoint).

func ParseIssuerURL

func ParseIssuerURL(raw string, opts ...URLOption) (URL, error)

ParseIssuerURL parses and validates raw as an issuer identifier.

func (URL) IsZero

func (u URL) IsZero() bool

IsZero reports whether u is the unset zero value.

func (URL) String

func (u URL) String() string

String returns the URL's string form.

func (URL) URL

func (u URL) URL() url.URL

URL returns a copy of the underlying net/url.URL.

func (URL) WithQuery

func (u URL) WithQuery(query url.Values) URL

WithQuery returns a copy of u with its query string replaced by query — for appending caller-supplied parameters (e.g. request_uri, client_id) to an already-validated endpoint URL. It does not re-validate scheme, credentials or fragment, since replacing only the query string cannot reintroduce a problem ParseEndpointURL already ruled out on u; this is what lets a caller build on an endpoint URL parsed under AllowLoopbackHTTP() without having to know that option applied to reconstruct the result.

type URLOption

type URLOption func(*urlOptions)

URLOption configures ParseIssuerURL or ParseEndpointURL.

func AllowLoopbackHTTP

func AllowLoopbackHTTP() URLOption

AllowLoopbackHTTP permits an http:// scheme when the host is a loopback address ("localhost", 127.0.0.0/8, or ::1). It exists for local development only and must never be enabled from configuration that could reach a production deployment by accident.

Directories

Path Synopsis
Package client implements the FAPI 2.0 relying-party (RP) role: the public API a client application uses to drive an authorization-code flow against a FAPI-conformant authorization server.
Package client implements the FAPI 2.0 relying-party (RP) role: the public API a client application uses to drive an authorization-code flow against a FAPI-conformant authorization server.
cmd
conformance-as command
Command conformance-as is a standalone FAPI 2.0 authorization server, exposing the server package's PAR/authorize/token/JWKS/metadata endpoints over real HTTPS with a minimal HTML consent page.
Command conformance-as is a standalone FAPI 2.0 authorization server, exposing the server package's PAR/authorize/token/JWKS/metadata endpoints over real HTTPS with a minimal HTML consent page.
conformance-client command
Command conformance-client drives this module's client package through every module of a FAPI2 relying-party conformance test plan against a locally running OIDF conformance suite, entirely headlessly: it plays both the RP under test (via the client package) and the "browser" that carries requests between the RP and the suite's mock authorization server, since neither role here needs a human or a real browser — the suite's mock AS doesn't render an interactive consent page for a private_key_jwt+DPoP RP test, and this driver's own HTTP client can intercept the authorization redirect itself.
Command conformance-client drives this module's client package through every module of a FAPI2 relying-party conformance test plan against a locally running OIDF conformance suite, entirely headlessly: it plays both the RP under test (via the client package) and the "browser" that carries requests between the RP and the suite's mock authorization server, since neither role here needs a human or a real browser — the suite's mock AS doesn't render an interactive consent page for a private_key_jwt+DPoP RP test, and this driver's own HTTP client can intercept the authorization redirect itself.
conformance
server/scripts/generate-client-key command
Command generate-client-key produces one throwaway ES256 keypair for the OIDF conformance suite's test client to authenticate with (private_key_jwt client assertions, DPoP proofs, and — under the message-signing profile — signed request objects).
Command generate-client-key produces one throwaway ES256 keypair for the OIDF conformance suite's test client to authenticate with (private_key_jwt client assertions, DPoP proofs, and — under the message-signing profile — signed request objects).
server/scripts/setup-config command
Command setup-config bootstraps everything a fresh clone needs to run the AS-side conformance suites (conformance/scripts/run-all.sh's "AS baseline" and "AS message-signing" legs) that isn't already committed to this repo.
Command setup-config bootstraps everything a fresh clone needs to run the AS-side conformance suites (conformance/scripts/run-all.sh's "AS baseline" and "AS message-signing" legs) that isn't already committed to this repo.
Package extension lets a single definition of a custom authorization parameter — including a Rich Authorization Requests (RFC 9396) detail type — be shared between client and server, so its wire name, cardinality, encoding, size limit, sensitivity and validation rules are implemented exactly once instead of twice with subtly different rules.
Package extension lets a single definition of a custom authorization parameter — including a Rich Authorization Requests (RFC 9396) detail type — be shared between client and server, so its wire name, cardinality, encoding, size limit, sensitivity and validation rules are implemented exactly once instead of twice with subtly different rules.
Package fapihttp provides the hardened HTTP transport used internally by client, server and resource: strict TLS verification, response-size limits, bounded (or disabled) redirects, endpoint origin validation, connection and body-read deadlines, SSRF restrictions for discovery and JWKS fetches, and content-type checks.
Package fapihttp provides the hardened HTTP transport used internally by client, server and resource: strict TLS verification, response-size limits, bounded (or disabled) redirects, endpoint origin validation, connection and body-read deadlines, SSRF restrictions for discovery and JWKS fetches, and content-type checks.
Package fapitest is an in-process interoperability harness that wires a client.Client, server.Server and resource.Verifier together over real HTTP (via httptest.Server) to run end-to-end flows in tests.
Package fapitest is an in-process interoperability harness that wires a client.Client, server.Server and resource.Verifier together over real HTTP (via httptest.Server) to run end-to-end flows in tests.
internal
canonical
Package canonical implements shared canonicalization rules — URL/URI canonicalization, JSON canonicalization where required for signature input, and parameter-ordering/normalization — so client, server and resource agree byte-for-byte on what they are signing, verifying or comparing.
Package canonical implements shared canonicalization rules — URL/URI canonicalization, JSON canonicalization where required for signature input, and parameter-ordering/normalization — so client, server and resource agree byte-for-byte on what they are signing, verifying or comparing.
clientassertion
Package clientassertion implements private_key_jwt-style client authentication: assertion construction and verification.
Package clientassertion implements private_key_jwt-style client authentication: assertion construction and verification.
critical
Package critical implements the "crit" (Critical) Header Parameter check RFC 7515 §4.1.11 (JWS) and RFC 7516 §4.1.13 (JWE, which inherits the JWS rule) both require: a header must be rejected only if "crit" names a parameter the recipient doesn't actually understand and process — every other unrecognized member is ignored, never rejected (RFC 7515 §4.2/§4.3, RFC 7516 §4.2/§4.3).
Package critical implements the "crit" (Critical) Header Parameter check RFC 7515 §4.1.11 (JWS) and RFC 7516 §4.1.13 (JWE, which inherits the JWS rule) both require: a header must be rejected only if "crit" names a parameter the recipient doesn't actually understand and process — every other unrecognized member is ignored, never rejected (RFC 7515 §4.2/§4.3, RFC 7516 §4.2/§4.3).
dpop
Package dpop implements DPoP (RFC 9449) proof creation and verification: proof JWT construction, the "ath" access-token hash, JWK thumbprint computation, and the checks needed to detect proof replay.
Package dpop implements DPoP (RFC 9449) proof creation and verification: proof JWT construction, the "ath" access-token hash, JWK thumbprint computation, and the checks needed to detect proof replay.
jarm
Package jarm implements JWT Secured Authorization Response Mode signing and verification: encoding the authorization response as a signed JWT and validating one on receipt.
Package jarm implements JWT Secured Authorization Response Mode signing and verification: encoding the authorization response as a signed JWT and validating one on receipt.
jose
Package jose implements the shared JWT/JWS/JWK parsing, encoding and signature-verification primitives used throughout the module: strict parsing, JWK validation, and algorithm-policy enforcement.
Package jose implements the shared JWT/JWS/JWK parsing, encoding and signature-verification primitives used throughout the module: strict parsing, JWK validation, and algorithm-policy enforcement.
jwe
Package jwe implements JWE (RFC 7516) compact-serialization encryption and decryption for exactly the two key-management algorithms fapi.KeyManagementAlgorithm supports (RSA-OAEP-256 and ECDH-ES+A256KW), each of which may be paired with either content-encryption algorithm fapi.ContentEncryptionAlgorithm supports: A256GCM (a single AEAD primitive) or A256CBC-HS512 (encrypt-then-MAC — AES-256-CBC plus a separate HMAC-SHA-512 tag, RFC 7518 §5.2.3).
Package jwe implements JWE (RFC 7516) compact-serialization encryption and decryption for exactly the two key-management algorithms fapi.KeyManagementAlgorithm supports (RSA-OAEP-256 and ECDH-ES+A256KW), each of which may be paired with either content-encryption algorithm fapi.ContentEncryptionAlgorithm supports: A256GCM (a single AEAD primitive) or A256CBC-HS512 (encrypt-then-MAC — AES-256-CBC plus a separate HMAC-SHA-512 tag, RFC 7518 §5.2.3).
metadata
Package metadata implements shared parsing and validation for authorization-server and client metadata documents (OAuth 2.0 Authorization Server Metadata / OpenID Connect Discovery, and OAuth 2.0 Dynamic Client Registration metadata).
Package metadata implements shared parsing and validation for authorization-server and client metadata documents (OAuth 2.0 Authorization Server Metadata / OpenID Connect Discovery, and OAuth 2.0 Dynamic Client Registration metadata).
par
Package par implements the shared wire format for Pushed Authorization Requests (RFC 9126): request encoding, the request_uri response shape, and the parameter rules common to submitting and accepting a PAR request.
Package par implements the shared wire format for Pushed Authorization Requests (RFC 9126): request encoding, the request_uri response shape, and the parameter rules common to submitting and accepting a PAR request.
pkce
Package pkce implements PKCE (RFC 7636) code-verifier generation and code-challenge derivation/verification.
Package pkce implements PKCE (RFC 7636) code-verifier generation and code-challenge derivation/verification.
requestobject
Package requestobject implements signed JAR (RFC 9101) request-object construction and verification for the parameters carried in an authorization/PAR request.
Package requestobject implements signed JAR (RFC 9101) request-object construction and verification for the parameters carried in an authorization/PAR request.
token
Package token implements shared JWT access token (RFC 9068) and ID token (OIDC Core) issuance and validation logic that sits below the public TokenSet/TokenResult types.
Package token implements shared JWT access token (RFC 9068) and ID token (OIDC Core) issuance and validation logic that sits below the public TokenSet/TokenResult types.
validation
Package validation implements shared strict-parsing and input-validation helpers (bounded string/size checks, allow-listed character sets, redirect URI matching, scope syntax, and similar low-level checks used across role packages).
Package validation implements shared strict-parsing and input-validation helpers (bounded string/size checks, allow-listed character sets, redirect URI matching, scope syntax, and similar low-level checks used across role packages).
Package keys defines operation-based signing, verification, and decryption contracts so that callers never need to hold or pass around a raw crypto.PrivateKey.
Package keys defines operation-based signing, verification, and decryption contracts so that callers never need to hold or pass around a raw crypto.PrivateKey.
ephemeral
Package ephemeral provides in-memory implementations of keys.KeyManager and keys.ClientKeySource — for local development and testing only.
Package ephemeral provides in-memory implementations of keys.KeyManager and keys.ClientKeySource — for local development and testing only.
Package resource implements the FAPI 2.0 resource-server (RS) role: verifying incoming access tokens and their sender-constraint proofs on behalf of a protected API.
Package resource implements the FAPI 2.0 resource-server (RS) role: verifying incoming access tokens and their sender-constraint proofs on behalf of a protected API.
Package server implements the FAPI 2.0 authorization-server (AS) role: pushed authorization requests, the authorization endpoint state machine, token issuance, and the server's own discovery metadata and published keys.
Package server implements the FAPI 2.0 authorization-server (AS) role: pushed authorization requests, the authorization endpoint state machine, token issuance, and the server's own discovery metadata and published keys.
Package storage defines the persistence contracts used by client and server, plus the replay-detection primitive they can both safely share.
Package storage defines the persistence contracts used by client and server, plus the replay-detection primitive they can both safely share.
memstore
Package memstore provides in-memory implementations of every storage interface server.Dependencies needs (ClientRepository, TransactionStore, GrantStore, ReplayStore) — for local development and testing only.
Package memstore provides in-memory implementations of every storage interface server.Dependencies needs (ClientRepository, TransactionStore, GrantStore, ReplayStore) — for local development and testing only.

Jump to

Keyboard shortcuts

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