refactor(account): share token freshness helper (#20591)
parent
5daf2fa7f0
commit
916afb5220
|
|
@ -120,6 +120,10 @@ class TokenRefreshRequest extends Schema.Class<TokenRefreshRequest>("TokenRefres
|
||||||
|
|
||||||
const clientId = "opencode-cli"
|
const clientId = "opencode-cli"
|
||||||
const eagerRefreshThreshold = Duration.minutes(5)
|
const eagerRefreshThreshold = Duration.minutes(5)
|
||||||
|
const eagerRefreshThresholdMs = Duration.toMillis(eagerRefreshThreshold)
|
||||||
|
|
||||||
|
const isTokenFresh = (tokenExpiry: number | null, now: number) =>
|
||||||
|
tokenExpiry != null && tokenExpiry > now + eagerRefreshThresholdMs
|
||||||
|
|
||||||
const mapAccountServiceError =
|
const mapAccountServiceError =
|
||||||
(message = "Account service operation failed") =>
|
(message = "Account service operation failed") =>
|
||||||
|
|
@ -219,7 +223,7 @@ export namespace Account {
|
||||||
|
|
||||||
const account = maybeAccount.value
|
const account = maybeAccount.value
|
||||||
const now = yield* Clock.currentTimeMillis
|
const now = yield* Clock.currentTimeMillis
|
||||||
if (account.token_expiry && account.token_expiry > now + Duration.toMillis(eagerRefreshThreshold)) {
|
if (isTokenFresh(account.token_expiry, now)) {
|
||||||
return account.access_token
|
return account.access_token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -229,7 +233,7 @@ export namespace Account {
|
||||||
|
|
||||||
const resolveToken = Effect.fnUntraced(function* (row: AccountRow) {
|
const resolveToken = Effect.fnUntraced(function* (row: AccountRow) {
|
||||||
const now = yield* Clock.currentTimeMillis
|
const now = yield* Clock.currentTimeMillis
|
||||||
if (row.token_expiry && row.token_expiry > now + Duration.toMillis(eagerRefreshThreshold)) {
|
if (isTokenFresh(row.token_expiry, now)) {
|
||||||
return row.access_token
|
return row.access_token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ const truncate = Layer.effectDiscard(
|
||||||
|
|
||||||
const it = testEffect(Layer.merge(AccountRepo.layer, truncate))
|
const it = testEffect(Layer.merge(AccountRepo.layer, truncate))
|
||||||
|
|
||||||
|
const insideEagerRefreshWindow = Duration.toMillis(Duration.minutes(1))
|
||||||
|
const outsideEagerRefreshWindow = Duration.toMillis(Duration.minutes(10))
|
||||||
|
|
||||||
const live = (client: HttpClient.HttpClient) =>
|
const live = (client: HttpClient.HttpClient) =>
|
||||||
Account.layer.pipe(Layer.provide(Layer.succeed(HttpClient.HttpClient, client)))
|
Account.layer.pipe(Layer.provide(Layer.succeed(HttpClient.HttpClient, client)))
|
||||||
|
|
||||||
|
|
@ -63,7 +66,7 @@ it.live("orgsByAccount groups orgs per account", () =>
|
||||||
url: "https://one.example.com",
|
url: "https://one.example.com",
|
||||||
accessToken: AccessToken.make("at_1"),
|
accessToken: AccessToken.make("at_1"),
|
||||||
refreshToken: RefreshToken.make("rt_1"),
|
refreshToken: RefreshToken.make("rt_1"),
|
||||||
expiry: Date.now() + 10 * 60_000,
|
expiry: Date.now() + outsideEagerRefreshWindow,
|
||||||
orgID: Option.none(),
|
orgID: Option.none(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -75,7 +78,7 @@ it.live("orgsByAccount groups orgs per account", () =>
|
||||||
url: "https://two.example.com",
|
url: "https://two.example.com",
|
||||||
accessToken: AccessToken.make("at_2"),
|
accessToken: AccessToken.make("at_2"),
|
||||||
refreshToken: RefreshToken.make("rt_2"),
|
refreshToken: RefreshToken.make("rt_2"),
|
||||||
expiry: Date.now() + 10 * 60_000,
|
expiry: Date.now() + outsideEagerRefreshWindow,
|
||||||
orgID: Option.none(),
|
orgID: Option.none(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -159,7 +162,7 @@ it.live("token refreshes before expiry when inside the eager refresh window", ()
|
||||||
url: "https://one.example.com",
|
url: "https://one.example.com",
|
||||||
accessToken: AccessToken.make("at_old"),
|
accessToken: AccessToken.make("at_old"),
|
||||||
refreshToken: RefreshToken.make("rt_old"),
|
refreshToken: RefreshToken.make("rt_old"),
|
||||||
expiry: Date.now() + 60_000,
|
expiry: Date.now() + insideEagerRefreshWindow,
|
||||||
orgID: Option.none(),
|
orgID: Option.none(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -267,7 +270,7 @@ it.live("config sends the selected org header", () =>
|
||||||
url: "https://one.example.com",
|
url: "https://one.example.com",
|
||||||
accessToken: AccessToken.make("at_1"),
|
accessToken: AccessToken.make("at_1"),
|
||||||
refreshToken: RefreshToken.make("rt_1"),
|
refreshToken: RefreshToken.make("rt_1"),
|
||||||
expiry: Date.now() + 10 * 60_000,
|
expiry: Date.now() + outsideEagerRefreshWindow,
|
||||||
orgID: Option.none(),
|
orgID: Option.none(),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue