1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
[]
= "pas-external"
= "0.12.0"
= "2024"
= "1.94"
= "MIT OR Apache-2.0"
= "Ppoppo Accounts System (PAS) external SDK — OAuth2 PKCE, JWT verification port, Axum middleware, session liveness"
= "https://github.com/hakchin/ppoppo"
= "https://accounts.ppoppo.com"
= "https://docs.rs/pas-external"
= ["oauth2", "pkce", "jwt", "axum", "authentication"]
= ["authentication", "web-programming"]
[]
= ["oauth", "token"]
# `oauth` pulls in `tokio::sync` to carry the #005 break-glass
# sv-validator (`session_version` module). The validator depends on
# `<AuthClient as PasAuthPort>::userinfo` (HTTP userinfo fallback) so
# it lives behind the `oauth` feature; non-oauth consumers never
# compile it. `async-trait` is unconditional (Phase 9 — required by
# the always-compiled `audit::AuditSink` trait).
= [
"dep:reqwest",
"dep:sha2",
"dep:rand",
"dep:base64",
"dep:url",
"dep:tokio",
]
= ["dep:ppoppo-token", "ppoppo-sdk-core/token"]
# Well-known JWKS (RFC 7517) fetcher with TTL cache + rotation handling.
# Pulls in the HTTP client (oauth) and the token-verification primitives.
= ["oauth", "token", "dep:tokio", "ppoppo-sdk-core/well-known-fetch"]
# Phase 11.Z 0.10.0 — `epoch::SharedCacheCache` (per-user sv readout
# over the canonical `STANDARDS_SHARED_CACHE.md §3.1` `sv:{sub}`
# namespace). Pulls `ppoppo-infra` trait crate; substrate is the
# consumer's choice (KVRocks, Redis, in-memory). Pulls `well-known-fetch`
# transitively so the `epoch` module is available.
= ["well-known-fetch", "dep:ppoppo-infra"]
# Server-side session liveness: AES-256-GCM refresh_token encryption at rest
# plus a PAS-backed liveness classifier. Consumers that persist PAS refresh
# tokens server-side should enable this feature.
= ["oauth", "dep:aes-gcm", "dep:base64"]
# Axum middleware. Pulls in session-liveness so that the OAuth callback can
# encrypt PAS refresh_tokens before handing them to the consumer's
# SessionStore — the plaintext never reaches consumer code. The `BearerAuthLayer`
# tower::Layer + tower::Service impls live in `ppoppo-sdk-core::bearer` (Phase A
# Slice 4 lift); pas-external re-exports them at `pas_external::bearer::*` for
# 3rd-party consumers (RCW/CTW; audit decision D in
# `RFC_2026-05-08_app-credential-collapse.md`). pas-external still depends on
# `axum` directly because the OIDC RP composition root (`oidc::relying_party`,
# `oidc::state_store`) builds axum responses + extracts cookies in its own
# handler bodies.
= ["oauth", "session-liveness", "token", "well-known-fetch", "ppoppo-sdk-core/axum", "dep:axum", "dep:axum-extra", "dep:tower", "dep:tracing"]
# Exposes:
# - `pas_port::MemoryPasAuth` — the in-memory `PasAuthPort` adapter
# used by SDK boundary tests and recommended for downstream consumers'
# integration tests.
# - `test_support::FakePasServer` — wiremock-wrapped fake PAS
# Authorization Server for consumer boundary tests against the real
# `RelyingParty::new(...)` boot path (Phase 11.Y replacement for
# the deleted `for_test_with_parts` escape hatch). Pulls in
# `wiremock` + `serde_json` + `ppoppo-token` to stand up the
# discovery + JWKS + token endpoints in-process.
# No runtime cost when disabled.
= ["dep:wiremock", "well-known-fetch", "ppoppo-sdk-core/test-support"]
[]
# Core types
= { = "2", = false, = ["display", "from_str", "from", "into"] }
= { = "1", = ["serde"] }
# 1st-party shared SDK primitives (verifier port, audit trait, session
# liveness port, identity types). Path-dep only — `ppoppo-sdk-core` is
# never published to crates.io. RFC_2026-05-08_app-credential-collapse
# Phase A.
= { = true, = false }
# Clock port — injected wall-clock abstraction for deterministic testing.
= { = true, = ["native"] }
# Token verification — Ppoppo JWT engine (RFC 9068, EdDSA).
# 0.x SemVer: workspace-pinned; γ port-and-adapter consumes engine
# internally, never re-exports `jsonwebtoken::*` through the SDK
# boundary. Phase 10.0 split (D1) introduces `access_token::*` and
# `id_token::*` namespaces — pas-external imports the access-token
# profile only; id_token RP middleware lives in `pas-external::oidc`
# (Phase 10.11+) or a sibling SDK crate.
= { = true, = true }
# Backend-agnostic infrastructure traits (`Cache`, `CacheExt`).
# Phase 11.Z 0.10.0 (RFC_2026-05-08 §4.1 lock) — `epoch::SharedCacheCache`
# adapts any `ppoppo_infra::Cache` impl to the SDK's sv-specific
# `epoch::Cache` shape. Substrate-agnostic by construction:
# consumers wire `ppoppo-kvrocks::KvCache` for production or any
# in-memory test impl. `ppoppo-infra` is trait-only (no Redis client
# deps) so the published SDK surface stays small. Optional + gated
# behind `feature = "shared-cache"` so non-sv consumers don't pay
# the dep cost.
= { = true, = true }
# OAuth2 HTTP client
= { = "0.13", = false, = ["json", "form", "rustls"], = true }
= { = "2", = ["serde"], = true }
# PKCE
= { = "0.11", = true }
= { = "0.22", = true }
= { = "0.10", = true }
# Session liveness: AES-256-GCM at-rest encryption for refresh_token
= { = "0.10", = ["aes"], = true }
# Well-known keyset and #005 sv-validator use `tokio::sync::RwLock`.
= { = "1", = false, = ["sync"], = true }
# Async trait methods. Phase 9 promoted this from `oauth` / `token`
# feature gates to unconditional: the always-compiled `audit::AuditSink`
# trait requires it across every build configuration. Compile-time
# overhead is ~50 KB; the alternative (gating the audit module behind
# a feature) would re-introduce matrix complexity for zero dep saving.
= "0.1"
# Serialization
= { = "1", = ["derive"] }
= "1"
# Error handling
= "2"
# Axum middleware (feature = "axum")
= { = "0.8", = true }
= { = "0.12", = ["cookie-private"], = true }
# Tower Layer + Service traits for the `oidc::axum::BearerAuthLayer`
# perimeter mechanism (Phase 11.X.C lift). Workspace-pinned at 0.5;
# axum 0.8 transitively depends on the same major.
= { = true, = true }
= { = "0.1", = true }
= { = "0.3", = ["serde", "serde-well-known", "parsing"] }
# HTTP test substrate for `test_support::FakePasServer` (mocks PAS
# discovery + JWKS + token endpoints in-process). Optional dep, gated
# on the `test-support` feature so non-test consumers do not pay the
# wiremock compile cost.
= { = "0.6", = true }
[]
= "1"
= { = "1", = ["rt-multi-thread", "macros"] }
= "0.1"
= "1"
= "2"
[[]]
= "liveness_boundary"
= ["test-support", "session-liveness"]
[[]]
= "bearer_verifier_boundary"
= ["test-support", "well-known-fetch"]
[[]]
= "id_token_verifier_boundary"
= ["test-support", "well-known-fetch"]
[[]]
= "audit_sink_boundary"
= ["test-support"]
# Perimeter `BearerAuthLayer` boundary tests for the SDK re-export
# surface (`pas_external::bearer::*`). Phase A Slice 4 renamed from
# `oidc_axum_boundary` after the Layer kit moved to
# `ppoppo_sdk_core::bearer` and pas-external dropped the
# `oidc::axum::*` namespace (audit decisions D + F in
# `RFC_2026-05-08_app-credential-collapse.md`).
[[]]
= "bearer_boundary"
= ["test-support", "axum"]
# 0.10.0 boundary tests — RFC_2026-05-08 §4.1/§4.2 lock validation.
[[]]
= "epoch_shared_cache_boundary"
= ["test-support", "shared-cache"]
[[]]
= "session_liveness_lookup_boundary"
= ["test-support", "well-known-fetch"]
[]
= "forbid"
[]
= "deny"
= "deny"
= "deny"