toJwt method
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);
}