1

I'm writing a very simple 2FA application, which generates TOTP codes for a given secret key, digits, interval and algorithm.

However, I have a hard time with maths, and I do not know how to construct the algorithm for generating the TOTP code

I'm writing the whole app in BLoC pattern using the Flutter BLoC library.

Have a look at this video, the app generates a code which has an interval of 30 seconds, however the progress indicator doesn't start from 0 seconds, but it starts from a time every 30 seconds for example: 13:00:00 and when 30 seconds pass (13:00:30), it generates another code.

Video: https://streamable.com/u7vhcg

Now, when it comes to my application, it generates codes, but they're not synced. How do I do it like that, that it generates codes, but every code is synced?

Vide of my app: https://streamable.com/zn3pnf

As you see, the TOTP codes are not synced. How do I do that, so they will be synced?

Let me know if you want to see more code.

Code

totp_generator_bloc.dart

import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:dart_otp/dart_otp.dart';
import 'package:duckie/models/code/code_model.dart';
import 'package:hive/hive.dart';
import 'package:meta/meta.dart';

part 'totp_generator_event.dart';
part 'totp_generator_state.dart';

class TotpGeneratorBloc extends Bloc<TotpGeneratorEvent, TotpGeneratorState> {
  TotpGeneratorBloc() : super(TotpGeneratorInitial());

  @override
  Stream<TotpGeneratorState> mapEventToState(
    TotpGeneratorEvent event,
  ) async* {
    if (event is GetTotpItemsEvent) {
      Box box = Hive.box('totpmodel');
      List totpItems = [];

      for (var totpItem in box.values) {
        final String secret = totpItem.secret;
        final int digits = int.parse(totpItem.digits);
        final int interval = int.parse(totpItem.interval);
        final String algorithmString = totpItem.algorithm;
        final String accountName = totpItem.accountName;
        int timeStamp = totpItem.timeStamp;

        OTPAlgorithm algorithm = OTPAlgorithm.SHA1;

        switch (algorithmString) {
          case 'sha1':
            algorithm = OTPAlgorithm.SHA1;
            break;
          case 'sha256':
            algorithm = OTPAlgorithm.SHA256;
            break;
          case 'sha384':
            algorithm = OTPAlgorithm.SHA384;
            break;
          case 'sha512':
            algorithm = OTPAlgorithm.SHA512;
            break;
        }

        final TOTP totp = TOTP(
          secret: secret,
          digits: digits,
          interval: interval,
          algorithm: algorithm,
        );

        try {
          final String otp =
              totp.value(date: DateTime.fromMillisecondsSinceEpoch(timeStamp));

          totpItems.add(CodeModel(otp, interval, 0, accountName));

          yield TotpGeneratorFinal(totpItems);
        } catch (error) {
          yield TotpGeneratorError('totp-fail-title', 'totp-fail-content');
        }
      }
    }
  }
}

szakes1
  • 794
  • 3
  • 16
  • 35

1 Answers1

0

Isn't it kind of a problem of subscribing to a Timer.periodic?

Something outside your bloc should:

  1. read current seconds from the system clock (datetime) (you will have starting position for the counter)

  2. subscribe to Timer.periodic(Duration(seconds: 1), (Timer t) => _handleClocksTick());

  3. _handleClocksTick() - checks if incremented counter is divisible by 30?

3.1. if yes, then _bloc.add(GetNewCodeEvent); reset(counter);

Hm... I think this your counter should be handled by your bloc. This would be even better solution.

like first solution in this case Show running clock on flutter

user61253764
  • 478
  • 5
  • 9