Renew before expiry
Refresh a token, lease, or session a few minutes before it expires. Worked examples for Google and Slack v2 OAuth (with refresh-token rotation); the same shape applies to GitHub Apps, presigned URLs, and any time-bound credential.
Use this to
- Refresh a Google OAuth access token 5 minutes before its expiry_date
- Refresh a Slack v2 OAuth token before the 12-hour access window closes (refresh tokens always rotate)
- Renew a GitHub App installation token before its 1-hour TTL expires
- Re-sign a presigned S3 upload URL ahead of its expiration
Code
Google OAuth: schedule a renewal before expiry_date
// after first authorizing, schedule a renewal 5 minutes before expiry await dk.schedule("refresh-google", { key: account.id, at: new Date(tokens.expiry_date - 5 * 60 * 1000), }); // handler refreshes, persists, reschedules. // Google may rotate the refresh_token. fall back to the stored // one if the refresh response omits it (common case). dk.handle("refresh-google", async ({ key, reschedule }) => { const stored = await db.googleTokens.find(key); oauth2Client.setCredentials({ refresh_token: stored.refresh_token }); const { credentials } = await oauth2Client.refreshAccessToken(); await db.googleTokens.update(key, { access_token: credentials.access_token, refresh_token: credentials.refresh_token ?? stored.refresh_token, expiry_date: credentials.expiry_date, }); reschedule({ at: new Date(credentials.expiry_date - 5 * 60 * 1000) }); });
Slack v2: refresh_token rotates on every refresh
// Slack v2 with token rotation enabled. the refresh_token is // single-use and rotates on every call, so persist both tokens // or the next renewal will fail. dk.handle("refresh-slack", async ({ key, reschedule }) => { const stored = await db.slackTokens.find(key); const res = await fetch("https://slack.com/api/oauth.v2.access", { method: "POST", body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: stored.refresh_token, client_id: process.env.SLACK_CLIENT_ID!, client_secret: process.env.SLACK_CLIENT_SECRET!, }), }); const data = await res.json(); await db.slackTokens.update(key, { access_token: data.access_token, refresh_token: data.refresh_token, // mandatory rotation }); // renew at 90% of the new access token's lifetime reschedule({ delay: `${Math.floor(data.expires_in * 0.9)}s` }); });
npm install delaykit
bun add delaykit