refresh method
Refreshes session using the DPoP-bound refresh token flow.
The authorization server is re-discovered from OAuthSession.issuer.
The DPoP key pair from the session is reused. The given session is
never mutated; a brand-new OAuthSession is returned and stored.
If the server rejects the refresh token with invalid_grant, the
session is deleted from the OAuthSessionStore and an
OAuthSessionRevokedException is thrown so callers can route the user
back through authorize — unless the store has already rotated past the
token this call tried, in which case the rejection says only that the
tried token was spent and the current stored session is returned instead.
Throws:
- OAuthException when no refresh token is available or the request fails.
- OAuthSessionRevokedException when the session has been revoked.
Implementation
Future<OAuthSession> refresh(final OAuthSession session) async {
final refreshToken = session.refreshToken;
if (refreshToken == null || refreshToken.isEmpty) {
throw OAuthException('No refresh token available');
}
// Single-flight: coalesce concurrent refreshes of the same account onto
// one shared future so the rotating refresh token is POSTed exactly once.
final inFlight = _inFlightRefreshes[session.sub];
if (inFlight != null) return inFlight;
final future = _refresh(session, refreshToken);
_inFlightRefreshes[session.sub] = future;
try {
return await future;
} finally {
_inFlightRefreshes.remove(session.sub);
}
}