spomky-labs / otphp Goto Github PK
View Code? Open in Web Editor NEW:closed_lock_with_key: A PHP library for generating one time passwords according to RFC 4226 (HOTP) and the RFC 6238 (TOTP)
License: MIT License
:closed_lock_with_key: A PHP library for generating one time passwords according to RFC 4226 (HOTP) and the RFC 6238 (TOTP)
License: MIT License
Q | A |
---|---|
Bug report? | no |
Feature request? | no |
BC Break report? | no |
RFC? / Specification | no |
Library version | 10.0(.1) |
Hey folks,
I was wondering whether it was possible to generate recovery codes with this library. I'll have to admit that I can't find much info on the subject of generating recovery codes either, so maybe this is not the job of the library but rather something I have create myself.
I was hoping to create my personal TOTP codes to not have to rely on services like Google Authenticator or Authy. I have setup your script and it's outputting stuff, works fine. What I was wondering is how to get from the QR code found in most logins when beginning the 2FA process to the TOTP to verify and eventually use to login.
I'm currently testing with Homebridge which shows the following QR code output:
otpauth://totp/Homebridge%20UI%20([some code]):[user name]?secret=[some random string]&period=30&digits=6&algorithm=SHA1&issuer=Homebridge%20UI%20([some code])
Can I insert the complete URI in to your script and output the verify TOTP or do I have to first cut it into pieces?
If I setup the issuer, the google authenticator do not recognize the key, if I remove the issuer the google authenticator add de account.
FreeOTP supports the image parameter in query string which allows to display an image associated with the OTP.
This library should handle with parameter.
Q | A |
---|---|
Bug report? | yes |
Feature request? | no |
BC Break report? | no |
RFC? / Specification | no |
Library version | 9.1 |
$otp = \OTPHP\TOTP::create(
$encoded, // Let the secret be defined by the class
60, // The period (60 seconds)
'sha1', // The digest algorithm
10 // The output will generate 10 digits
);
$result=$otp->verify($code,time());
I have faced a problem that the period an otp works is usually no more than 20 seconds when I actually set the period to 60.
I have checked the system timestamp, the time when an otp is generated and the time when the otp is verified has just a bit more than 20 seconds.
When generating the otp, I created an TOTP instance at that time. The user send thru the otp to another route after waiting around 20 seconds, this verify route has another function so I have another TOTP instance created after 20 seconds. Now, it doesn't verify the otp.
I have also checked the source code but just can't figure out what is going wrong.
I was only wondering how to or if it's possible to add the issuer when using the getQrCodeUri method.
Q | A |
---|---|
Bug report? | yes |
Feature request? | yes |
BC Break report? | no |
RFC? / Specification | no |
Library version | 10.0 |
How exactly is one supposed to get the output of a HOTP at a specific counter # before verification?
Use case: User sets up TOTP in Authenticator - but have the backup HOTP code sent to them via email or displayed on screen to be saved in whichever manner they choose.
I'm migrating from version 4, and I'm wondering why setSecret() method is private. Is it a bug or there's a good reason for that?
In development with error reporting turned on, this is getting in the way of generating provisioning URIs and pulling the label out of them for Ajax responses.
Consider:
$uri = 'otpauth://totp/My%20Test%20-%20Auth?secret=12345678901234';
$otp = \OTPHP\Factory::loadFromProvisioningUri($uri);
$label = $otp->getLabel();
echo $label;
Produces:
PHP Notice: Undefined offset: 1 in /tmp/otp/vendor/spomky-labs/otphp/src/Factory.php on line 103
PHP Stack trace:
PHP 1. {main}() /tmp/otp/test.php:0
PHP 2. OTPHP\Factory::loadFromProvisioningUri() /tmp/otp/test.php:16
PHP 3. OTPHP\Factory::createOTP() /tmp/otp/vendor/spomky-labs/otphp/src/Factory.php:31
PHP 4. OTPHP\Factory::getLabel() /tmp/otp/vendor/spomky-labs/otphp/src/Factory.php:88
PHP Notice: Undefined offset: 1 in /tmp/otp/vendor/spomky-labs/otphp/src/Factory.php on line 56
PHP Stack trace:
PHP 1. {main}() /tmp/otp/test.php:0
PHP 2. OTPHP\Factory::loadFromProvisioningUri() /tmp/otp/test.php:16
PHP 3. OTPHP\Factory::populateOTP() /tmp/otp/vendor/spomky-labs/otphp/src/Factory.php:33
string(13) "My Test - Auth"
It appears if the URI doesn't contain both an issuer AND a label, this warning will be generated.
The same error is also emitted from the populateOTP method in Factor.php when the path only contains a label:
list($issuer, $label) = explode(':', rawurldecode(mb_substr($data['path'], 1, null, '8bit')));
Q | A |
---|---|
Bug report? | no |
Feature request? | no |
BC Break report? | no |
RFC? / Specification | no |
Library version | 9.1 |
This is a question, not an issue.
Will this library allow a user to use an OTP more than once in the period of 30 seconds?
I would like to prevent reusing an OTP more than once in a period.
Is this a configurable option?
Q | A |
---|---|
Bug report? | no |
Feature request? | yes |
BC Break report? | no |
RFC? / Specification | no |
Library version | 8.3.x, 9.x |
The base 32 encoding library used by this project doesn't claim to protect against timing attacks.
It uses substr and strlen, which as per http://blog.ircmaxell.com/2014/11/its-all-about-time.html are not timing safe.
https://github.com/paragonie/constant_time_encoding is designed to prevent timing attacks
If you're interested in this change, could poke at it some.
Hi again Spomky,
I'm not complete sure how works the verify method on TOTP scenarios. By default the interval is set to 30, but then I tried with 180, but after 120 seconds the value generated with the method verify(), returns false instead of true. I was expecting verify returns true after 180..
Interval is not related with the expire time of the OTP in seconds?
Thanks in advance
Q | A |
---|---|
Bug report? | no |
Feature request? | no |
BC Break report? | no |
RFC? / Specification | no |
Library version | 10.1 |
Hello, i am trying to figure out how can i create OTP key which i will send via sms so my client can verify himself.
$totp = TOTP::create( null, // Let the secret be defined by the class 30, // The period (30 seconds) 'sha1', // The digest algorithm 8 // The output will generate 8 digits );
How can i get digits that create generated ?
When only verifying a TOTP code, it doesn't look like label is needed anywhere, yet it is a required constructor parameter for the TOTP class.
This means passing in a blank string or random junk for the sake of making it happy.
Can this be changed to make it neater? Otherwise a simple static function could be use that eliminates the need to construct a TOTP object just to call verify.
Q | A |
---|---|
Bug report? | no |
Feature request? | yes |
BC Break report? | no |
RFC? / Specification | no |
Library version | latest |
Physical TOTP tokens usually have no connection to the internet, so their internal clock will drift over time. We use Feitian c200 tokens, and they usually start out exact but slowly drift to about 7-8 window units over a few years. When logging in, we allow a window size of 2 to allow a little bit of drift since we last saw the token, and to allow the user a little bit of time to physically type the number into the browser.
verifyOtpWithWindow
will return a true / false if the password is within the window, but it won't tell us if it is near the start or the end of the window.
For longer term use of tokens, it is important to record this offset after each login so that we are effectively re-syncing the token and allowing the full width of the window for their next login. We have about 1500 tokens: many teams have a supply that sits in a box waiting for new people to sign up. So we have a sync process on setup that allows a window size of 10 in case the token has drifted a long way. But we also need a way to get the current offset from verifyOtpWithWindow
so that each time it is used for a real login we can stay in sync. Is this something you'd consider adding to your library?
We should get the option to expire the totp once it's successfully verified even if the otp expire time remain.
My problem is that I am setting 5 min otp expire time because it takes time to deliver via sms, but once it's verified within a minute if I send another otp it remain same (last otp expire time is not over yet). How to resolve this problem.
RIP QR code generation.
https://developers.google.com/chart/image/
Warning: This API is deprecated and is scheduled to be turned off on March 14, 2019. Please use the actively maintained Google Charts API instead.
Q | A |
---|---|
Bug report? | yes/no |
Feature request? | yes/no |
BC Break report? | yes/no |
RFC? / Specification | yes/no |
Library version | x.y(.z) |
It's not a good idea to recommend that users use the Google Graph API to generate QR codes - the OTP secret should be protected, but when putting the full secret in a request URI, it gets leaked. It's just as bad as doing something like ?user=bob&password=password123
. I really think you should remove that feature from this library. It promotes insecure usage.
The better alternative would be to use a JS library to render the QR code locally, or provide some PHP library to generate the image data as a base64
string to be rendered in the HTML.
Thanks for the lib!
It would be great if there was a static method to get a randomly generated secret.
What I'm doing now is something like this:
$secret = (new TOTP(''))->getSecret();
or simply taking the code that generates the random secret in the first place:
$secret = trim(Base32::encode(random_bytes(32)), '=');
It just doesn't seem nice and clean to me.
Would be nice to have something along the lines of:
$secret = (OTP | HOTP | TOTP)::getRandomSecret();
Would you welcome me PRing something? I've implemented it in my dev version and it's a small change.
Q | A |
---|---|
Documentation | yes |
Library version | 9+ |
Please, add information that you changed library for Base32 encoding to this file:
otphp/UPGRADE_v8-v9.md
- $encoded_secret = Base32\Base32::encode($secret);
+ $encoded_secret = ParagonIE\ConstantTime\Base32::encodeUpper($secret);
Please could you provide a better explanation to use this library? It would be great, I'm stuck building an example usage
<?php
require_once dirname(__FILE__).'/lib/OTPInterface.php';
require_once dirname(__FILE__).'/lib/TOTPInterface.php';
require_once dirname(__FILE__).'/lib/OTP.php';
require_once dirname(__FILE__).'/lib/TOTP.php';
use OTPHP\TOTP as TOTP;
//$test = new TOTP; // Fatal error: Cannot instantiate abstract class OTPHP\TOTP
class MyOtp extends TOTP{
}
$test = new MyOtp(); // Fatal error: Class MyOtp contains 7 abstract methods and must therefore be declared abstract or implement the remaining methods ...
It's clear I have some lack in terms of OOD here. Thanks in advance
Q | A |
---|---|
Bug report? | yes |
Feature request? | no |
BC Break report? | no |
RFC? / Specification | no |
Library version | 10.0 |
The default OTP period is 30 sec which I tried to chenge to 60 sec as per doc
$user_secret = load_user_secret($user);
$OTP = TOTP::create( $user_secret, 60 );
$current_otp = $OTP->now();
But at the time of verification by $OTP->verify($posted_otp)
, It returns false
most of the time.
Could you please help on this?
Regards
Ankit
First of all: Thank you for an excellent library!
I was pretty new to two factor authentication when I started implementing it and used this library, and I misunderstood the documentation in a way I reckon could apply to other "noobs" as well:
In the Use.md, chapter "My first OTPs" there is an example code regarding TOTP. If you read this with limited knowledge of how TOTP is implemented, it is easy to assume that the code (123456) will always be valid for 30 seconds. The example states "At least 30 seconds later", while the truth is that in this example, verify() could return false after just a couple of seconds, if the code was generated at the very end of the current period. To be able to guarantee "At least 30 seconds later" (which is what you'd normally always want when using this in a real life system), you have to set the $window argument of validate() to 1.
I struggled quite some time understanding this, and could not figure out why my codes some times worked and some times failed long before 30 seconds. It was not until I read the docs and source code in detail I figured this out.
So the documentation/example should maybe be updated to explain this more precise, hopefully saving others from the same frustration I encountered :)
Q | A |
---|---|
Bug report? | no |
Feature request? | yes |
BC Break report? | no |
RFC? / Specification | no |
Library version | master |
This packages blockes me for using https://github.com/scheb/2fa-google-authenticator because of PHP 8.
Problem 1
- beberlei/assert[v3.0.0, ..., v3.2.7] require php ^7 -> your php version (8.0.2) does not satisfy that requirement.
- spomky-labs/otphp[v9.1.0, ..., v9.1.4] require php ^7.1 -> your php version (8.0.2) does not satisfy that requirement.
- scheb/2fa-google-authenticator v5.7.0 requires spomky-labs/otphp ^9.1|^10.0 -> satisfiable by spomky-labs/otphp[v9.1.0, ..., v9.1.4, v10.0.0, v10.0.1].
- spomky-labs/otphp[v10.0.0, ..., v10.0.1] require beberlei/assert ^3.0 -> satisfiable by beberlei/assert[v3.0.0, ..., v3.3.0].
- Root composer.json requires scheb/2fa-google-authenticator ^5.7 -> satisfiable by scheb/2fa-google-authenticator[v5.7.0].
Todo:
Q | A |
---|---|
Bug report? | no |
Feature request? | no |
BC Break report? | no |
RFC? / Specification | no |
Library version | 8.3) |
Hi I'm trying to create a TOTP, that is created when a user request access.
This otp is then sent in an email, to the email they have provided.
The then click on a link which includes the otp and this will then log them into the site.
I'm not quite sure what I'm doing wrong.
I would like to create window of 2min for example to allow them to recieve them email and attempt to login.
Before email is sent.
$time = now()->timestamp;
$totp = new TOTP(
null,
null, // Let the secret be defined by the class
300 // The period (5 seconds)
);
$otp = $totp->at($time);
Then when the otp is recieved on the other end.
$otp = Recieved from query string.
$totp = new TOTP(
null,
null, // Let the secret be defined by the class
300 // The period (5 seconds)
);
dd($totp->verify($otp, null,300));
It just keeps coming out as false.
What am I missing?
Thanks.
Q | A |
---|---|
Bug report? | yes |
Feature request? | no |
BC Break report? | no |
RFC? / Specification | no |
Library version | 9.0.2 |
I tried passing in the secret with mixed characters (alnum and special characters), the object was constructed, the QR code was valid, but the verify method was failing with:
Base32::doDecode() only expects characters in the correct base32 alphabet
I believe that this should fail when instantiating an instance of TOTP.
I also tried testing the library using 'SECRET' for the secret. The rendered QR code and Google Authenticator did not produce valid codes until I took one of the secrets generated by the lib (without passing in the 'secret' parameter) and then setting this manually. I don't know if it has something to do with the length of the secret or something else.
The external links to the algorithm in the description is invalid
A php library for generating one-time passwords according to [RFC 4226](http://tools.ietf.org/html/rfc4226) (HOTP Algorithm) and [RFC 6238](http://tools.ietf.org/html/rfc6238) (TOTP Algorithm)
Q | A |
---|---|
Bug report? | yes |
Feature request? | no |
BC Break report? | no |
RFC? / Specification | yes/no |
Library version | latest |
While parsing an URI without account or with service name not being an URL it breaks, example:
otpauth://totp/BestService?algorithm=SHA1&digits=6&period=30&secret=HELLOWORLD
If you run $otp = Factory::loadFromProvisioningUri($uri)
and then $this->otp->getIssuer()
the latter will return NULL
, where it should return BestService
If you want an example of an site that uses this URI format, check this site
Thanks for making this library!
I was looking over the docs and the release process seems great, but you should add an exception to break the schedule/back compatibility rules as needed for security fixes.
Q | A |
---|---|
Bug report? | no |
Feature request? | yes |
BC Break report? | no |
RFC? / Specification | no |
Library version | 10.0(.1) |
Currently, the OTP time window can only be an integer
:
public function verify(string $otp, ?int $input = null, ?int $window = null): bool;
This parameter is a multiplier for the period set while generating the OTP;
so by using an integer
it is impossible to create a time window smaller than the period itself.
Example:
Google Authenticator only accepts a period equal to 30 seconds (not less, not more).
I would like to validate the OTP in a time window which goes by timestamp - 45s
to timestamp + 45s
.
Currently this is impossible because I cannot set a time window equal to 0.5
(15 seconds).
Opened questions:
TOTP
and HOTP
classes as final
.v7
of the library.secret
).v7
and mandatory parameters required.TOTP
objects are not supposed to be modified. If a parameter is changed, then it is a new object. This classes should be immutable and setters have to be removed.HOTP
object should be immutable. The only problem is that the counter has to be updated if an OTP verification succeeded.More information about these concepts:
Q | A |
---|---|
Bug report? | no |
Feature request? | no |
BC Break report? | no |
RFC? / Specification | yes/no |
Library version | x.y(.z) |
First of all I congratulate you for this excellent library
I have implemented TOTP and it works fine on my local server, but when I test it on the production server, there is a mismatch in the OTP validation. Valid well if I enter the value given in Google Authenticator in the last 10 seconds (the period is by default) and even having changed the number in the app, it accept it as valid to the previous one until 20 seconds after having changed.
Any suggestions?
Note, the local server has php 7.2 and the production server php 7.3I
The release of the version 9.0 should be done by the end of the year.
At this date, both PHP 5.6 and 7.0 will not be supported anymore.
Same goes for HHVM.
This library should drop those versions.
Developers who use this library on those platforms should continue to use the v8.x
I'm stuck with an issue where my two factor authentication isn't able to verify codes anymore. I had successfully integrated the package with Laravel 5.3 and it was working wonderfully. I came back to my computer this morning and am unable to login without manually disabling the two factor system.
I can't think of anything I changed that would have broken the two factor system. After manually disabling the two factor for the account, I went back to set up 2fa for that account. I wasn't even able to get the initial check to go through. And I know I hadn't changed and of those files.
Here is the code to validate my 2fa input:
public function enableTwoFactor(Request $request) {
$user = Auth::user();
$label = $user->email;
$secret = Base32::encode($user->secret);
$totp = new TOTP(
$label,
$secret,
30,
'sha1',
6
);
$check = $totp->verify($request->code);
if ( $check ) {
$user->enable_2fa = 1;
$user->backup_codes = $this->generateBackupCodes();
$user->save();
}
return redirect('/account/2fa');
}
The information that is passed into the barcode is the same code shown above. I can't seem to see any reason for the code not to successfully verify anymore.
Am I trying to verify the 2fa code correctly? Or has there recently been a change to how 2fa codes are verified that would cause this to no longer verify correctly?
Let me know if you need any more information.
Thank you
Q | A |
---|---|
Bug report? | no |
Feature request? | no |
BC Break report? | no |
RFC? / Specification | no |
Library version | latest |
Hello!
I like this package for OTP and it was working just fine!
I made a new feature to show how many seconds left to expire the OTP password.
I have an app that the client can close the page and open again, and because of that my counter started again in my started period. So i decided to do a method that i can calculate de expiration and i can start my timer right in the time left.
I don't know hot to put this new thing in this project. can i do a PR ?
My code will do someting like this:
$otp = TOTP::create(null,30,'sha1',6);
echo "My token in {$otp->now()} with {$otp->getPeriod()} seconds to live and will expire in {$otp->getExpiration()} seconds!";
This library provides completely abstract classes and implementation may be difficult.
Examples could help developpers to implement missing methods.
Q | A |
---|---|
Bug report? | yes |
Feature request? | no |
BC Break report? | no |
RFC? / Specification | no |
Library version | 9.1.2 |
running this library on the latest xampp (inlcuding php 7.3.0) on windows issues the following warning:
Warning: pack() [<a href='https://php.net/manual/function.pack'>function.pack</a>]: 64-bit format codes are not available for 32-bit versions of PHP in vendor\spomky-labs\otphp\src\OTP.php on line 128
the pack()
call will return false and this violates the intToByteString
return-type which makes php hard-error.
Just a quick little suggestion. The README has a link which points to the docs, and the docs do not mention the loading of a provisioning URI or the getSecret() function. This caused me a little confusion as I was happy to use the built in secret generator but did not know how to get the secret out of the OTP object to store in the database. As referenced in #57 there is a Wiki page on what information to store in the database. Maybe this should be linked on the README or in the docs?
It looks like google doesn't like digits, digest and interval in the provisioning url.
Using version 8.3.2, the base32 class was changed. When a string encoded with the normal "Base32::encode" function is set as the secret, the TOTP->verify function fails because it tries to decode the string with "Base32::decodeUpper". If the original string is encoded with Base::encodeUpper then the verify works fine.
To avoid the regression, I think it would be most appropriate to modify the ParameterTrait->setSecret function to pass the secret through a php "strtoupper" call.
hello i have try to use but i have a lot of error :
Interface 'OTPHP\OTPInterface' not found or syntax error, unexpected 'use' (T_USE)
i don't understand how to make for use(i have find old tuto http://sebsauvage.net/wiki/doku.php?id=totp but don't work :/) don't have find or included on google search how use namespace.
can you help me please ?
sorry if not the good place but don't know how to send mp on github XD
thank you :)
Q | A |
---|---|
Bug report? | maybe |
Feature request? | no |
BC Break report? | no |
RFC? / Specification | yes |
Library version | 9.0 |
Code..
<?php
use OTPHP\TOTP;
$otp = TOTP::create();
Error..
Fatal error: Uncaught Error: Call to undefined method OTPHP\TOTP::create() in /Applications/XAMPP/xamppfiles/htdocs/1log/test.php:7 Stack trace: #0 {main} thrown in /Applications/XAMPP/xamppfiles/htdocs/1log/test.php on line 7
Am I doing something stupid? Thanks
PHP version.. 7.1.7
Composer Latest Version..
Using version ^9.0 for spomky-labs/otphp
- Installing christian-riesen/base32 (1.3.1) Loading from cache
- Installing beberlei/assert (v2.7.6) Loading from cache
- Installing spomky-labs/otphp (v9.0.0) Downloading: 100%
Google Authenticator generates codes that are always 6 digits long, so are zero padded strings, rather than integers. However, this library generates integers, so they are not always the number of digits specified. This causes problems when verifying the submitted codes.
My solution was to override the at()
method as follows:
public function at( $timestamp ) {
return sprintf( '%06d', parent::at( $timestamp ) );
}
When relying on composer require spomky-labs/otphp
as suggested in the docs, I get v8.3 by default.
I expected v9.0 as that branch is marked as default, and v8.3 is no longer maintained.
I've tested some code:
public function getOtp($number)
{
$totp = $this->getTotp();
$otp = $totp->now();
sleep(5);
return $totp->verify($otp);
}
protected function getTotp()
{
return new TOTP(null, null, 10);
}
Sometimes it returns false. However, I look at this function
private function timecode($timestamp)
{
return (int) ((((int) $timestamp * 1000) / ($this->getPeriod() * 1000)));
}
For example, I assume that period is 60 and timestamp is 118, the time code would be 1.
When timestamp is 120, time code would be changed to 2, which may makes verify function return false.
Q | A |
---|---|
Bug report? | yes |
Library version | 9.1.0 |
The rendered QR code from this call $totp->getQrCodeUri()
is not accepted by Google Authenticator.
Error message from the app: "the barcode 'optauth://totp/ ... " is not a valid authentication token barcode"
Some investigation leads me to believe the URI contained in the QR is not of correct format as per https://github.com/google/google-authenticator/wiki/Key-Uri-Format
Q | A |
---|---|
Bug report? | yes |
Feature request? | no |
BC Break report? | no |
RFC? / Specification | no |
Library version | v9.1.4 |
When trying to use the OTPHP\OTP::getQrCodeUri () method without parameters:
$now = time();
$period = self::TF_PERIOD;
$otp = TOTP::create(self::TF_TOKEN, $period, 'sha1', 6);
$otp->setLabel(self::TF_LABEL);
$url = $otp->getQrCodeUri();
I get an error:
[Tue Aug 13 08:49:35.346689 2019] [php7:notice] [pid 12] [client 172.20.71.2:44216] [ArgumentCountError]: Too few arguments to function OTPHP\OTP::getQrCodeUri(), 0 passed in ...
Version PHP 7.2
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.