Refresh Token Handling

Nucleic-Session: F3D68A19-D251-48C6-AFAA-9BDC68FB88BC
Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
2026-07-09 04:25:50 -07:00
co-authored by nucleic
parent 3c8a81d21f
commit b025c19bf4
@@ -0,0 +1,79 @@
import Foundation
import Testing
@testable import NucleicCore
/// Locks the newest-wins rule that keeps a sandboxed Codex login coherent with the host's
/// `~/.codex/auth.json` (LOGIN SYNC). Only the I/O-free core is exercised the real file copy
/// touches the developer's home so `CodexAuthFile` splits the decision (`shouldReplace`) and the
/// parsing (`lastRefresh`/`isValidLogin`) out for exactly this.
struct CodexAuthFileTests {
/// An `auth.json` like the one `codex` writes, with the given `last_refresh` and refresh token.
private static func login(lastRefresh: String, refresh: String = "ref", apiKey: String = "") -> String {
"""
{"OPENAI_API_KEY":"\(apiKey)","tokens":{"id_token":"id","access_token":"acc",\
"refresh_token":"\(refresh)"},"last_refresh":"\(lastRefresh)"}
"""
}
// MARK: - Parsing
@Test func parsesLastRefreshWithAndWithoutFractionalSeconds() {
// Codex emits fractional seconds; a re-login may not. Both parse, and the fractional instant
// sorts strictly after the whole-second one at the same minute.
let frac = Self.lastRefresh(Self.login(lastRefresh: "2026-07-09T10:56:59.068487Z"))
let plain = Self.lastRefresh(Self.login(lastRefresh: "2026-07-09T10:56:59Z"))
#expect(frac != nil && plain != nil)
#expect(frac! > plain!)
}
private static func lastRefresh(_ json: String) -> Double? { CodexAuthFile.lastRefresh(json) }
@Test func validatesLoginRequiresNonEmptyRefreshToken() {
#expect(CodexAuthFile.isValidLogin(Self.login(lastRefresh: "2026-07-09T10:56:59Z")))
// API-key-only auth.json (no tokens) has no refresh lineage to reconcile.
#expect(!CodexAuthFile.isValidLogin(#"{"OPENAI_API_KEY":"sk-abc","tokens":{}}"#))
#expect(!CodexAuthFile.isValidLogin(#"{"tokens":{"refresh_token":""}}"#))
#expect(!CodexAuthFile.isValidLogin("not json"))
#expect(CodexAuthFile.lastRefresh("not json") == nil)
}
// MARK: - Newest-wins decision
@Test func replacesWhenCandidateIsStrictlyNewer() {
let current = Self.login(lastRefresh: "2026-07-09T10:00:00Z", refresh: "old")
let candidate = Self.login(lastRefresh: "2026-07-09T11:00:00Z", refresh: "rotated")
#expect(CodexAuthFile.shouldReplace(candidate: candidate, current: current))
}
@Test func skipsWhenCandidateIsOlderOrEqual() {
let current = Self.login(lastRefresh: "2026-07-09T11:00:00Z")
#expect(!CodexAuthFile.shouldReplace(
candidate: Self.login(lastRefresh: "2026-07-09T10:00:00Z"), current: current))
// Equal timestamp is not newer re-seeding the same login must not thrash the file.
#expect(!CodexAuthFile.shouldReplace(
candidate: Self.login(lastRefresh: "2026-07-09T11:00:00Z"), current: current))
}
@Test func skipsIdenticalCredential() {
let same = Self.login(lastRefresh: "2026-07-09T11:00:00Z")
#expect(!CodexAuthFile.shouldReplace(candidate: same, current: same))
}
@Test func skipsInvalidOrUndatedCandidate() {
let current = Self.login(lastRefresh: "2026-07-09T10:00:00Z")
// A truncated/half-written file, or one missing last_refresh, must never be propagated even
// though it might "look" newer.
#expect(!CodexAuthFile.shouldReplace(candidate: #"{"tokens":{"refresh_token":"r"}}"#, current: current))
#expect(!CodexAuthFile.shouldReplace(candidate: "not json", current: current))
}
@Test func seedsWhenDestinationIsMissingOrUnparseable() {
// Unlike the Claude Keychain (which never fabricates), the Codex path is a file copy used to
// seed the container: a nil destination (not yet created) or an unparseable one is stale, so a
// valid candidate wins. This is what pushes the host login into a fresh container home.
let candidate = Self.login(lastRefresh: "2026-07-09T11:00:00Z")
#expect(CodexAuthFile.shouldReplace(candidate: candidate, current: nil))
#expect(CodexAuthFile.shouldReplace(candidate: candidate, current: "not json"))
}
}