Otp.NET icon indicating copy to clipboard operation
Otp.NET copied to clipboard

TOTP window start

Open miloush opened this issue 1 year ago • 7 comments

We have Totp.RemainingSecods() which however does not indicate when the current time window started. There is no way to find out the steps that the Totp was created with (#45) so developers cannot even calculate it manually given an instance of Totp.

How about adding Totp.ElapsedSeconds()?

miloush avatar May 01 '23 23:05 miloush

We have Totp.RemainingSecods() which however does not indicate when the current time window started. There is no way to find out the steps that the Totp was created with (#45) so developers cannot even calculate it manually given an instance of Totp.

How about adding Totp.ElapsedSeconds()?

Why not use the totp.RemainingSeconds() method to get the remaining seconds and then subtracting it from DateTime.UtcNow to get the elapsed seconds?

nabeel-servcorp avatar Jul 20 '23 02:07 nabeel-servcorp

That doesn't give you any meaningful information, does it? If the remaining seconds is 5 and UtcNow is 12:34:56, then subtracting gives you 12:34:51, regardless of when the window actually started..

miloush avatar Jul 20 '23 05:07 miloush

The step time (window) should already be known when creating your TOTP object, so you will need a reference to that value to do the calculation. Maybe this snippet will help point you in the right direction:

var window = 30;
var totp = new OtpNet.Totp(
    step:  window,
    secretKey: xxxxxxx)
...
var dtNow = DateTime.UtcNow;
var remainingSeconds = totp.RemainingSeconds();
Console.WriteLine($"Window:       {window} seconds");
Console.WriteLine($"Remaining:    {remainingSeconds}");
Console.WriteLine($"Now:          {dtNow.ToLocalTime()}");
Console.WriteLine($"Window start: {dtNow.AddSeconds(remainingSeconds - window).ToLocalTime()}");

damiarnold avatar Jul 20 '23 12:07 damiarnold

Thank you @damiarnold I understand that, but I cannot do that if I am not the one who created the Totp object. And when I have several Totp objects I created, I have to keep a table of the objects and their windows, while the Totp object already has all the information needed.

miloush avatar Jul 20 '23 12:07 miloush

Fair enough - I agree that convenience properties would help in many of these cases - especially if you are not the one creating the objects and do not have access to their initial constructor argument values.

Short of submitting a pull request, you could always (as a last resort) use reflection on the private members, but this may be a non-starter for your use case, and is certainly not my go-to solution unless there is no other reasonable choice.

@kspearrin any thoughts on merits of providing public property wrappers for private members such as the Totp members _step, _totpSize, _correctedTime as well as the Otp _hashMode member? There may be a couple in the Hotp class I haven't peeked into yet, but if this seems reasonable, I could possibly work on a pull request in the next week or so for "convenience properties" for reasonable members that are currently private/protected, as the workarounds many of us use now do have some limitations.

damiarnold avatar Jul 20 '23 13:07 damiarnold

I did #49. I also looked into ElapsedSeconds() and since RemainingSeconds() returns integral value (as opposed to TimeSpan or double), I don't find enough value in ElapsedSeconds() if #49 is merged since you will have acces to the Step property and can calculate that easily (RemainingSeconds() in practice already calculates Step - ElapsedSeconds()).

For people who are looking for actual elapsed or remaining time (e.g. to update the OTP displayed in UI), it would be more useful to have the window start as DateTime available.

miloush avatar Jul 20 '23 13:07 miloush

so developers cannot even calculate it manually given an instance of Totp

You can calculate the step manually from only RemainingSeconds() , but it would take up to step + 2 seconds to do so.

And when I have several Totp objects I created, I have to keep a table of the objects and their windows, while the Totp object already has all the information needed.

The only reason for a table would be if each object had a unique step value.

The RemainingSeconds() and window start times will be identical for all instances using the same step, time source and time correction offset.

Note: the delay parameter or step is ignored on some authenticator apps and hardcoded to 30 seconds.

The following methods return the start of the window for a given step. The period parameter calculates the start of the next window(s) value can be negative for previous windows.

A period of 1 would give just past the end time of the current window.

DateTimeOffset GetCurrentWindowStart(long step = 30, int period = 0)
{
    return DateTimeOffset.FromUnixTimeSeconds((
                         DateTimeOffset.UtcNow().ToUnixTimeSeconds()
                         / step + period ) * step);
}

Or if you want the value in the local time zone:

DateTimeOffset GetCurrentWindowStartLocal(long step = 30, int period = 0)
{
    return ConvertTime(DateTimeOffset.FromUnixTimeSeconds((
                         DateTimeOffset.UtcNow().ToUnixTimeSeconds()
                         / step + period) * step), TimeZoneInfo.Local);
}

Or just the Time Parameter:

long GetCurrentWindowStart(long step = 30, long period = 0)
{
    return (DateTimeOffset.UtcNow().ToUnixTimeSeconds() / step + period ) * step;
}

stromkos avatar Feb 13 '24 16:02 stromkos