Skip to content

Go: package jose

Generated from the Go source by scripts/gen-docs.sh — do not edit; run it to regenerate. Canonical deep reference: pkg.go.dev/github.com/dnsid-ai/dnsid-go/jose. Guides and account setup: https://docs.dnsid.ai.

import "github.com/dnsid-ai/dnsid-go/jose"

Package jose implements the DNSid JOSE profile: creating and verifying DNSid JWTs and compact JWS objects signed with an agent’s operational key.

The main entry type is Profile. A Profile combines an identity resolver (usually a dnsid.IdentityManager), a local agent domain, and a dnsid.KeyProvider. The resolver is required for verification; the local domain and key provider are required for signing. Config bounds token lifetime and clock skew; its zero value applies the profile defaults.

A typical round trip mints a JWT as the local agent and verifies a peer’s token by resolving the issuer’s DNSid identity record from DNS:

profile := jose.NewFromIdentityManagerKeyProvider(idm, jose.Config{})
token, err := profile.CreateJWT(jose.JWTOptions{Audience: "bob.example"})
// ... send token to bob.example ...
vd, claims, err := profile.VerifyJWT(ctx, token, jose.VerifyJWTOptions{ExpectedAudience: "bob.example"})
// At the receiving side, Bob must verify against its own expected audience.
// vd identifies the verified issuer domain; claims holds the parsed JWT.

CreateJWS and VerifyJWS provide the same flow for compact JWS over arbitrary payloads.

See https://docs.dnsid.ai for protocol guides and account setup.

Example

Example mints a DNSid JWT as agent.example and verifies it as relying-party.example, entirely offline.

