getAccessToken method

Future<String> getAccessToken()

Implementation

Future<String> getAccessToken() async {
  final token = await _storage.read(key: _accessTokenKey);
  if (token == null || token.isEmpty) {
    throw Exception('Not signed in to OpenAI.');
  }

  final expiryStr = await _storage.read(key: _expiryKey);
  final expiry = expiryStr == null ? null : DateTime.tryParse(expiryStr);
  // Missing expiry is treated like "already expired" — returning a stale
  // token on an unknown-age install is worse than paying for a refresh.
  final needsRefresh =
      expiry == null ||
      expiry.isBefore(DateTime.now().add(const Duration(minutes: 5)));

  if (needsRefresh) {
    final refreshToken = await _storage.read(key: _refreshTokenKey);
    if (refreshToken != null && refreshToken.isNotEmpty) {
      await _refreshToken();
      final refreshed = await _storage.read(key: _accessTokenKey);
      if (refreshed == null || refreshed.isEmpty) {
        throw Exception('Not signed in to OpenAI.');
      }
      return refreshed;
    }
    // No refresh token. Expiry known + expired → truly stuck; surface as
    // signed-out. Expiry unknown (legacy) → fall through and return the
    // stored token, matching the pre-hardening behavior.
    if (expiry != null) {
      throw Exception('Not signed in to OpenAI.');
    }
  }

  return token;
}