tryFromJson static method

AuthTokenModel? tryFromJson(
  1. Map<String, dynamic> json, {
  2. required bool convert,
})

Attempts to parse an AuthTokenModel from an OAuth2 token response map.

Validates json using JsonSentinel.validate before parsing. Returns null if validation fails or any field cast throws, making this safe to call on untrusted API responses.

The convert parameter controls how expiration values are stored:

  • true — treats expires_in and refresh_expires_in as durations in seconds and converts them to absolute millisecond timestamps by adding to the current time. Use this when parsing a fresh API response.
  • false — stores the raw values as-is. Use this when deserialising from SecureStorage, where values are already absolute timestamps.
// Fresh API response.
final token = AuthTokenModel.tryFromJson(apiResponse, convert: true);

// Restoring from secure storage.
final stored = AuthTokenModel.tryFromJson(storedJson, convert: false);

Implementation

static AuthTokenModel? tryFromJson(Map<String, dynamic> json, {required bool convert}) {
  final Map<String, List<Type?>?> expectedTypes = <String, List<Type?>?>{
    "access_token": <Type?>[String],
    "refresh_token": <Type?>[String],
    "token_type": <Type?>[String],
    "expires_in": <Type?>[int],
    "refresh_expires_in": <Type?>[int],
  };

  final bool ok = JsonSentinel.validate(json: json, expectedTypes: expectedTypes, context: 'AuthTokenModel', escalate: true).isValid;

  if (!ok) return null;

  try {
    final String accessToken = json["access_token"] as String;
    final String refreshToken = json["refresh_token"] as String;
    final String tokenType = json["token_type"] as String;
    final int expiresInSeconds = json["expires_in"] as int;
    final int refreshExpiresInSeconds = json["refresh_expires_in"] as int;

    final int nowMs = DateTime.now().millisecondsSinceEpoch;

    final int expiresIn = convert ? (nowMs + (expiresInSeconds * 1000)) : expiresInSeconds;

    final int refreshExpiresIn = convert ? (nowMs + (refreshExpiresInSeconds * 1000)) : refreshExpiresInSeconds;

    return AuthTokenModel(
      accessToken: accessToken,
      refreshToken: refreshToken,
      tokenType: tokenType,
      expiresIn: expiresIn,
      refreshExpiresIn: refreshExpiresIn,
    );
  } catch (e, st) {
    JsonSentinel.logger?.call(
      '[AuthTokenModel] Parsing failed after validation (skipping token).',
      error: e,
      stackTrace: st,
      extras: const <String, Object?>{'model': 'AuthTokenModel'},
      escalate: true,
    );
    return null;
  }
}