package main
import (
"context"
"crypto/tls"
"encoding/json"
"fmt"
"time"
dnsid "github.com/dnsid-ai/dnsid-go"
"github.com/dnsid-ai/dnsid-go/jose"
dnsidlog "github.com/dnsid-ai/dnsid-go/log"
)
// The fakes below stand in for live DNS, HTTPS, and transparency-log lookups
// so the example runs offline. In production, omit the With... options and
// let the verifier resolve the issuer's published identity record.
type fakeDNSResolver struct {
records map[string][]dnsid.TXTRecordRData
}
func (r fakeDNSResolver) FetchTXT(_ context.Context, name string) ([]dnsid.TXTRecordRData, dnsid.DNSSECState, error) {
return r.records[name], dnsid.DNSSECStateUnsigned, nil
}
type fakeHTTPSFetcher struct {
responses map[string]json.RawMessage
}
func (f fakeHTTPSFetcher) FetchJSON(_ context.Context, rawURL string, _ dnsid.FetchOptions) (json.RawMessage, *tls.Certificate, error) {
return f.responses[rawURL], nil, nil
}
type fakeLogReader struct{}
func (fakeLogReader) Canonical(dnsidlog.LogEvent) ([]byte, error) { return nil, nil }
func (fakeLogReader) KeyTimestamp(context.Context, string, string) (time.Time, error) {
return time.Now(), nil
}
func (fakeLogReader) VerifyBilateralBinding(context.Context, dnsidlog.BilateralBindingInput) (dnsidlog.BilateralBinding, error) {
return dnsidlog.BilateralBinding{}, nil
}
func (fakeLogReader) VerifyOperationalContinuity(context.Context, string, string, string) error {
return nil
}
func (fakeLogReader) VerifyGovernanceRelationship(context.Context, string, string) error { return nil }
func (fakeLogReader) VerifyNonRevocation(context.Context, string, time.Time) (dnsidlog.LoggedStateEvidence, error) {
return dnsidlog.LoggedStateEvidence{}, nil
}
func (fakeLogReader) ReadEvent(context.Context, dnsidlog.LogRef) (dnsidlog.LogEvent, error) {
return dnsidlog.LogEvent{}, nil
}
func (fakeLogReader) RebuildHistory(context.Context, string) ([]dnsidlog.LogEvent, error) {
return nil, nil
}
// Example mints a DNSid JWT as agent.example and verifies it as
// relying-party.example, entirely offline.
func main() {
// The agent's identity: its domain, governing organization, entity
// record-signing key, and operational key.
agentKey := dnsid.GenerateES256KeyProvider()
issuer, err := dnsid.NewIdentityManager(dnsid.Config{Identity: &dnsid.IdentityConfig{
Domain: "agent.example",
GovernanceID: "agent.example",
LogRef: "algorand:ADDR",
StatusURL: "https://agent.example/.well-known/dnsid/status.json",
KeyURL: "https://agent.example/ku.json",
EntityKeyURL: "https://agent.example/ek.json",
}}, agentKey, dnsid.WithEntityKeyProvider(dnsid.GenerateES256KeyProvider()))
if err != nil {
panic(err)
}
// Publish the agent's identity record and key sets into the offline
// fakes, exactly as they would appear in DNS and over HTTPS.
record, err := issuer.CreateTXTRecord()
if err != nil {
panic(err)
}
jwksBytes, err := json.Marshal(issuer.GetKeySet().Raw())
if err != nil {
panic(err)
}
entityBytes, err := json.Marshal(issuer.GetEntityKeySet().Raw())
if err != nil {
panic(err)
}
registry := dnsidlog.NewLogRegistry()
if err := registry.Register("algorand", func(string) dnsidlog.LogReader { return fakeLogReader{} }); err != nil {
panic(err)
}
// The relying party's manager resolves the agent's identity from the
// fakes above.
verifier, err := dnsid.NewIdentityManager(dnsid.Config{Identity: &dnsid.IdentityConfig{
Domain: "relying-party.example",
GovernanceID: "relying-party.example",
LogRef: "algorand:ADDR",
StatusURL: "https://relying-party.example/.well-known/dnsid/status.json",
}}, dnsid.GenerateES256KeyProvider(),
dnsid.WithDNSResolver(fakeDNSResolver{records: map[string][]dnsid.TXTRecordRData{
"_dnsid.agent.example": {{Value: record.Serialize(), TTL: time.Minute}},
}}),
dnsid.WithHTTPSFetcher(fakeHTTPSFetcher{responses: map[string]json.RawMessage{
record.KeyURI: jwksBytes,
record.EntityKeyURI: entityBytes,
record.StatusURI: json.RawMessage(`{"state":"ACTIVE","lastTransitionAt":"` + time.Now().UTC().Format(time.RFC3339Nano) + `"}`),
}}),
dnsid.WithLogRegistry(registry),
)
if err != nil {
panic(err)
}
// Mint a JWT as the agent.
issuerProfile := jose.NewFromIdentityManager(issuer, agentKey, jose.Config{})
token, err := issuerProfile.CreateJWT(jose.JWTOptions{
Audience: "relying-party.example",
AdditionalClaims: map[string]any{"purpose": "quickstart"},
})
if err != nil {
panic(err)
}
// Verify it as the relying party.
verifierProfile := jose.NewFromIdentityManager(verifier, nil, jose.Config{})
vd, claims, err := verifierProfile.VerifyJWT(context.Background(), token)
if err != nil {
panic(err)
}
fmt.Println("verified domain:", vd.Domain())
fmt.Println("issuer:", claims.Issuer)
fmt.Println("audience:", claims.Audiences[0])
fmt.Println("purpose:", claims.Extra["purpose"])
}
verified domain: agent.example
issuer: agent.example
audience: relying-party.example
purpose: quickstart

Claims holds the validated claims of a verified DNSid JWT.

type Claims struct {
// Issuer is the token's iss claim, normalized to a FQDN.
Issuer string
// Subject is the token's sub claim, normalized to a FQDN. The DNSid
// JOSE profile requires it to equal Issuer.
Subject string
// Audiences is the token's aud claim with each entry normalized to a
// FQDN.
Audiences []string
// JWTID is the token's jti claim.
JWTID string
// IssuedAt is the token's iat claim.
IssuedAt time.Time
// Expiry is the token's exp claim.
Expiry time.Time
// Extra holds the token's private (non-registered) claims.
Extra map[string]any
}

Config contains JOSE/JWT/JWS profile policy. The zero value applies the profile defaults.

type Config struct {
// MaxLifetime caps the validity window (exp - iat) of created and
// verified JWTs. Zero means omitted (15 minutes); negatives are invalid.
MaxLifetime time.Duration
// ClockSkew is the tolerance applied to time-based claim checks during
// verification. Zero means omitted (60 seconds); WithClockSkew(0)
// specifies zero tolerance. Skew never extends expiration.
ClockSkew time.Duration
// contains filtered or unexported fields
}

func (c Config) WithClockSkew(skew time.Duration) Config

WithClockSkew specifies tolerance, including explicit zero.

func (c Config) WithMaxLifetime(lifetime time.Duration) Config

WithMaxLifetime specifies a positive maximum lifetime.

JWTOptions configures JWT creation.

type JWTOptions struct {
// Audience is the token's aud claim. It is required and must normalize
// to a valid FQDN.
Audience string
// Expiry is the requested positive lifetime, capped by Config.MaxLifetime.
// A zero struct field means omitted (15 minutes); WithExpiry records
// explicit presence and rejects zero. Negative lifetimes are invalid.
Expiry time.Duration
// AdditionalClaims holds private claims to embed in the token. The
// registered claim names iss, sub, aud, exp, iat, nbf, and jti are
// reserved; CreateJWT fails if any of them appear here.
AdditionalClaims map[string]any
// contains filtered or unexported fields
}

func (o JWTOptions) WithExpiry(expiry time.Duration) JWTOptions

WithExpiry specifies an explicit positive lifetime; zero is rejected.

Profile implements the DNSid JOSE profile on top of core DNSid identity verification.

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

func New(resolver dnsid.IdentityResolver, localDomain string, kp dnsid.KeyProvider, cfg Config) *Profile

New constructs a JOSE profile. localDomain and kp are required for create/sign operations. Invalid lifetime/skew configuration is rejected when used.

func NewFromIdentityManager(manager identityManager, kp dnsid.KeyProvider, cfg Config) *Profile

NewFromIdentityManager constructs a JOSE profile from an IdentityManager-like core manager.

func NewFromIdentityManagerKeyProvider(manager identityManagerWithKeyProvider, cfg Config) *Profile

NewFromIdentityManagerKeyProvider constructs a JOSE profile from a manager that exposes its KeyProvider.

func (p *Profile) CreateJWS(payload []byte) (string, error)

CreateJWS creates a compact JWS over payload using this profile’s active key.

func (p *Profile) CreateJWT(opts JWTOptions) (string, error)

CreateJWT produces a DNSid JOSE-profile JWT signed by this profile’s active key.

func (p *Profile) VerifyJWS(ctx context.Context, compact string, opts ...dnsid.VerifyDomainOpts) ([]byte, *dnsid.VerifiedDomain, error)

VerifyJWS verifies compact JWS bytes, passing optional trusted current-peer evidence through core DNSid verification. Payload bytes remain opaque.

func (p *Profile) VerifyJWT(ctx context.Context, tokenStr string, opts ...VerifyJWTOptions) (*dnsid.VerifiedDomain, *Claims, error)

VerifyJWT verifies a DNSid JOSE-profile JWT through core domain verification.

VerifyJWTOptions configures JWT verification.

type VerifyJWTOptions struct {
// ExpectedAudience is the audience that must appear in the token's aud
// claim. Empty means the profile's local domain; verification fails if
// both are empty.
ExpectedAudience string
// ExpectedIssuer, when non-empty, requires the token's iss claim to
// match this domain after FQDN normalization.
ExpectedIssuer string
// Peer supplies trusted current application-peer evidence, never endpoint certificates.
Peer dnsid.VerifyDomainOpts
}

Generated by gomarkdoc