two_factor_authentication_page.dart 4.6 KB

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