two_factor_authentication_page.dart 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. import 'package:ente_auth/l10n/l10n.dart';
  2. import 'package:ente_auth/models/account/two_factor.dart';
  3. import 'package:ente_auth/services/user_service.dart';
  4. import 'package:ente_auth/ui/lifecycle_event_handler.dart';
  5. import 'package:flutter/material.dart';
  6. import 'package:flutter/services.dart';
  7. import 'package:pinput/pinput.dart';
  8. class TwoFactorAuthenticationPage extends StatefulWidget {
  9. final String sessionID;
  10. const TwoFactorAuthenticationPage(this.sessionID, {super.key});
  11. @override
  12. State<TwoFactorAuthenticationPage> createState() =>
  13. _TwoFactorAuthenticationPageState();
  14. }
  15. class _TwoFactorAuthenticationPageState
  16. extends State<TwoFactorAuthenticationPage> {
  17. final _pinController = TextEditingController();
  18. String _code = "";
  19. late LifecycleEventHandler _lifecycleEventHandler;
  20. @override
  21. void initState() {
  22. _lifecycleEventHandler = LifecycleEventHandler(
  23. resumeCallBack: () async {
  24. if (mounted) {
  25. final data = await Clipboard.getData(Clipboard.kTextPlain);
  26. if (data != null && data.text != null && data.text!.length == 6) {
  27. _pinController.text = data.text!;
  28. }
  29. }
  30. },
  31. );
  32. WidgetsBinding.instance.addObserver(_lifecycleEventHandler);
  33. super.initState();
  34. }
  35. @override
  36. void dispose() {
  37. WidgetsBinding.instance.removeObserver(_lifecycleEventHandler);
  38. super.dispose();
  39. }
  40. @override
  41. Widget build(BuildContext context) {
  42. final l10n = context.l10n;
  43. return Scaffold(
  44. appBar: AppBar(
  45. title: Text(
  46. l10n.twoFactorAuthTitle,
  47. ),
  48. ),
  49. body: _getBody(),
  50. );
  51. }
  52. Widget _getBody() {
  53. final l10n = context.l10n;
  54. final pinPutDecoration = BoxDecoration(
  55. border: Border.all(
  56. color: Theme.of(context)
  57. .inputDecorationTheme
  58. .focusedBorder!
  59. .borderSide
  60. .color,
  61. ),
  62. borderRadius: BorderRadius.circular(15.0),
  63. );
  64. return Column(
  65. crossAxisAlignment: CrossAxisAlignment.stretch,
  66. mainAxisAlignment: MainAxisAlignment.center,
  67. mainAxisSize: MainAxisSize.max,
  68. children: [
  69. Text(
  70. l10n.enterCodeHint,
  71. style: const TextStyle(
  72. height: 1.4,
  73. fontSize: 16,
  74. ),
  75. textAlign: TextAlign.center,
  76. ),
  77. const Padding(padding: EdgeInsets.all(32)),
  78. Padding(
  79. padding: const EdgeInsets.fromLTRB(40, 0, 40, 0),
  80. child: Pinput(
  81. onSubmitted: (String code) {
  82. _verifyTwoFactorCode(code);
  83. },
  84. length: 6,
  85. defaultPinTheme: const PinTheme(),
  86. submittedPinTheme: PinTheme(
  87. decoration: pinPutDecoration.copyWith(
  88. borderRadius: BorderRadius.circular(20.0),
  89. ),
  90. ),
  91. focusedPinTheme: PinTheme(
  92. decoration: pinPutDecoration,
  93. ),
  94. followingPinTheme: PinTheme(
  95. decoration: pinPutDecoration.copyWith(
  96. borderRadius: BorderRadius.circular(5.0),
  97. ),
  98. ),
  99. onChanged: (String pin) {
  100. setState(() {
  101. _code = pin;
  102. });
  103. },
  104. controller: _pinController,
  105. autofocus: true,
  106. ),
  107. ),
  108. const Padding(padding: EdgeInsets.all(24)),
  109. Container(
  110. padding: const EdgeInsets.fromLTRB(80, 0, 80, 0),
  111. width: double.infinity,
  112. height: 64,
  113. child: OutlinedButton(
  114. onPressed: _code.length == 6
  115. ? () async {
  116. await _verifyTwoFactorCode(_code);
  117. }
  118. : null,
  119. child: Text(l10n.verify),
  120. ),
  121. ),
  122. const Padding(padding: EdgeInsets.all(30)),
  123. GestureDetector(
  124. behavior: HitTestBehavior.opaque,
  125. onTap: () {
  126. UserService.instance.recoverTwoFactor(
  127. context,
  128. widget.sessionID,
  129. TwoFactorType.totp,
  130. );
  131. },
  132. child: Container(
  133. padding: const EdgeInsets.all(10),
  134. child: Center(
  135. child: Text(
  136. l10n.lostDeviceTitle,
  137. style: const TextStyle(
  138. decoration: TextDecoration.underline,
  139. fontSize: 12,
  140. ),
  141. ),
  142. ),
  143. ),
  144. ),
  145. ],
  146. );
  147. }
  148. Future<void> _verifyTwoFactorCode(String code) async {
  149. await UserService.instance.verifyTwoFactor(context, widget.sessionID, code);
  150. }
  151. }