import 'dart:convert'; import 'dart:math'; import 'package:crypto/crypto.dart'; /// PKCE (RFC 7636) verifier/challenge pair for Dropbox's and OneDrive's /// OAuth2 Authorization Code flow — proves to the token endpoint that the /// app completing the exchange is the same one that started the browser /// redirect, without needing an embedded client secret (appropriate for a /// public/mobile client, since a secret can't actually be kept secret in /// a distributed app binary). class PkcePair { final String codeVerifier; final String codeChallenge; PkcePair._(this.codeVerifier, this.codeChallenge); factory PkcePair.generate() { final verifier = _randomUrlSafeString(64); final challenge = base64Url.encode(sha256.convert(utf8.encode(verifier)).bytes).replaceAll('=', ''); return PkcePair._(verifier, challenge); } static String _randomUrlSafeString(int length) { // RFC 7636's unreserved character set for a code_verifier. const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~'; final random = Random.secure(); return List.generate(length, (_) => chars[random.nextInt(chars.length)]).join(); } }