fjffk简�的咖啡店科开�2方法2
环æ»�皆山也。其西å�—诸峰,林壑尤美,望之蔚然而深秀者,ç�…ç�Šä¹Ÿã€‚山行å…七里,æ¸�闻水声潺潺,而泻出于两峰之间者,酿泉也
???????????????????????????????
??????????????/?????????????????
............/...................
.........................../...
???????????????????????????????
>>>>>>>>>>>>>/>>>>>>>>>>>>>>>>>
ÿØÿà JFIF ÿÛ „ ( %!1!%)+//.383,7(-.+
-%%-////---/-.+/--+------/------/--0+--/-/-----.-----ÿÀ ¥2" ÿÄ ÿÄ J ! 1AQ"aq�2‘#BR‚¡�Ñ3br’¢±Âð$CSƒ²á4c“%DsÓñÿÄ ÿÄ * !1AQa‘"2q3�±ð#b¡ÿÚ ? ¼QxJQa�uò¸Zö Úü8,�Úú
"SSn<rçù–´âE—^ªBÖ9À\†¸Â�ÔÂ�Ã�TÂÃÛ5
ëd´³Ã�#Ã�;Þ38ϔÂ� ¶H£M:wÃŽ3…³…âpÔF&‚FK¸9„â4à GEõªfÂ�ÿ ‘ñ(ßwÂpŽF|È¥ù®häðÃ�Ѷ¹‘[Ã’inÙW¶ùñYËœQ{›K"išÒ[Ú8žë\F¹@-?v"ÔUâ€�,ìöžkÿ {I‡£šÃ�?e
rÂ�ÃV
?>
.........................................
.............................................................................
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>>>>>>>>>>>>>/>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
dsdfPK ‚$W] áL< < LICENSEnu „[µü¤ The MIT License (MIT)
Copyright (c) Antonio Carlos Ribeiro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
PK ‚$W]ˆ‚Š!# !# README.mdnu „[µü¤ # Google2FA for Laravel
### Google Two-Factor Authentication Package for Laravel
Google2FA is a PHP implementation of the Google Two-Factor Authentication Module, supporting the HMAC-Based One-time Password (HOTP) algorithm specified in [RFC 4226](https://tools.ietf.org/html/rfc4226) and the Time-based One-time Password (TOTP) algorithm specified in [RFC 6238](https://tools.ietf.org/html/rfc6238).
This package is a Laravel bridge to [Google2FA](https://github.com/antonioribeiro/google2fa)'s PHP package.
The intent of this package is to create QRCodes for Google2FA and check user typed codes. If you need to create backup/recovery codes, please check below.
### Recovery/Backup codes
if you need to create recovery or backup codes to provide a way for your users to recover a lost account, you can use the [Recovery Package](https://github.com/antonioribeiro/recovery).
## Demos, Example & Playground
Please check the [Google2FA Package Playground](https://pragmarx.com/playground/google2fa#/).

Here's an demo app showing how to use Google2FA: [google2fa-example](https://github.com/antonioribeiro/google2fa-example).
You can scan the QR code on [this (old) demo page](https://antoniocarlosribeiro.com/technology/google2fa) with a Google Authenticator app and view the code changing (almost) in real time.
## Compatibility
| Laravel | [Google2FA](https://github.com/antonioribeiro/google2fa) | Google2FA-Laravel |
|---------|-----------|-------------------|
| 4.2 | <= 1.0.1 | |
| 5.0-5.1 | <= 1.0.1 | |
| 5.2-10.x | >= 2.0.0 | >= 0.2.0 |
Before Google2FA 2.0 (Laravel 5.1) you have to install `pragmarx/google2fa:~1.0`, because this package was both a Laravel package and a PHP (agnostic).
## Demo
Click [here](https://pragmarx.com/playground/google2fa/middleware) to see the middleware demo:

## Installing
Use Composer to install it:
composer require pragmarx/google2fa-laravel
## Installing on Laravel
### Laravel 5.5 and above
You don't have to do anything else, this package autoloads the Service Provider and create the Alias, using the new Auto-Discovery feature.
### Laravel 5.4 and below
Add the Service Provider and Facade alias to your `app/config/app.php` (Laravel 4.x) or `config/app.php` (Laravel 5.x):
``` php
PragmaRX\Google2FALaravel\ServiceProvider::class,
'Google2FA' => PragmaRX\Google2FALaravel\Facade::class,
```
## Publish the config file
``` php
php artisan vendor:publish --provider="PragmaRX\Google2FALaravel\ServiceProvider"
```
## Using It
#### Use the Facade
``` php
use Google2FA;
return Google2FA::generateSecretKey();
```
#### In Laravel you can use the IoC Container
``` php
$google2fa = app('pragmarx.google2fa');
return $google2fa->generateSecretKey();
```
## Middleware
This package has a middleware which will help you code 2FA on your app. To use it, you just have to:
### Add the middleware to your Kernel.php:
``` php
protected $routeMiddleware = [
...
'2fa' => \PragmaRX\Google2FALaravel\Middleware::class,
];
```
### Using it in one or more routes:
``` php
Route::get('/admin', function () {
return view('admin.index');
})->middleware(['auth', '2fa']);
```
### QRCode
This package uses the [Google2FA-QRCode package](https://github.com/antonioribeiro/google2fa-qrcode), please check it for more info on how to configure the proper QRCode generators for your use case.
### Imagick QRCode Backend
There are three available: **imagemagick** (default), **svg** and **eps**.
You can change it via config:
``` php
/*
* Which image backend to use for generating QR codes?
*
* Supports imagemagick, svg and eps
*/
'qrcode_image_backend' => \PragmaRX\Google2FALaravel\Support\Constants::QRCODE_IMAGE_BACKEND_IMAGEMAGICK,
```
Or runtime:
``` php
Google2FA::setQRCodeBackend('svg');
```
### Configuring the view
You can set your 'ask for a one time password' view in the config file (config/google2fa.php):
``` php
/**
* One Time Password View
*/
'view' => 'google2fa.index',
```
And in the view you just have to provide a form containing the input, which is also configurable:
``` php
/**
* One Time Password request input name
*/
'otp_input' => 'one_time_password',
```
Here's a form example:
```html
```
## One Time Password Lifetime
Usually an OTP lasts forever, until the user logs off your app, but, to improve application safety, you may want to re-ask, only for the Google OTP, from time to time. So you can set a number of minutes here:
``` php
/**
* Lifetime in minutes.
* In case you need your users to be asked for a new one time passwords from time to time.
*/
'lifetime' => 0, // 0 = eternal
```
Keep in mind that this uses the Laravel sessions in the background. If this number exceeds the value set in ``config('session.lifetime')`` you will still be logged out, even if your OTP lifetime has not expired.
And you can decide whether your OTP will be kept alive while your users are browsing the site or not:
``` php
/**
* Renew lifetime at every new request.
*/
'keep_alive' => true,
```
## Manually logging out from 2Fa
This command will logout your user and redirect he/she to the 2FA form on the next request:
``` php
Google2FA::logout();
```
If you don't want to use the Facade, you may:
``` php
use PragmaRX\Google2FALaravel\Support\Authenticator;
(new Authenticator(request()))->logout();
```
## Throttling / Lockout after X attempts
Unless you need something really fancy, you can probably use Laravel's [route throttle middleware](https://laravel.com/docs/6.x/middleware) for that:
```php
Route::get('/admin', function () {
return view('admin.index');
})->middleware(['auth', '2fa', 'throttle']);
```
## Stateless usage
```php
$authenticator = app(Authenticator::class)->bootStateless($request);
if ($authenticator->isAuthenticated()) {
// otp auth success!
}
```
You can also use a stateless middleware:
``` php
protected $routeMiddleware = [
...
'2fa' => \PragmaRX\Google2FALaravel\MiddlewareStateless::class,
];
```
## 2FA and Laravel login via remember
When Laravel login via remember is activated, the session is renovated and the 2FA code is required again. To solve this, add the ``LoginViaRemember`` listener in your ``App\Providers\EventServiceProvider``:
``` php
use Illuminate\Auth\Events\Login;
use PragmaRX\Google2FALaravel\Listeners\LoginViaRemember;
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
Login::class => [
LoginViaRemember::class,
],
];
...
```
## Events
The following events are fired:
- EmptyOneTimePasswordReceived
- LoggedOut
- LoginFailed
- LoginSucceeded
- OneTimePasswordExpired
- OneTimePasswordRequested
## Documentation
Check the ReadMe file in the main [Google2FA](https://github.com/antonioribeiro/google2fa) repository.
## Tests
The package tests were written with [phpspec](http://www.phpspec.net/en/latest/).
## Author
[Antonio Carlos Ribeiro](http://twitter.com/iantonioribeiro)
## License
Google2FA is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
## Contributing
Pull requests and issues are more than welcome.
PK ‚$W]©¾YÒ tests/Google2FaLaravelTest.phpnu „[µü¤ createFromBase(
\Symfony\Component\HttpFoundation\Request::create(
'/',
'GET'
)
);
}
protected function getPackageProviders($app)
{
return [
\PragmaRX\Google2FALaravel\ServiceProvider::class,
\Illuminate\Auth\AuthServiceProvider::class,
];
}
public function setUp(): void
{
parent::setup();
$this->app->make('Illuminate\Contracts\Http\Kernel')
->pushMiddleware('Illuminate\Session\Middleware\StartSession')
->pushMiddleware('Illuminate\View\Middleware\ShareErrorsFromSession::class');
View::addLocation(__DIR__.'/views');
$this->loginUser();
}
protected function getPackageAliases($app)
{
return [
'Google2FA' => \PragmaRX\Google2FALaravel\Facade::class,
'Auth' => \Illuminate\Support\Facades\Auth::class,
];
}
protected function home()
{
return $this->call('GET', 'home')->getContent();
}
private function loginUser()
{
$user = new User();
$user->username = 'foo';
$user->google2fa_secret = Constants::SECRET;
Auth::login($user);
}
protected function assertLogin($password = null, $message = 'google2fa passed')
{
config(['google2fa.error_messages.wrong_otp' => self::WRONG_OTP_ERROR_MESSAGE]);
$renderedView = $this->call('POST', 'login', ['one_time_password' => $password])->getContent();
$this->assertStringContainsString(
$message,
$renderedView
);
if ($message !== self::WRONG_OTP_ERROR_MESSAGE) {
$this->assertStringNotContainsString(
self::WRONG_OTP_ERROR_MESSAGE,
$renderedView
);
}
}
protected function getEnvironmentSetUp($app)
{
config(['app.debug' => true]);
$app['router']->get('home', ['as' => 'home', 'uses' => function () {
return 'we are home';
}])->middleware(\PragmaRX\Google2FALaravel\Middleware::class);
$app['router']->post('login', ['as' => 'login.post', 'uses' => function () {
return 'google2fa passed';
}])->middleware(\PragmaRX\Google2FALaravel\Middleware::class);
$app['router']->post('logout', ['as' => 'logout.post', 'uses' => function () {
Google2FA::logout();
}]);
}
public function getOTP()
{
return Google2FA::getCurrentOtp(Auth::user()->google2fa_secret);
}
// --------------------------------------------- tests
public function testCanInstantiate()
{
$this->assertEquals(16, strlen(Google2FA::generateSecretKey()));
}
public function testIsActivated()
{
$this->assertTrue(Google2FA::isActivated());
}
public function testVerifyGoogle2FA()
{
$this->assertFalse(Google2FA::verifyGoogle2FA(Auth::user()->google2fa_secret, '000000'));
}
public function testRedirectToGoogle2FAView()
{
$this->assertStringContainsString(
'google2fa view',
$this->home()
);
}
public function testGoogle2FAPostPasses()
{
$this->assertLogin($this->getOTP());
$this->assertStringContainsString(
'we are home',
$this->home()
);
}
public function testWrongOTP()
{
$this->assertLogin('9999999', self::WRONG_OTP_ERROR_MESSAGE);
}
public function testLogout()
{
$this->assertStringContainsString(
'google2fa view',
$this->home()
);
$this->assertLogin($this->getOTP());
$this->assertStringContainsString(
'we are home',
$this->home()
);
$this->assertStringContainsString(
'',
$this->call('POST', 'logout')->getContent()
);
$this->assertStringContainsString(
'google2fa view',
$this->home()
);
}
public function testLogin()
{
$this->startSession();
$request = $this->createEmptyRequest();
$request->setLaravelSession($this->app['session.store']);
$authenticator = app(\PragmaRX\Google2FALaravel\Google2FA::class)->boot($request);
$authenticator->login();
$this->assertTrue($request->getSession()->get('google2fa.auth_passed'));
}
public function testOldPasswords()
{
config(['google2fa.forbid_old_passwords' => true]);
$this->assertStringContainsString(
'google2fa view',
$this->home()
);
$this->assertLogin($this->getOTP());
$this->assertStringContainsString(
'we are home',
$this->home()
);
$this->assertStringContainsString(
'',
$this->call('POST', 'logout')->getContent()
);
$this->assertStringContainsString(
'google2fa view',
$this->home()
);
}
public function testPasswordExpiration()
{
config(['google2fa.lifetime' => 1]);
$this->assertLogin($this->getOTP());
$this->assertStringContainsString(
'we are home',
$this->home()
);
Carbon::setTestNow(Carbon::now()->addMinutes(3));
$this->assertStringContainsString(
'google2fa view',
$this->home()
);
}
public function testGoogle2FAEmptyPassword()
{
$this->assertLogin('', $message = config('google2fa.error_messages.cannot_be_empty'));
$this->assertLogin(null, $message);
}
public function testQrcodeInline()
{
$qrCode = Google2FA::getQRCodeInline('company name', 'email@company.com', Constants::SECRET);
$this->assertStringStartsWith(
'assertTrue(
strlen($qrCode) > 1024
);
}
public function testStateless()
{
$authenticator = app(Authenticator::class)->bootStateless($this->createEmptyRequest());
$this->assertFalse($authenticator->isAuthenticated());
}
public function testViewError()
{
config([
'google2fa.error_messages.cannot_be_empty' => self::EMPTY_OTP_ERROR_MESSAGE,
]);
$this->assertStringContainsString(
self::EMPTY_OTP_ERROR_MESSAGE,
$this->call('POST', 'login', ['input_one_time_password_missing' => 'missing'])->getContent()
);
}
public function testQrCodeBackend()
{
$this->assertEquals(
PackageConstants::QRCODE_IMAGE_BACKEND_SVG,
Google2FA::getQRCodeBackend()
);
Google2FA::setQRCodeBackend('imagemagick');
$this->assertEquals(
PackageConstants::QRCODE_IMAGE_BACKEND_IMAGEMAGICK,
Google2FA::getQRCodeBackend()
);
}
}
PK ‚$W]WA´Ëw w tests/TestCase.phpnu „[µü¤ all() as $message)
{{ $message }}
@endforeach
PK ‚$W]çû20: : tests/Constants.phpnu „[µü¤ email;
}
/**
* Route notifications for the Slack channel.
*
* @return string
*/
public function routeNotificationForSlack()
{
return config('services.slack.webhook_url');
}
/**
* Get the connection of the entity.
*
* @return string|null
*/
public function getQueueableConnection()
{
// TODO: Implement getQueueableConnection() method.
}
/**
* Retrieve the model for a bound value.
*
* @param mixed $value
* @param string|null $field
*
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value, $field = null)
{
// TODO: Implement resolveRouteBinding() method.
}
}
PK ‚$W] tests/.gitkeepnu „[µü¤ PK ‚$W]ú¢’›U U tests/bootstrap.phpnu „[µü¤
./tests
./src
PK ‚$W]FÜ+Ãu u # src/Exceptions/InvalidSecretKey.phpnu „[µü¤ bootStateless($request);
if ($authenticator->isAuthenticated()) {
return $next($request);
}
return $authenticator->makeRequestOneTimePasswordResponse();
}
}
PK ‚$W]Í(²uQ Q " src/Listeners/LoginViaRemember.phpnu „[µü¤ registerGoogle2fa($event->user);
}
}
/**
* Force register Google2fa login.
*
* @param User $user
*/
private function registerGoogle2fa(User $user)
{
$secret = $user->{Google2FA::config('otp_secret_column')};
if (!is_null($secret) && !empty($secret)) {
Google2FA::login();
}
}
}
PK ‚$W]WØ|;X X src/Support/Authenticator.phpnu „[µü¤ boot($request);
$this->setStateless();
return $this;
}
/**
* Fire login (success or failed).
*
* @param $succeeded
*/
private function fireLoginEvent($succeeded)
{
event(
$succeeded
? new LoginSucceeded($this->getUser())
: new LoginFailed($this->getUser())
);
return $succeeded;
}
/**
* Get the OTP from user input.
*
* @throws InvalidOneTimePassword
*
* @return mixed
*/
protected function getOneTimePassword()
{
$password = $this->getInputOneTimePassword();
if (is_null($password) || empty($password)) {
event(new EmptyOneTimePasswordReceived());
if ($this->config('throw_exceptions', true)) {
throw new InvalidOneTimePassword(config('google2fa.error_messages.cannot_be_empty'));
}
}
return $password;
}
/**
* Check if the current use is authenticated via OTP.
*
* @return bool
*/
public function isAuthenticated()
{
return $this->canPassWithoutCheckingOTP() || ($this->checkOTP() === Constants::OTP_VALID);
}
/**
* Check if it is already logged in or passable without checking for an OTP.
*
* @return bool
*/
protected function canPassWithoutCheckingOTP()
{
return
!$this->isEnabled() ||
$this->noUserIsAuthenticated() ||
!$this->isActivated() ||
$this->twoFactorAuthStillValid();
}
/**
* Check if the input OTP is valid. Returns one of the possible OTP_STATUS codes:
* 'empty', 'valid' or 'invalid'.
*
* @return string
*/
protected function checkOTP()
{
if (!$this->inputHasOneTimePassword() || empty($this->getInputOneTimePassword())) {
return Constants::OTP_EMPTY;
}
$isValid = $this->verifyOneTimePassword();
if ($isValid) {
$this->login();
$this->fireLoginEvent($isValid);
return Constants::OTP_VALID;
}
$this->fireLoginEvent($isValid);
return Constants::OTP_INVALID;
}
/**
* Verify the OTP.
*
* @throws InvalidOneTimePassword
*
* @return mixed
*/
protected function verifyOneTimePassword()
{
return $this->verifyAndStoreOneTimePassword($this->getOneTimePassword());
}
}
PK ‚$W]œ‡4Ì Ì src/Support/ErrorBag.phpnu „[µü¤ $message,
]);
}
/**
* Get a message bag with a message for a particular status code.
*
* @param $statusCode
*
* @return MessageBag
*/
protected function getErrorBagForStatusCode($statusCode)
{
$errorMap = [
SymfonyResponse::HTTP_UNPROCESSABLE_ENTITY => 'google2fa.error_messages.wrong_otp',
SymfonyResponse::HTTP_BAD_REQUEST => 'google2fa.error_messages.cannot_be_empty',
];
return $this->createErrorBagForMessage(
trans(
config(
array_key_exists($statusCode, $errorMap) ? $errorMap[$statusCode] : 'google2fa.error_messages.unknown',
'google2fa.error_messages.unknown'
)
)
);
}
}
PK ‚$W]ÊÖq� src/Support/Input.phpnu „[µü¤ getRequest()->has($this->config('otp_input'));
}
protected function getInputOneTimePassword()
{
return $this->getRequest()->input($this->config('otp_input'));
}
abstract public function getRequest();
abstract protected function config($string, $children = []);
}
PK ‚$W];E–|2 2 src/Support/Response.phpnu „[µü¤ getErrorBagForStatusCode($statusCode),
$statusCode
);
}
/**
* Make the status code, to respond accordingly.
*
* @return int
*/
protected function makeStatusCode()
{
if ($this->getRequest()->isMethod('get') || ($this->checkOTP() === Constants::OTP_VALID)) {
return SymfonyResponse::HTTP_OK;
}
if ($this->checkOTP() === Constants::OTP_EMPTY) {
return SymfonyResponse::HTTP_BAD_REQUEST;
}
return SymfonyResponse::HTTP_UNPROCESSABLE_ENTITY;
}
/**
* Make a web response.
*
* @param $statusCode
*
* @return \Illuminate\Http\Response
*/
protected function makeHtmlResponse($statusCode)
{
$view = $this->getView();
if ($statusCode !== SymfonyResponse::HTTP_OK) {
$view->withErrors($this->getErrorBagForStatusCode($statusCode));
}
return new IlluminateHtmlResponse($view, $statusCode);
}
/**
* Create a response to request the OTP.
*
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public function makeRequestOneTimePasswordResponse()
{
event(
app()->version() < '5.4'
? new OneTimePasswordRequested53($this->getUser())
: new OneTimePasswordRequested($this->getUser())
);
$expectJson = app()->version() < '5.4'
? $this->getRequest()->wantsJson()
: $this->getRequest()->expectsJson();
return $expectJson
? $this->makeJsonResponse($this->makeStatusCode())
: $this->makeHtmlResponse($this->makeStatusCode());
}
/**
* Get the OTP view.
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
private function getView()
{
return view($this->config('view'));
}
abstract protected function getErrorBagForStatusCode($statusCode);
abstract protected function inputHasOneTimePassword();
abstract public function checkOTP();
abstract protected function getUser();
abstract public function getRequest();
abstract protected function config($string, $children = []);
}
PK ‚$W]kÜÐ7 src/Support/Request.phpnu „[µü¤ request;
}
/**
* Set the request property.
*
* @param mixed $request
*
* @return $this
*/
public function setRequest($request)
{
$this->request = $request;
return $this;
}
}
PK ‚$W]}'=: : src/Support/Config.phpnu „[µü¤ auth)) {
$this->auth = app($this->config('auth'));
if (!empty($this->config('guard'))) {
$this->auth = app($this->config('auth'))->guard($this->config('guard'));
}
}
return $this->auth;
}
/**
* Get the current user.
*
* @return mixed
*/
protected function getUser()
{
return $this->getAuth()->user();
}
abstract protected function config($string, $children = []);
}
PK ‚$W]‚ˆ€õc c src/Support/Session.phpnu „[µü¤ config('session_var').(is_null($name) || empty($name) ? '' : '.'.$name);
}
/**
* Get a session var value.
*
* @param null $var
*
* @return mixed
*/
public function sessionGet($var = null, $default = null)
{
if ($this->stateless) {
return $default;
}
return $this->getRequest()->session()->get(
$this->makeSessionVarName($var),
$default
);
}
/**
* Put a var value to the current session.
*
* @param $var
* @param $value
*
* @return mixed
*/
protected function sessionPut($var, $value)
{
if ($this->stateless) {
return $value;
}
$this->getRequest()->session()->put(
$this->makeSessionVarName($var),
$value
);
return $value;
}
/**
* Forget a session var.
*
* @param null $var
*/
protected function sessionForget($var = null)
{
if ($this->stateless) {
return;
}
$this->getRequest()->session()->forget(
$this->makeSessionVarName($var)
);
}
/**
* @param mixed $stateless
*
* @return Authenticator
*/
public function setStateless($stateless = true)
{
$this->stateless = $stateless;
return $this;
}
abstract protected function config($string, $children = []);
abstract public function getRequest();
}
PK ‚$W]¦ïyñÖ Ö src/Events/LoginSucceeded.phpnu „[µü¤ user = $user;
}
}
PK ‚$W]1uÞ’2 2 + src/Events/EmptyOneTimePasswordReceived.phpnu „[µü¤ user = $user;
}
}
PK ‚$W]iYâ â ) src/Events/OneTimePasswordRequested53.phpnu „[µü¤ user = $user;
}
}
PK ‚$W]iÂÓ]à à ' src/Events/OneTimePasswordRequested.phpnu „[µü¤ user = $user;
}
}
PK ‚$W]ú¢bÑ Ñ src/Events/LoggedOut.phpnu „[µü¤ user = $user;
}
}
PK ‚$W]H bóÞ Þ % src/Events/OneTimePasswordExpired.phpnu „[µü¤ user = $user;
}
}
PK ‚$W]›Ï¹Ñ Ñ src/config/config.phpnu „[µü¤ env('OTP_ENABLED', true),
/*
* Lifetime in minutes.
*
* In case you need your users to be asked for a new one time passwords from time to time.
*/
'lifetime' => env('OTP_LIFETIME', 0), // 0 = eternal
/*
* Renew lifetime at every new request.
*/
'keep_alive' => env('OTP_KEEP_ALIVE', true),
/*
* Auth container binding.
*/
'auth' => 'auth',
/*
* Guard.
*/
'guard' => '',
/*
* 2FA verified session var.
*/
'session_var' => 'google2fa',
/*
* One Time Password request input name.
*/
'otp_input' => 'one_time_password',
/*
* One Time Password Window.
*/
'window' => 1,
/*
* Forbid user to reuse One Time Passwords.
*/
'forbid_old_passwords' => false,
/*
* User's table column for google2fa secret.
*/
'otp_secret_column' => 'google2fa_secret',
/*
* One Time Password View.
*/
'view' => 'google2fa.index',
/*
* One Time Password error message.
*/
'error_messages' => [
'wrong_otp' => "The 'One Time Password' typed was wrong.",
'cannot_be_empty' => 'One Time Password cannot be empty.',
'unknown' => 'An unknown error has occurred. Please try again.',
],
/*
* Throw exceptions or just fire events?
*/
'throw_exceptions' => env('OTP_THROW_EXCEPTION', true),
/*
* Which image backend to use for generating QR codes?
*
* Supports imagemagick, svg and eps
*/
'qrcode_image_backend' => \PragmaRX\Google2FALaravel\Support\Constants::QRCODE_IMAGE_BACKEND_SVG,
];
PK ‚$W] src/config/.gitkeepnu „[µü¤ PK ‚$W]ŽQ ¸¯ ¯ src/Middleware.phpnu „[µü¤ boot($request);
if ($authenticator->isAuthenticated()) {
return $next($request);
}
return $authenticator->makeRequestOneTimePasswordResponse();
}
}
PK ‚$W]
‰ƒ�³ ³ src/ServiceProvider.phpnu „[µü¤ publishes([
__DIR__.'/config/config.php' => config_path('google2fa.php'),
]);
}
/**
* Merge configuration.
*/
private function mergeConfig()
{
$this->mergeConfigFrom(
__DIR__.'/config/config.php',
'google2fa'
);
}
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('pragmarx.google2fa', function ($app) {
return $app->make(Google2FA::class);
});
}
public function boot()
{
$this->configurePaths();
$this->mergeConfig();
}
}
PK ‚$W]fŽ—˜W W src/Google2FA.phpnu „[µü¤ getQRCodeBackend()) {
case Constants::QRCODE_IMAGE_BACKEND_SVG:
return new \BaconQrCode\Renderer\Image\SvgImageBackEnd();
case Constants::QRCODE_IMAGE_BACKEND_EPS:
return new \BaconQrCode\Renderer\Image\EpsImageBackEnd();
case Constants::QRCODE_IMAGE_BACKEND_IMAGEMAGICK:
default:
return null;
}
}
/**
* Set the QRCode Backend.
*
* @param string $qrCodeBackend
*
* @return self
*/
public function setQrCodeBackend(string $qrCodeBackend)
{
$this->qrCodeBackend = $qrCodeBackend;
return $this;
}
/**
* Authenticator constructor.
*
* @param IlluminateRequest $request
*/
public function __construct(IlluminateRequest $request)
{
$this->boot($request);
parent::__construct(null, $this->getImageBackend());
}
/**
* Authenticator boot.
*
* @param $request
*
* @return Google2FA
*/
public function boot($request)
{
$this->setRequest($request);
$this->setWindow($this->config('window'));
return $this;
}
/**
* The QRCode Backend.
*
* @return mixed
*/
public function getQRCodeBackend()
{
return $this->qrCodeBackend
?: $this->config('qrcode_image_backend', Constants::QRCODE_IMAGE_BACKEND_IMAGEMAGICK);
}
/**
* Get the user Google2FA secret.
*
* @throws InvalidSecretKey
*
* @return mixed
*/
protected function getGoogle2FASecretKey()
{
return $this->getUser()->{$this->config('otp_secret_column')};
}
/**
* Check if the 2FA is activated for the user.
*
* @return bool
*/
public function isActivated()
{
$secret = $this->getGoogle2FASecretKey();
return !is_null($secret) && !empty($secret);
}
/**
* Store the old OTP timestamp.
*
* @param $key
*
* @return mixed
*/
protected function storeOldTimestamp($key)
{
return $this->config('forbid_old_passwords') === true
? $this->sessionPut(Constants::SESSION_OTP_TIMESTAMP, $key)
: $key;
}
/**
* Get the previous OTP timestamp.
*
* @return null|mixed
*/
protected function getOldTimestamp()
{
return $this->config('forbid_old_passwords') === true
? $this->sessionGet(Constants::SESSION_OTP_TIMESTAMP)
: null;
}
/**
* Keep this OTP session alive.
*/
protected function keepAlive()
{
if ($this->config('keep_alive')) {
$this->updateCurrentAuthTime();
}
}
/**
* Get minutes since last activity.
*
* @return int
*/
protected function minutesSinceLastActivity()
{
return Carbon::now()->diffInMinutes(
$this->sessionGet(Constants::SESSION_AUTH_TIME),
true
);
}
/**
* Check if no user is authenticated using OTP.
*
* @return bool
*/
protected function noUserIsAuthenticated()
{
return is_null($this->getUser());
}
/**
* Check if OTP has expired.
*
* @return bool
*/
protected function passwordExpired()
{
if (($minutes = $this->config('lifetime')) !== 0 && $this->minutesSinceLastActivity() > $minutes) {
event(new OneTimePasswordExpired($this->getUser()));
$this->logout();
return true;
}
$this->keepAlive();
return false;
}
/**
* Verifies, in the current session, if a 2fa check has already passed.
*
* @return bool
*/
protected function twoFactorAuthStillValid()
{
return
(bool) $this->sessionGet(Constants::SESSION_AUTH_PASSED, false) &&
!$this->passwordExpired();
}
/**
* Check if the module is enabled.
*
* @return mixed
*/
protected function isEnabled()
{
return $this->config('enabled');
}
/**
* Set current auth as valid.
*/
public function login()
{
$this->sessionPut(Constants::SESSION_AUTH_PASSED, true);
$this->updateCurrentAuthTime();
}
/**
* OTP logout.
*/
public function logout()
{
$user = $this->getUser();
$this->sessionForget();
event(new LoggedOut($user));
}
/**
* Update the current auth time.
*/
protected function updateCurrentAuthTime()
{
$this->sessionPut(Constants::SESSION_AUTH_TIME, Carbon::now()->toIso8601String());
}
/**
* Verify the OTP.
*
* @param $secret
* @param $one_time_password
*
* @return mixed
*/
public function verifyGoogle2FA($secret, $one_time_password)
{
return $this->verifyKey(
$secret,
$one_time_password,
$this->getWindow(),
null, // $timestamp
$this->getOldTimestamp() ?: null
);
}
/**
* Verify the OTP and store the timestamp.
*
* @param $one_time_password
*
* @return mixed
*/
protected function verifyAndStoreOneTimePassword($one_time_password)
{
return $this->storeOldTimestamp(
$this->verifyGoogle2FA(
$this->getGoogle2FASecretKey(),
$one_time_password
)
);
}
}
PK ‚$W]„µ¼ùJ J src/Facade.phpnu „[µü¤ logout();
}
}
PK ‚$W]+Ù¼b� � CHANGELOG.mdnu „[µü¤ ## Change Log
## [2.0.1] - 2021-08-15
### Added
- Make image backend optional if bacon is not installed
## [2.0.0] - 2021-08-15
### Added
- Allow Google2FA QRCode 3.0
## [1.4.1] - 2020-09-20
### Added
- Improve PHP 8 compatibility by not defaulting to imagemagick
- Make the code PHP 7.0 compatible
- Supports using a different guard
- Ability to setWindow dynamically
## [1.4.0] - 2020-09-20
### Added
- Laravel 8 support
## [1.1.2] - 2019-03-13
### Added
- New config item for empty OTP error messages: 'cannot_be_empty' => 'One Time Password cannot be empty.',
- Tests to check for HTML error messages
## [1.1.1] - 2019-03-12
### Removed
- PHP 7.0 support
## [1.1.0] - 2019-09-10
### Added
- Laravel 6 support
## [1.0.0] - 2019-03-21
- Start using Google2FA QRCode as base class
- Support QRCode generation again
- Support API / Stateless requests
## [0.3.0] - 2019-03-20
- Upgrade to GoogleFA 5.0
## [0.2.1] - 2019-03-20
- Removed QRCode generation via Google2FA 4.0
## [0.2.0] - 2018-03-07
### Add
- Firing events:
- EmptyOneTimePasswordReceived
- LoggedOut
- LoginFailed
- LoginSucceeded
- OneTimePasswordExpired
- OneTimePasswordRequested
### Changed
- Google2FA is not dependent from Middleware anymore
- Small refactor
- Change license to MIT
- Move from phpspec tests to PHPUnit and Orchestra
### Removed
- Support for PHP 5.4-5.6 (should still work, but would have to be tested by users)
## [0.1.4] - 2017-12-05
### Add
- Support Laravel 5.2+
## [0.1.2] - 2016-06-22
### Fixed
- Fix middleware returning nulls
## [0.1.1] - 2016-06-22
### Fixed
- Keepalive time not being updated correctly
## [0.1.0] - 2016-06-21
### Added
- First version
PK ‚$W]-¾kC C
composer.jsonnu „[µü¤ {
"name": "pragmarx/google2fa-laravel",
"description": "A One Time Password Authentication package, compatible with Google Authenticator.",
"keywords": ["authentication", "two factor authentication", "google2fa", "laravel"],
"license": "MIT",
"authors": [
{
"name": "Antonio Carlos Ribeiro",
"email": "acr@antoniocarlosribeiro.com",
"role": "Creator & Designer"
}
],
"require": {
"php": ">=7.0",
"laravel/framework": "^5.4.36|^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
"pragmarx/google2fa-qrcode": "^1.0|^2.0|^3.0"
},
"require-dev": {
"phpunit/phpunit": "~5|~6|~7|~8|~9|~10|~11",
"orchestra/testbench": "3.4.*|3.5.*|3.6.*|3.7.*|4.*|5.*|6.*|7.*|8.*|9.*|10.*",
"bacon/bacon-qr-code": "^2.0"
},
"autoload": {
"psr-4": {
"PragmaRX\\Google2FALaravel\\": "src/",
"PragmaRX\\Google2FALaravel\\Tests\\": "tests/"
}
},
"extra": {
"component": "package",
"frameworks": ["Laravel"],
"branch-alias": {
"dev-master": "0.2-dev"
},
"laravel": {
"providers": [
"PragmaRX\\Google2FALaravel\\ServiceProvider"
],
"aliases": {
"Google2FA": "PragmaRX\\Google2FALaravel\\Facade"
}
}
},
"suggest": {
"bacon/bacon-qr-code": "Required to generate inline QR Codes.",
"pragmarx/recovery": "Generate recovery codes."
},
"minimum-stability": "dev",
"prefer-stable": true
}
PK ‚$W]R·G˜N N
.gitignorenu „[µü¤ .DS_Store
composer.phar
composer.lock
vendor
coverage/*
.phpunit.result.cache
PK ‚$W]Ý•A�Ç Ç docs/middleware.jpgnu „[µü¤ ÿØÿà JFIF ÿÛ C !"$"$ÿÛ CÿÂ
�" ÿÄ ÿÄ ÿÚ öP ¼“ÕçØ[Vd=^WRGáõß²úŒÓ=Ó©qÉ×ÁÏuŠŸ«ó·ÓVÃåôꔜzj”ioæeÕ9:ùï’£’‡èxµ—Ø MÚ†/£JøÌgZ¥_Ô¶L¥æ³Þâã͹CÉ�X¨~:ãBç©Æ¯•›Åµ§”z¿L“Ò •úÇ�}OŸëù¾¼G.½5zúo†¾Lt®áµúõyý¯›§óŸj�;¢Îý¿¶ÄËA¦§×{<´´»L²MÙ[{7—õÏ9ôo'|–+k~Ymf¾³ÍßQ'ßÕù÷TšH|ݾ(¹5Ýy×ÞT}ðíÍ•³¹öyúX¯RóvÊOûÐ�ÔÙYY™Ûd÷{y—²øç±æ‡Éú@ >Řk�ÓÂ*«§µ]]c#ѦväoG/»xŠªègì{ڜ×'¶jUÚ·_ódÔ|ý9ï.¹êóÑSíRçæºcX�gZÊ+>¦7›Ò •øÖºóã‚Íǯ&On霶¤Å} qvùünx²XmOmíÄq˶êòγkkâ››6l