108 lines
3.3 KiB
Dart
108 lines
3.3 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:marianum_mobile/auth_link/device_binding.dart';
|
|
import 'package:marianum_mobile/auth_link/guardian_login_link.dart';
|
|
import 'package:marianum_mobile/auth_link/pending_guardian_request.dart';
|
|
|
|
void main() {
|
|
final live = Uri.parse('https://connect.marianum-fulda.de');
|
|
|
|
group('GuardianLoginLink.parse', () {
|
|
test('accepts a link of the active server', () {
|
|
final link = GuardianLoginLink.parse(
|
|
Uri.parse(
|
|
'https://connect.marianum-fulda.de/app/guardian-login?rid=r1<=t1',
|
|
),
|
|
apiBase: live,
|
|
);
|
|
expect(link?.requestId, 'r1');
|
|
expect(link?.linkToken, 't1');
|
|
});
|
|
|
|
test('rejects links of another server', () {
|
|
expect(
|
|
GuardianLoginLink.parse(
|
|
Uri.parse(
|
|
'https://connect-beta.marianum-fulda.de/app/guardian-login?rid=r<=t',
|
|
),
|
|
apiBase: live,
|
|
),
|
|
isNull,
|
|
);
|
|
});
|
|
|
|
test('respects a path prefix of a custom server', () {
|
|
final custom = Uri.parse('https://dev.example.org/connect/');
|
|
expect(
|
|
GuardianLoginLink.parse(
|
|
Uri.parse(
|
|
'https://dev.example.org/connect/app/guardian-login?rid=r<=t',
|
|
),
|
|
apiBase: custom,
|
|
),
|
|
isNotNull,
|
|
);
|
|
});
|
|
|
|
test('rejects other paths, plain http and missing parameters', () {
|
|
for (final raw in [
|
|
'https://connect.marianum-fulda.de/app/other?rid=r<=t',
|
|
'http://connect.marianum-fulda.de/app/guardian-login?rid=r<=t',
|
|
'https://connect.marianum-fulda.de/app/guardian-login?rid=r',
|
|
'https://connect.marianum-fulda.de/app/guardian-login?lt=t',
|
|
]) {
|
|
expect(
|
|
GuardianLoginLink.parse(Uri.parse(raw), apiBase: live),
|
|
isNull,
|
|
reason: raw,
|
|
);
|
|
}
|
|
});
|
|
});
|
|
|
|
group('DeviceBinding', () {
|
|
test('challenge is the base64url SHA-256 of the secret, unpadded', () {
|
|
// RFC 7636 appendix B test vector.
|
|
expect(
|
|
DeviceBinding.challengeFor(
|
|
'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk',
|
|
),
|
|
'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM',
|
|
);
|
|
});
|
|
|
|
test('secrets are random and URL-safe', () {
|
|
final a = DeviceBinding.generateSecret();
|
|
final b = DeviceBinding.generateSecret();
|
|
expect(a, isNot(b));
|
|
expect(a, matches(RegExp(r'^[A-Za-z0-9_-]{43}$')));
|
|
});
|
|
});
|
|
|
|
group('PendingGuardianRequest', () {
|
|
final request = PendingGuardianRequest(
|
|
requestId: 'r1',
|
|
email: 'e@x.de',
|
|
deviceSecret: 's',
|
|
expiresAt: DateTime.utc(2026, 9, 19, 12, 15),
|
|
resendAvailableAt: DateTime.utc(2026, 9, 19, 12, 1),
|
|
);
|
|
|
|
test('round-trips through JSON', () {
|
|
final copy = PendingGuardianRequest.fromJson(request.toJson())!;
|
|
expect(copy.requestId, 'r1');
|
|
expect(copy.email, 'e@x.de');
|
|
expect(copy.expiresAt, request.expiresAt);
|
|
expect(copy.codeLength, 6);
|
|
});
|
|
|
|
test('corrupt JSON reads as absent', () {
|
|
expect(PendingGuardianRequest.fromJson({'requestId': 1}), isNull);
|
|
});
|
|
|
|
test('expires at expiresAt', () {
|
|
expect(request.isExpired(DateTime.utc(2026, 9, 19, 12, 14)), isFalse);
|
|
expect(request.isExpired(DateTime.utc(2026, 9, 19, 12, 15)), isTrue);
|
|
});
|
|
});
|
|
}
|