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
//! Minimal, UNVERIFIED JWT payload reads (auth §10.3, §10.4). We are not the
//! token's audience, so we never verify the signature — we only read our OWN
//! token's stated `exp` (to schedule refresh when the token endpoint returns no
//! `expires_in`, as OpenAI's does) and the OpenAI `chatgpt_account_id` claim
//! (echoed as a data-plane header). Pure: base64url-decode the payload segment and
//! read a field, table-tested through `parse_token_response` from literal JWTs
//! (auth §8, §10.6). The authorization server enforces real validity; a
//! malformed/opaque token here simply yields `None`.
//!
//! It sits at the crate root, not under `auth`, because it has TWO readers on
//! opposite sides of the credential seam: `auth::oauth` reads brazen's own token
//! response, and `store::ambient`'s `codex` parser reads the expiry out of a
//! foreign tool's stored access token (auth §5.5). A pure byte→field reader
//! belongs to neither owner.
use URL_SAFE_NO_PAD;
use Engine;
use Value;
/// Decode a JWT's payload — the middle of `header.payload.signature` — to JSON, or
/// `None` when the token is not a three-segment base64url JWT with a JSON payload.
/// The token's absolute `exp` (unix seconds) when present and numeric (auth §10.3).
/// Already absolute — the caller does NOT add `now`.
pub
/// OpenAI's `chatgpt_account_id`, nested under the id_token's
/// `https://api.openai.com/auth` claim (auth §10.4) — echoed as the
/// `ChatGPT-Account-ID` data-plane header.
pub