Documentation
¶
Overview ¶
Package documents defines AuthKit's generic immutable signed-document wire contract. It authenticates transport metadata and opaque JSON payload bytes; application schema and authorization remain the receiving application's job.
Index ¶
- Constants
- Variables
- func Digest(payload []byte) string
- func NewPublisher(lookup LookupDocument, authorize AuthorizeRequest) http.Handler
- func NormalizeDigest(value string) (string, error)
- func NormalizeReferences(in map[string]string) (map[string]string, error)
- func NormalizeType(value string) (string, error)
- func ParseReferencesJSON(raw []byte) (map[string]string, error)
- func ValidateDigest(value string) error
- func ValidateReferences(references map[string]string) error
- func ValidateType(value string) error
- type AuthorizeRequest
- type DocumentVerifier
- type Envelope
- type Header
- type LookupDocument
- type Reference
- type Resolver
- type ResolverOptions
- type SignedDocument
- type VerifyOptions
Examples ¶
Constants ¶
const ( JOSEType = "authkit-document+jws" MaxTypeBytes = 256 MaxIssuerBytes = 2048 MaxAudienceBytes = 512 MaxAudiences = 32 MaxPayloadBytes = 1 << 20 MaxSignedPayloadBytes = MaxPayloadBytes + 16<<10 MaxCompactJWSBytes = 2 << 20 MaxReferences = 16 MaxReferencesJSONBytes = 4 << 10 )
const PublicationPathPrefix = "/.well-known/authkit/documents/"
Variables ¶
var ( ErrInvalidReference = errors.New("invalid_document_reference") ErrInvalidType = errors.New("invalid_document_type") ErrInvalidDigest = errors.New("invalid_document_digest") ErrDuplicateReference = errors.New("duplicate_document_reference") ErrTooManyReferences = errors.New("too_many_document_references") ErrReferencesTooLarge = errors.New("document_references_too_large") ErrWrongTokenType = errors.New("documents_wrong_token_type") ErrReservedAttribute = errors.New("reserved_document_attribute") ErrInvalidEnvelope = errors.New("invalid_document_envelope") ErrPayloadTooLarge = errors.New("document_payload_too_large") ErrMalformedJWS = errors.New("malformed_document_jws") ErrWrongJOSEType = errors.New("wrong_document_jose_type") ErrUnsupportedAlgorithm = errors.New("unsupported_document_algorithm") ErrUnsupportedSigner = errors.New("unsupported_document_signer") ErrUnknownKey = errors.New("unknown_document_key") ErrInvalidSignature = errors.New("invalid_document_signature") ErrDigestMismatch = errors.New("document_digest_mismatch") ErrIssuerMismatch = errors.New("document_issuer_mismatch") ErrAudienceMismatch = errors.New("document_audience_mismatch") ErrTypeMismatch = errors.New("document_type_mismatch") ErrUntrustedIssuer = errors.New("untrusted_document_issuer") ErrNotFound = errors.New("document_not_found") ErrFetch = errors.New("document_fetch_failed") ErrRedirect = errors.New("document_redirect_rejected") )
Functions ¶
func NewPublisher ¶
func NewPublisher(lookup LookupDocument, authorize AuthorizeRequest) http.Handler
NewPublisher returns the framework-neutral well-known publication handler.
func NormalizeDigest ¶
func NormalizeReferences ¶
NormalizeReferences prepares a mint-time documents claim. It trims types, canonicalizes digests, and rejects normalization collisions.
func NormalizeType ¶
func ParseReferencesJSON ¶
ParseReferencesJSON strictly parses a documents claim. Duplicate keys and non-canonical values are rejected instead of being overwritten by map decode.
func ValidateDigest ¶
func ValidateReferences ¶
func ValidateType ¶
ValidateType requires a bounded opaque identifier ending in /vN. AuthKit does not interpret the namespace or version beyond enforcing that the version is present in the type itself.
Types ¶
type AuthorizeRequest ¶
AuthorizeRequest either authenticates an incoming publisher request or adds existing machine credentials to an outgoing resolver request. Nil always denies; AuthKit does not define a document-specific credential.
type DocumentVerifier ¶
type DocumentVerifier interface {
ValidateDocumentIssuer(context.Context, string) error
VerifyDocument(context.Context, SignedDocument, VerifyOptions) (Envelope, error)
}
DocumentVerifier is implemented by verify.Verifier. The preflight trust check prevents an untrusted issuer from becoming a network destination.
type Envelope ¶
type Envelope struct {
Issuer string `json:"iss"`
Audiences []string `json:"aud"`
Type string `json:"type"`
Payload json.RawMessage `json:"payload"`
}
Envelope is the signed JWS payload. Payload is intentionally opaque to AuthKit and may contain any valid JSON value.
func DecodeEnvelope ¶
DecodeEnvelope strictly decodes the signed payload, rejecting duplicate or unknown fields before any application payload can be returned.
func NormalizeEnvelope ¶
NormalizeEnvelope returns a detached, normalized copy suitable for one-time marshaling and signing.
func (Envelope) HasAudience ¶
type Header ¶
Header is the security-relevant subset of an inspected compact JWS header. Inspecting a header or payload does not verify its signature.
type LookupDocument ¶
type LookupDocument func(context.Context, string) (SignedDocument, error)
LookupDocument returns a retained document by its immutable payload digest. Its compact JWS representation may change when the payload is re-signed.
type Reference ¶
Reference identifies one exact signed envelope. Type carries the application schema version (for example, example.catalog/v1); Digest covers the exact JWS payload bytes, not a decoded/re-encoded JSON value.
func NormalizeReference ¶
type Resolver ¶
type Resolver struct {
// contains filtered or unexported fields
}
Example (TwoSites) ¶
package main
import (
"context"
"crypto"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/http/httptest"
"github.com/open-rails/authkit/documents"
"github.com/open-rails/authkit/jwtkit"
"github.com/open-rails/authkit/verify"
)
const machineAuthorization = "Bearer existing-machine-credential"
func requireMachine(request *http.Request) error {
if request.Header.Get("Authorization") != machineAuthorization {
return errors.New("unauthorized")
}
return nil
}
func addMachine(request *http.Request) error {
request.Header.Set("Authorization", machineAuthorization)
return nil
}
func main() {
signer, _ := jwtkit.NewRSASigner(2048, "site-a-key")
var document documents.SignedDocument
siteA := httptest.NewServer(documents.NewPublisher(func(context.Context, string) (documents.SignedDocument, error) {
return document, nil
}, requireMachine))
defer siteA.Close()
document, _ = documents.Sign(context.Background(), signer, documents.Envelope{
Issuer: siteA.URL, Audiences: []string{"site-b"}, Type: "example.entitlements/v1", Payload: json.RawMessage(`{"plan":"starter"}`),
})
v := verify.NewVerifier()
_ = v.AddIssuer(siteA.URL, nil, verify.IssuerOptions{RawKeys: map[string]crypto.PublicKey{signer.KID(): signer.PublicKey()}})
resolver := documents.NewResolver(v, siteA.Client(), addMachine, documents.ResolverOptions{AllowHTTP: true})
siteB := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
payload, err := resolver.Resolve(r.Context(), siteA.URL, document.Reference, "site-b")
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
_, _ = w.Write(payload) // site B owns schema/processing from here.
}))
defer siteB.Close()
response, _ := siteB.Client().Get(siteB.URL)
body, _ := io.ReadAll(response.Body)
response.Body.Close()
fmt.Println(string(body))
}
Output: {"plan":"starter"}
func NewResolver ¶
func NewResolver(verifier DocumentVerifier, client *http.Client, authorize AuthorizeRequest, opts ResolverOptions) *Resolver
type ResolverOptions ¶
type SignedDocument ¶
type SignedDocument struct {
CompactJWS string `json:"jws"`
Reference Reference `json:"reference"`
SignedPayload []byte `json:"signed_payload"`
}
SignedDocument retains both the compact JWS and the exact bytes used as its payload so callers can publish them without a parse/re-encode step.
func FromCompact ¶
func FromCompact(compact string, reference Reference) (SignedDocument, error)