import 'dart:convert'; import 'dart:math'; import 'package:crypto/crypto.dart'; /// Binds a guardian login request to the device that started it (PKCE-style): /// the request carries only [challengeFor] of a secret that never leaves the /// device, verification sends the secret itself. A mail link opened on another /// device therefore cannot complete the login. abstract final class DeviceBinding { static String generateSecret([Random? random]) { final rng = random ?? Random.secure(); final bytes = List.generate(32, (_) => rng.nextInt(256)); return _base64UrlNoPad(bytes); } static String challengeFor(String secret) => _base64UrlNoPad(sha256.convert(utf8.encode(secret)).bytes); static String _base64UrlNoPad(List bytes) => base64Url.encode(bytes).replaceAll('=', ''); }