refreshAccessToken static method

Future<Map<String, dynamic>?> refreshAccessToken(
  1. String refreshToken, {
  2. bool rotateRefreshToken = true,
  3. String? ipAddress,
  4. String? userAgent,
  5. String? deviceName,
})

Exchange a refresh token for a new access token and optional rotated refresh token.

Returns null when refresh token is invalid/expired/revoked.

Implementation

static Future<Map<String, dynamic>?> refreshAccessToken(
  String refreshToken, {
  bool rotateRefreshToken = true,
  String? ipAddress,
  String? userAgent,
  String? deviceName,
}) async {
  if (!_config.enableRefreshTokens) return null;
  await ensureFrameworkTablesExist();

  final tokenHash = _hashRefreshToken(refreshToken);
  final rows = await DB.query(
    'SELECT id, user_id, expires_at, revoked_at FROM $_refreshTokensTable WHERE token_hash = ? LIMIT 1',
    positionalParams: [tokenHash],
  );
  if (rows.isEmpty) return null;

  final row = rows.first;
  if (row['revoked_at'] != null) return null;

  final expiresAtRaw = row['expires_at']?.toString();
  if (expiresAtRaw == null) return null;
  final expiresAt = DateTime.tryParse(expiresAtRaw);
  if (expiresAt == null || expiresAt.isBefore(DateTime.now())) {
    return null;
  }

  final userId = row['user_id']?.toString();
  if (userId == null || userId.isEmpty) return null;

  final user = await QueryBuilder(table: _config.table)
      .where('id', '=', userId)
      .limit(1)
      .first();
  if (user == null) return null;

  final cleanUser = _sanitizeUserData(user);
  final accessToken = _issueAccessToken(cleanUser);

  if (!rotateRefreshToken) {
    return {
      'user': cleanUser,
      'accessToken': accessToken,
      'token': accessToken,
    };
  }

  await DB.query(
    'UPDATE $_refreshTokensTable SET revoked_at = ? WHERE id = ?',
    positionalParams: [DateTime.now().toIso8601String(), row['id']],
  );

  final newRefreshToken = await _createRefreshTokenRecord(
    userId: userId,
    ipAddress: ipAddress,
    userAgent: userAgent,
    deviceName: deviceName,
  );

  return {
    'user': cleanUser,
    'accessToken': accessToken,
    'token': accessToken,
    'refreshToken': newRefreshToken,
  };
}