Migrate a GitHub OAuth app to expiring access tokens, safe refresh-token rotation, explicit callback URLs, and observable failure recovery.
A GitHub OAuth refresh token migration service moves an integration from long-lived access credentials to expiring access tokens and controlled refresh-token rotation. The change affects storage, concurrency, retries, callback validation, user sessions, monitoring, and support—not only the authorization request.
GitHub's August 2026 platform update introduced expiring access tokens, refresh tokens, multiple callback URLs, and explicit wildcard controls. New applications receive short-lived behavior by default, while existing applications can test the flow before enforcing it for every client.
Map the current authorization lifecycle
Document how a user connects the application, where the returned token is stored, which services use it, and what happens when access is revoked. Legacy integrations often spread one credential across web requests, background jobs, webhooks, exports, and support tools.
The map should identify:
OAuth application registrations and owners.
Callback URLs for local, preview, staging, and production environments.
Services that initiate or complete authorization.
Databases, caches, queues, and logs that may contain tokens.
Background workers that call GitHub without an active user session.
Existing encryption, key rotation, and retention controls.
Error handling for revoked or invalid credentials.
User-facing reconnection and account-removal paths.
Search code and operational systems, not only documentation. A forgotten worker can fail hours after the interactive flow appears successful.
Design token storage for rotation
GitHub's announced flow uses a short-lived access token and a longer-lived refresh token. Treat both as secrets, but model their purposes separately. Store expiry timestamps, granted scopes, connection owner, and token version beside encrypted values.
The data model needs to support:
Atomic replacement of an old token pair with a new pair.
Detection of concurrent refresh attempts.
Encryption with a key managed outside application source.
Revocation and deletion for one connection.
Audit metadata without logging token values.
Reconnection when rotation cannot recover.
Multiple organizations or installations per user where applicable.
Migration of records created under the legacy flow.
Avoid returning tokens to browser code unless the architecture explicitly requires it and the risk is understood. Server-side calls usually provide a narrower exposure boundary.
Prevent refresh races
Several requests may discover an expired token at the same time. If all refresh concurrently, one process can overwrite a newer pair or continue using a credential that has already been replaced. Coordinate refresh at the connection level.
A safe flow is:
Read the encrypted token record and expiry.
Reuse the access token if it remains valid with a safety margin.
Acquire a short lock or compare-and-swap version before refreshing.
Exchange the refresh token through the provider endpoint.
Persist the complete new pair atomically.
Release the lock and retry the original API request once.
Route invalid-grant failures to reconnection rather than infinite retry.
Test crashes between exchange and persistence. The support path must explain what the user sees if the old pair is no longer usable and the new one was not stored.
Roll out without forcing every user at once
GitHub documents using the offline_access scope to request the short-lived pattern during testing. Begin with internal accounts and a small external cohort. Keep feature flags at the connection or tenant level so failures remain contained.
The official authorizing OAuth apps documentation should be the implementation reference for request parameters and provider behavior. Verify the current documentation during delivery instead of copying examples from an old integration.
Measure:
Authorization completion rate.
Successful refreshes and refresh latency.
Concurrent refresh conflicts.
Invalid, revoked, and expired credential failures.
Background jobs delayed by token renewal.
User reconnection rate.
API failures before and after migration.
Connections still using the legacy pattern.
Enforce short-lived tokens globally only after the selected cohort remains stable through several expiry cycles.
Review every callback URL
Multiple callback URLs can simplify environment management, but every registered destination becomes part of the security boundary. Register exact HTTPS URLs where practical and keep development callbacks out of production registrations.
For each callback:
Confirm ownership of the scheme, host, port, and path.
Validate
statesecurely and bind it to the initiating session.Prevent open redirects after the callback completes.
Protect authorization codes from logs, analytics, and referrers.
Reject callbacks started for another tenant or environment.
Remove retired domains and preview hosts.
Monitor registration changes as privileged configuration.
Test error and cancellation behavior.
Wildcard matching deserves extra scrutiny because user-controlled subdomains or routes can receive an authorization code. Enable it only when the hosting and routing model guarantees control over every matching destination.
Build recovery into the product experience
Token expiry and revocation are normal lifecycle events. Users need a clear message that distinguishes reconnection from a general outage. Preserve unsent work where possible and avoid repeatedly triggering failed background actions.
Support tooling should show connection state, last successful refresh, granted scopes, recent provider error category, and the correct reconnection owner. It should never reveal credential values.
The software integration services overview covers broader ownership and delivery questions. For incident containment around privileged integrations, use the agent cyber containment guide.
Test cases that should block release
Include more than the happy authorization redirect:
Two workers refresh the same connection concurrently.
The access token expires during a paginated operation.
The refresh token is revoked by the user.
Storage succeeds but queue acknowledgement fails.
Encryption keys rotate while old records remain active.
A callback arrives with missing or incorrect state.
A wildcard destination contains user-controlled routing.
A user disconnects while a background job is in progress.
The provider is unavailable during refresh.
A legacy SDK does not understand the new response.
Each case needs an expected system state, user message, retry rule, and operator signal.
What the migration service should deliver
A complete engagement leaves an authorization-flow diagram, token data model, encrypted migration, refresh coordinator, callback allowlist, automated tests, cohort rollout controls, monitoring, user reconnection experience, and rollback procedure.
The provider should also identify credentials in logs or unsupported storage and create a remediation record. Migrating token lifetime while leaving exposed secrets elsewhere is incomplete.
Frequently asked questions
Must every existing user authorize again?
That depends on the current flow, SDK, scopes, and rollout design. Test a representative legacy connection before deciding. Some failures will still require explicit reconnection.
Should callback wildcards be enabled for tenant subdomains?
Only when every matching host and route is controlled by the application. Exact callbacks are easier to reason about and should remain the default choice.
Can refresh logic live in every API client?
Centralizing renewal per connection usually reduces races and inconsistent storage. Individual clients can request a valid credential from that coordinator instead of implementing rotation independently.
Explore Voquarn's integration services or book an authentication migration review for a staged rollout plan.
Written by
Moueen Togarvi
Founder & CEO at Voquarn Code, focused on product engineering, search growth, and practical AI systems.
