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;
var resolvedSecret = token;
var providedApiKey = apiKey;
providedApiKey ??= const String.fromEnvironment('MESHAGENT_API_KEY');
if (providedApiKey.isNotEmpty) {
resolvedApiKey = parseApiKey(providedApiKey);
resolvedSecret = resolvedApiKey.secret;
}
final usingDefaultSecret = resolvedSecret == null;
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;
}
// Match Python behavior: if exporting with default secret, drop kid.
if (usingDefaultSecret && payload.containsKey('kid')) {
payload.remove('kid');
}
// Merge extras
final merged = <String, dynamic>{...payload, if (extra != null) ...extra!};
if (expiration != null) {
// 'exp' is a NumericDate (seconds since epoch)
merged['exp'] = (expiration.millisecondsSinceEpoch / 1000).floor();
}
final jwt = JWT(merged);
return jwt.sign(SecretKey(resolvedSecret), algorithm: JWTAlgorithm.HS256);
}