Add email + sms auth

This commit is contained in:
Timothée Jaussoin 2020-04-16 18:07:17 +02:00
parent d73c952c76
commit c115f4b45c
13 changed files with 414 additions and 38 deletions

View file

@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use App\Account;
use App\Rules\SIP;
@ -11,6 +12,8 @@ use App\Helpers\Utils;
class AccountController extends Controller
{
private $emailCodeSize = 14;
public function index(Request $request)
{
return view('account.index', [
@ -23,12 +26,6 @@ class AccountController extends Controller
return view('account.login');
}
public function logout(Request $request)
{
Auth::logout();
return redirect()->route('account.login');
}
public function authenticate(Request $request)
{
$request->validate([
@ -55,4 +52,94 @@ class AccountController extends Controller
return redirect()->back();
}
public function loginEmail(Request $request)
{
return view('account.login_email');
}
public function authenticateEmail(Request $request)
{
$request->validate(['email' => 'required|email|exists:external.accounts,email']);
$account = Account::where('email', $request->get('email'))->first();
$account->confirmation_key = Str::random($this->emailCodeSize);
$account->save();
// TODO send email
return view('account.authenticate_email', [
'account' => $account
]);
}
public function authenticateEmailConfirm(Request $request, string $code)
{
$request->merge(['code' => $code]);
$request->validate(['code' => 'required|size:'.$this->emailCodeSize]);
$account = Account::where('confirmation_key', $code)->firstOrFail();
$account->confirmation_key = null;
$account->save();
Auth::login($account);
return redirect()->route('account.index');
}
public function loginPhone(Request $request)
{
return view('account.login_phone');
}
public function authenticatePhone(Request $request)
{
$request->validate(['phone' => 'required|starts_with:+|phone:AUTO']);
$account = Account::where('username', $request->get('phone'))->first();
// TODO add alias
if (!$account) {
return view('account.login_phone')->withErrors([
'phone' => 'Phone number not found'
]);
}
$account->confirmation_key = mt_rand(1000, 9999);
$account->save();
// TODO send SMS
return view('account.authenticate_phone', [
'account' => $account
]);
}
public function authenticatePhoneConfirm(Request $request)
{
$request->validate([
'account_id' => 'required',
'code' => 'required|digits:4'
]);
$account = Account::where('id', $request->get('account_id'))->firstOrFail();
if ($account->confirmation_key != $request->get('code')) {
return view('account.login_phone')->withErrors([
'code' => 'Wrong code'
]);
}
$account->confirmation_key = null;
$account->save();
Auth::login($account);
return redirect()->route('account.index');
}
public function logout(Request $request)
{
Auth::logout();
return redirect()->route('account.login');
}
}

View file

@ -15,6 +15,7 @@ class AccountEmailController extends Controller
public function update(Request $request)
{
// TODO exists doesn't already exists
$request->validate([
'email' => 'required|email',
'email_confirm' => 'required|same:email'

View file

@ -12,7 +12,8 @@
"fideloper/proxy": "^4.0",
"laravel/framework": "^6.2",
"laravel/tinker": "^2.0",
"laravelcollective/html": "^6.1"
"laravelcollective/html": "^6.1",
"propaganistas/laravel-phone": "^4.2"
},
"require-dev": {
"facade/ignition": "^1.4",

234
flexiapi/composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ba567a67313e8a2e9c9bfa01903053f8",
"content-hash": "1188abce9cc741a1318f4f7db580e86f",
"packages": [
{
"name": "dnoegel/php-xdg-base-dir",
@ -334,6 +334,123 @@
],
"time": "2020-02-22T01:51:47+00:00"
},
{
"name": "giggsey/libphonenumber-for-php",
"version": "8.12.1",
"source": {
"type": "git",
"url": "https://github.com/giggsey/libphonenumber-for-php.git",
"reference": "198dffa12831e17320207ce1bd3b121402d2cc8d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/198dffa12831e17320207ce1bd3b121402d2cc8d",
"reference": "198dffa12831e17320207ce1bd3b121402d2cc8d",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"giggsey/locale": "^1.7",
"php": ">=5.3.2"
},
"require-dev": {
"pear/pear-core-minimal": "^1.9",
"pear/pear_exception": "^1.0",
"pear/versioncontrol_git": "^0.5",
"phing/phing": "^2.7",
"php-coveralls/php-coveralls": "^1.0|^2.0",
"phpunit/phpunit": "^4.8.36|^5.0",
"symfony/console": "^2.8|^3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "8.x-dev"
}
},
"autoload": {
"psr-4": {
"libphonenumber\\": "src/"
},
"exclude-from-classmap": [
"/src/data/",
"/src/carrier/data/",
"/src/geocoding/data/",
"/src/timezone/data/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"Apache-2.0"
],
"authors": [
{
"name": "Joshua Gigg",
"email": "giggsey@gmail.com",
"homepage": "https://giggsey.com/"
}
],
"description": "PHP Port of Google's libphonenumber",
"homepage": "https://github.com/giggsey/libphonenumber-for-php",
"keywords": [
"geocoding",
"geolocation",
"libphonenumber",
"mobile",
"phonenumber",
"validation"
],
"time": "2020-03-30T08:29:12+00:00"
},
{
"name": "giggsey/locale",
"version": "1.8",
"source": {
"type": "git",
"url": "https://github.com/giggsey/Locale.git",
"reference": "85a1b251bad11c986fec2a051b10d4b80a5caa1b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/giggsey/Locale/zipball/85a1b251bad11c986fec2a051b10d4b80a5caa1b",
"reference": "85a1b251bad11c986fec2a051b10d4b80a5caa1b",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"pear/pear-core-minimal": "^1.9",
"pear/pear_exception": "^1.0",
"pear/versioncontrol_git": "^0.5",
"phing/phing": "~2.7",
"php-coveralls/php-coveralls": "^1.0|^2.0",
"phpunit/phpunit": "^4.8|^5.0",
"symfony/console": "^2.8|^3.0|^4.0",
"symfony/filesystem": "^2.8|^3.0|^4.0",
"symfony/finder": "^2.8|^3.0|^4.0",
"symfony/process": "^2.8|^3.0|^4.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Giggsey\\Locale\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Joshua Gigg",
"email": "giggsey@gmail.com",
"homepage": "http://giggsey.com/"
}
],
"description": "Locale functions required by libphonenumber-for-php",
"time": "2019-10-09T18:53:14+00:00"
},
{
"name": "laravel/framework",
"version": "v6.18.5",
@ -790,6 +907,60 @@
],
"time": "2020-03-17T18:58:12+00:00"
},
{
"name": "league/iso3166",
"version": "2.1.5",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/iso3166.git",
"reference": "aed3b32fc293afdf2c6c6a322c2408eb5d20804a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/iso3166/zipball/aed3b32fc293afdf2c6c6a322c2408eb5d20804a",
"reference": "aed3b32fc293afdf2c6c6a322c2408eb5d20804a",
"shasum": ""
},
"require": {
"php": "^7.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.12",
"phpunit/phpunit": "^5.7.11 || ^6.0 || ^7.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"League\\ISO3166\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Rob Bast",
"email": "rob.bast@gmail.com"
}
],
"description": "ISO 3166-1 PHP Library",
"homepage": "https://github.com/thephpleague/iso3166",
"keywords": [
"3166",
"3166-1",
"ISO 3166",
"countries",
"iso",
"library"
],
"time": "2020-01-29T07:08:12+00:00"
},
{
"name": "monolog/monolog",
"version": "2.0.2",
@ -1165,6 +1336,67 @@
],
"time": "2020-03-21T18:07:53+00:00"
},
{
"name": "propaganistas/laravel-phone",
"version": "4.2.3",
"source": {
"type": "git",
"url": "https://github.com/Propaganistas/Laravel-Phone.git",
"reference": "a63ceba3678a666508d4bc6fbc611680b15d2f40"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Propaganistas/Laravel-Phone/zipball/a63ceba3678a666508d4bc6fbc611680b15d2f40",
"reference": "a63ceba3678a666508d4bc6fbc611680b15d2f40",
"shasum": ""
},
"require": {
"giggsey/libphonenumber-for-php": "^7.0|^8.0",
"illuminate/support": "^5.5|^6.0|^7.0",
"illuminate/validation": "^5.5|^6.0|^7.0",
"league/iso3166": "^2.0",
"php": ">=7.1"
},
"require-dev": {
"orchestra/testbench": "*",
"phpunit/phpunit": "*"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Propaganistas\\LaravelPhone\\PhoneServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Propaganistas\\LaravelPhone\\": "src/"
},
"files": [
"src/helpers.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Propaganistas",
"email": "Propaganistas@users.noreply.github.com"
}
],
"description": "Adds phone number functionality to Laravel and Lumen based on Google's libphonenumber API.",
"keywords": [
"laravel",
"libphonenumber",
"lumen",
"phone",
"validation"
],
"time": "2020-03-03T16:55:31+00:00"
},
{
"name": "psr/container",
"version": "1.0.0",

View file

@ -118,6 +118,8 @@ return [
'url' => 'The :attribute format is invalid.',
'uuid' => 'The :attribute must be a valid UUID.',
'phone' => 'The :attribute field contains an invalid number.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines

View file

@ -0,0 +1,9 @@
@extends('layouts.main')
@section('content')
@if (Auth::check())
@include('parts.already_auth')
@else
<p>A unique authentication link was sent by email to {{ $account->email }}</p>
@endif
@endsection

View file

@ -0,0 +1,20 @@
@extends('layouts.main')
@section('content')
@if (Auth::check())
@include('parts.already_auth')
@else
<div class="card mt-3">
<div class="card-body">
{!! Form::open(['route' => 'account.authenticate_phone_confirm']) !!}
<div class="form-group">
{!! Form::label('code', 'Code') !!}
{!! Form::hidden('account_id', $account->id) !!}
{!! Form::text('code', old('code'), ['class' => 'form-control', 'placeholder' => '1234', 'required']) !!}
</div>
{!! Form::submit('Authenticate', ['class' => 'btn btn-primary']) !!}
{!! Form::close() !!}
</div>
</div>
@endif
@endsection

View file

@ -2,10 +2,7 @@
@section('content')
@if (Auth::check())
<div class="alert alert-primary" role="alert">
<a class="float-right" href="{{ route('logout') }}">Logout</a>
You are already authenticated
</div>
@include('parts.already_auth')
@else
<div class="card mt-3">
<div class="card-body">
@ -20,6 +17,9 @@
</div>
{!! Form::submit('Authenticate', ['class' => 'btn btn-primary']) !!}
{!! Form::close() !!}
<br />
<p>You can also authenticate using your <a href="{{ route('account.login_email') }}">Email address</a> or your <a href="{{ route('account.login_phone') }}">Phone number</a></p>
</div>
</div>
@endif

View file

@ -0,0 +1,19 @@
@extends('layouts.main')
@section('content')
@if (Auth::check())
@include('parts.already_auth')
@else
<div class="card mt-3">
<div class="card-body">
{!! Form::open(['route' => 'account.authenticate_email']) !!}
<div class="form-group">
{!! Form::label('email', 'Email') !!}
{!! Form::email('email', old('email'), ['class' => 'form-control', 'placeholder' => 'myemail@address.org', 'required']) !!}
</div>
{!! Form::submit('Send the authentication link', ['class' => 'btn btn-primary']) !!}
{!! Form::close() !!}
</div>
</div>
@endif
@endsection

View file

@ -0,0 +1,19 @@
@extends('layouts.main')
@section('content')
@if (Auth::check())
@include('parts.already_auth')
@else
<div class="card mt-3">
<div class="card-body">
{!! Form::open(['route' => 'account.authenticate_phone']) !!}
<div class="form-group">
{!! Form::label('phone', 'Phone') !!}
{!! Form::text('phone', old('phone'), ['class' => 'form-control', 'placeholder' => '+123456789', 'required']) !!}
</div>
{!! Form::submit('Send the authentication code by SMS', ['class' => 'btn btn-primary']) !!}
{!! Form::close() !!}
</div>
</div>
@endif
@endsection

View file

@ -1,26 +0,0 @@
@extends('layouts.main')
@section('content')
@if (Auth::check())
<div class="alert alert-primary" role="alert">
<a class="float-right" href="{{ route('logout') }}">Logout</a>
You are already authenticated
</div>
@else
<div class="card mt-3">
<div class="card-body">
{!! Form::open(['route' => 'account.authenticate']) !!}
<div class="form-group">
{!! Form::label('username', 'Username') !!}
{!! Form::text('username', old('username'), ['class' => 'form-control', 'placeholder' => 'username@sip.linphone.org', 'required']) !!}
</div>
<div class="form-group">
{!! Form::label('password', 'Password') !!}
{!! Form::password('password', ['class' => 'form-control', 'placeholder' => 'myPassword', 'required']) !!}
</div>
{!! Form::submit('Authenticate', ['class' => 'btn btn-primary']) !!}
{!! Form::close() !!}
</div>
</div>
@endif
@endsection

View file

@ -0,0 +1,4 @@
<div class="alert alert-primary" role="alert">
<a class="float-right" href="{{ route('logout') }}">Logout</a>
You are already authenticated
</div>

View file

@ -22,6 +22,14 @@
Route::get('login', 'AccountController@login')->name('account.login');
Route::post('authenticate', 'AccountController@authenticate')->name('account.authenticate');
Route::get('login/email', 'AccountController@loginEmail')->name('account.login_email');
Route::post('authenticate/email', 'AccountController@authenticateEmail')->name('account.authenticate_email');
Route::get('authenticate/email/{code}', 'AccountController@authenticateEmailConfirm')->name('account.authenticate_email_confirm');
Route::get('login/phone', 'AccountController@loginPhone')->name('account.login_phone');
Route::post('authenticate/phone', 'AccountController@authenticatePhone')->name('account.authenticate_phone');
Route::post('authenticate/phone/confirm', 'AccountController@authenticatePhoneConfirm')->name('account.authenticate_phone_confirm');
Route::group(['middleware' => 'auth'], function () {
Route::get('/', 'AccountController@index')->name('account.index');
Route::get('logout', 'AccountController@logout')->name('account.logout');