parseCodexAuthJson function
Parses the codex CLI's ~/.codex/auth.json session file (issue
#1704): {"tokens": {access_token, refresh_token, id_token, account_id?}, "last_refresh": epochSeconds} — the shape a codex login (SSO tenants included) leaves behind. The expiry comes from the
access token's own exp claim (the file carries none); a missing claim
means unknown, and the runtime's reactive refresh covers it. Null when
the shape is not a codex session.
Implementation
ChatGptOAuthCredentials? parseCodexAuthJson(String contents) {
final Object? decoded;
try {
decoded = jsonDecode(contents);
} on Object {
return null;
}
if (decoded is! Map) return null;
final tokens = decoded['tokens'];
if (tokens is! Map) return null;
String? field(String name) {
final value = tokens[name];
return value is String && value.isNotEmpty ? value : null;
}
final accessToken = field('access_token');
final refreshToken = field('refresh_token');
final idToken = field('id_token');
if (accessToken == null || refreshToken == null || idToken == null) {
return null;
}
final accountId = field('account_id');
return ChatGptOAuthCredentials(
accessToken: accessToken,
refreshToken: refreshToken,
idToken: idToken,
accountId: accountId ?? _accountIdFromJwt(idToken),
expiresAt: jwtExpClaim(accessToken),
);
}