toJwt method

String toJwt({
  1. String? token,
  2. String? apiKey,
  3. DateTime? expiration,
})

Encodes as JWT (HS256). If token is null, tries compile-time env ('MESHAGENT_SECRET'). expiration adds 'exp' (seconds since epoch) to the payload. If apiKey is provided (or set via MESHAGENT_API_KEY), it determines the signing secret and ensures the payload contains the API key metadata.

Implementation

String toJwt({String? token, String? apiKey, DateTime? expiration}) {
  ApiKey? resolvedApiKey;
  final envApiKey = const String.fromEnvironment('MESHAGENT_API_KEY');
  final providedApiKey = apiKey != null && apiKey.isNotEmpty ? apiKey : (envApiKey.isNotEmpty ? envApiKey : null);
  final usedDefaultSecretPath = token == null && providedApiKey == null;

  var resolvedSecret = token;

  if (providedApiKey != null) {
    resolvedApiKey = parseApiKey(providedApiKey);
    resolvedSecret = resolvedApiKey.secret;
  }

  resolvedSecret ??= const String.fromEnvironment('MESHAGENT_SECRET');

  // Warn if missing ApiScope on newer versions (mirrors Python logger.warning)
  final hasApi = grants.any((g) => g.name == 'api');
  if (!hasApi && _semverCompare(version, '0.3.5') > 0) {
    // ignore: avoid_print
    print(
      'Warning: there is no ApiScope in the participant token; this participant will not be able to call the room API. Use addApiGrant to add an ApiScope.',
    );
  }

  final payload = Map<String, dynamic>.from(toJson());

  if (resolvedApiKey != null) {
    payload['kid'] = resolvedApiKey.id;
    payload['sub'] = resolvedApiKey.projectId;
  }

  if (usedDefaultSecretPath && payload.containsKey('kid')) {
    payload.remove('kid');
  }

  if (expiration != null) {
    // 'exp' is a NumericDate (seconds since epoch)
    payload['exp'] = (expiration.millisecondsSinceEpoch / 1000).floor();
  }

  final jwt = JWT(payload);
  return jwt.sign(SecretKey(resolvedSecret), algorithm: JWTAlgorithm.HS256);
}