mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-04-25 00:16:29 +02:00
refresh rowboat access token on every gateway request
Wire a custom fetch into the OpenRouter gateway provider so each outbound request resolves a fresh access token, instead of baking one token into the provider at turn start. Add a 60s expiry margin and serialize concurrent refreshes behind a single in-flight promise. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a80ef4d320
commit
a86f555cbb
3 changed files with 40 additions and 14 deletions
|
|
@ -216,12 +216,15 @@ export async function refreshTokens(
|
|||
return tokens;
|
||||
}
|
||||
|
||||
const EXPIRY_MARGIN_SECONDS = 60;
|
||||
|
||||
/**
|
||||
* Check if tokens are expired
|
||||
* Check if tokens are expired. Treats tokens as expired EXPIRY_MARGIN_SECONDS
|
||||
* before the real expiry to absorb clock skew and in-flight request latency.
|
||||
*/
|
||||
export function isTokenExpired(tokens: OAuthTokens): boolean {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
return tokens.expires_at <= now;
|
||||
return tokens.expires_at <= now + EXPIRY_MARGIN_SECONDS;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,18 +3,12 @@ import { IOAuthRepo } from './repo.js';
|
|||
import { IClientRegistrationRepo } from './client-repo.js';
|
||||
import { getProviderConfig } from './providers.js';
|
||||
import * as oauthClient from './oauth-client.js';
|
||||
import { OAuthTokens } from './types.js';
|
||||
|
||||
export async function getAccessToken(): Promise<string> {
|
||||
const oauthRepo = container.resolve<IOAuthRepo>('oauthRepo');
|
||||
const { tokens } = await oauthRepo.read('rowboat');
|
||||
if (!tokens) {
|
||||
throw new Error('Not signed into Rowboat');
|
||||
}
|
||||
|
||||
if (!oauthClient.isTokenExpired(tokens)) {
|
||||
return tokens.access_token;
|
||||
}
|
||||
let refreshInFlight: Promise<OAuthTokens> | null = null;
|
||||
|
||||
async function performRefresh(tokens: OAuthTokens): Promise<OAuthTokens> {
|
||||
console.log("Refreshing rowboat access token");
|
||||
if (!tokens.refresh_token) {
|
||||
throw new Error('Rowboat token expired and no refresh token available. Please sign in again.');
|
||||
}
|
||||
|
|
@ -40,7 +34,29 @@ export async function getAccessToken(): Promise<string> {
|
|||
tokens.refresh_token,
|
||||
tokens.scopes,
|
||||
);
|
||||
|
||||
const oauthRepo = container.resolve<IOAuthRepo>('oauthRepo');
|
||||
await oauthRepo.upsert('rowboat', { tokens: refreshed });
|
||||
|
||||
return refreshed;
|
||||
}
|
||||
|
||||
export async function getAccessToken(): Promise<string> {
|
||||
const oauthRepo = container.resolve<IOAuthRepo>('oauthRepo');
|
||||
const { tokens } = await oauthRepo.read('rowboat');
|
||||
if (!tokens) {
|
||||
throw new Error('Not signed into Rowboat');
|
||||
}
|
||||
|
||||
if (!oauthClient.isTokenExpired(tokens)) {
|
||||
return tokens.access_token;
|
||||
}
|
||||
|
||||
if (!refreshInFlight) {
|
||||
refreshInFlight = performRefresh(tokens).finally(() => {
|
||||
refreshInFlight = null;
|
||||
});
|
||||
}
|
||||
const refreshed = await refreshInFlight;
|
||||
return refreshed.access_token;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,18 @@ import { createOpenRouter } from '@openrouter/ai-sdk-provider';
|
|||
import { getAccessToken } from '../auth/tokens.js';
|
||||
import { API_URL } from '../config/env.js';
|
||||
|
||||
const authedFetch: typeof fetch = async (input, init) => {
|
||||
const token = await getAccessToken();
|
||||
const headers = new Headers(init?.headers);
|
||||
headers.set('Authorization', `Bearer ${token}`);
|
||||
return fetch(input, { ...init, headers });
|
||||
};
|
||||
|
||||
export async function getGatewayProvider(): Promise<ProviderV2> {
|
||||
const accessToken = await getAccessToken();
|
||||
return createOpenRouter({
|
||||
baseURL: `${API_URL}/v1/llm`,
|
||||
apiKey: accessToken,
|
||||
apiKey: 'managed-by-rowboat',
|
||||
fetch: authedFetch,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue