ppoppo-token 0.3.0

JWT (RFC 9068, EdDSA) issuance + verification engine for the Ppoppo ecosystem. Single deep module with a small interface (issue, verify) hiding RFC 8725 mitigations M01-M45, JWKS handling, and substrate ports (epoch, session, replay).
Documentation
//! Profile-shared JOSE wire-format errors.
//!
//! Scope: errors emitted by the engine submodules whose enforcement
//! axis is JWS/JOSE structural — algorithm whitelist (M01-M06), header
//! attack surface (M07-M16a), serialization shape (M31-M34, segment
//! count, base64url, JSON unique keys), plus the structural rejections
//! `engine::verify` runs before any submodule (oversize, JSON-form,
//! JWE).
//!
//! NOT in scope: registered-claim semantics (M17-M30 — exp/aud/iss/iat/
//! sub/jti/cat/client_id). Those are profile-aware: RFC 9068 requires
//! `cat`/`client_id`; OIDC requires `nonce`. Each profile owns its
//! claims-validation pipeline and its claims-error variants. When
//! id_token's `check_claims` lands (Phase 10.x), variant naming will
//! parallel access_token's intentionally — the *errors* are structurally
//! similar but the *enforcement contracts* are not, and forcing them
//! through one shared enum would obscure that distinction.
//!
//! Variants map to mitigation IDs in
//! `0context/STANDARDS_JWT_DETAILS_MITIGATION_PPOPPO.md` §A, §B, §D.

#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)]
pub enum SharedAuthError {
    // ── A. Algorithm (M01-M06) ────────────────────────────────────────────
    #[error("M01: alg=none rejected")]
    AlgNone,
    #[error("M02: algorithm outside whitelist")]
    AlgNotWhitelisted,
    #[error("M03: HMAC algorithm rejected")]
    AlgHmacRejected,
    #[error("M04: RSA algorithm rejected")]
    AlgRsaRejected,
    #[error("M05: ECDSA algorithm rejected")]
    AlgEcdsaRejected,

    // ── B. Header (M07-M16a) ──────────────────────────────────────────────
    #[error("M07: jku header rejected")]
    HeaderJku,
    #[error("M08: x5u header rejected")]
    HeaderX5u,
    #[error("M09: jwk header rejected")]
    HeaderJwk,
    #[error("M10: x5c header rejected")]
    HeaderX5c,
    #[error("M11: crit header rejected")]
    HeaderCrit,
    #[error("M12: kid missing or unknown")]
    KidUnknown,
    #[error("M13: typ mismatch")]
    TypMismatch,
    #[error("M14: nested JWS rejected")]
    NestedJws,
    #[error("M15: JWE rejected")]
    JwePayload,
    #[error("M16: extra header params")]
    HeaderExtraParam,
    #[error("M16a: b64=false rejected")]
    HeaderB64False,

    // ── D. Serialization (M31-M34) ────────────────────────────────────────
    #[error("M31: JWS JSON serialization rejected — Compact only")]
    JwsJsonRejected,
    #[error("M32: JSON object contains duplicate keys")]
    DuplicateJsonKeys,
    #[error("M33: segment contains non-URL-safe base64 characters")]
    LaxBase64,
    #[error("M34: token exceeds maximum size")]
    OversizedToken,

    // ── Parse / structural ────────────────────────────────────────────────
    #[error("token is not a JWS Compact serialization")]
    NotJwsCompact,
    #[error("header is not valid JSON")]
    HeaderUnparseable,
    #[error("payload is not valid JSON")]
    PayloadUnparseable,
}