mirror of
https://github.com/Bubka/2FAuth.git
synced 2024-11-07 17:04:34 +01:00
Fix and complete reverse-proxy support & Adjust front-end views
This commit is contained in:
parent
911e18c9c4
commit
725c012042
@ -6,6 +6,7 @@
|
||||
use App\Api\v1\Requests\UserUpdateRequest;
|
||||
use App\Api\v1\Resources\UserResource;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
@ -14,9 +15,12 @@ class UserController extends Controller
|
||||
*
|
||||
* @return \App\Api\v1\Resources\UserResource
|
||||
*/
|
||||
public function show()
|
||||
public function show(Request $request)
|
||||
{
|
||||
$user = User::first();
|
||||
// 2 cases:
|
||||
// - The method is called from a protected route > we return the request's authenticated user
|
||||
// - The method is called from a guest route > we fetch a possible registered user
|
||||
$user = $request->user() ?: User::first();
|
||||
|
||||
return $user
|
||||
? new UserResource($user)
|
||||
|
@ -16,8 +16,9 @@ class UserResource extends JsonResource
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->when($request->user(), $this->id),
|
||||
'name' => $this->name,
|
||||
'email' => $this->when(Auth::guard()->check(), $this->email),
|
||||
'email' => $this->when($request->user(), $this->email),
|
||||
];
|
||||
}
|
||||
}
|
@ -65,5 +65,10 @@ public function register()
|
||||
return response()->json([
|
||||
'message' => $exception->getMessage()], 400);
|
||||
});
|
||||
|
||||
$this->renderable(function (UnsupportedWithReverseProxyException $exception, $request) {
|
||||
return response()->json([
|
||||
'message' => __('errors.unsupported_with_reverseproxy')], 400);
|
||||
});
|
||||
}
|
||||
}
|
14
app/Exceptions/UnsupportedWithReverseProxyException.php
Normal file
14
app/Exceptions/UnsupportedWithReverseProxyException.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class UnsupportedWithReverseProxyException.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class UnsupportedWithReverseProxyException extends Exception
|
||||
{
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
// Part of Firefly III (https://github.com/firefly-iii)
|
||||
// Largely inspired by Firefly III remote user implementation (https://github.com/firefly-iii)
|
||||
// see https://github.com/firefly-iii/firefly-iii/blob/main/app/Support/Authentication/RemoteUserProvider.php
|
||||
|
||||
namespace App\Extensions;
|
||||
@ -8,7 +8,7 @@
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Contracts\Auth\UserProvider;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Arr;
|
||||
use Exception;
|
||||
|
||||
class RemoteUserProvider implements UserProvider
|
||||
@ -16,19 +16,20 @@ class RemoteUserProvider implements UserProvider
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function retrieveById($identifier): User
|
||||
public function retrieveById($identifier)
|
||||
{
|
||||
$user = User::where('email', $identifier)->first();
|
||||
// 2FAuth is single user by design and domain data are not coupled to the user model.
|
||||
// So we provide a non-persisted user, dynamically instanciated using data
|
||||
// from the auth proxy.
|
||||
// This way no matter the user account used at proxy level, 2FAuth will always
|
||||
// authenticate a request from the proxy and will return domain data without restriction.
|
||||
//
|
||||
// The downside of this approach is that we have to be sure that no change that needs
|
||||
// to be persisted will be made to the user instance afterward (i.e through middlewares).
|
||||
|
||||
// if (null === $user) {
|
||||
// $user = User::create(
|
||||
// [
|
||||
// 'name' => $identifier,
|
||||
// 'email' => $identifier,
|
||||
// 'password' => bcrypt(Str::random(64)),
|
||||
// ]
|
||||
// );
|
||||
// }
|
||||
$user = new User;
|
||||
$user->name = $identifier['user'];
|
||||
$user->email = Arr::has($identifier, 'email') ? $identifier['email'] : $identifier['user'];
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use App\Exceptions\UnsupportedWithReverseProxyException;
|
||||
|
||||
class ForgotPasswordController extends Controller
|
||||
{
|
||||
@ -21,6 +22,20 @@ class ForgotPasswordController extends Controller
|
||||
|
||||
use SendsPasswordResetEmails;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$authGuard = config('auth.defaults.guard');
|
||||
|
||||
if ($authGuard === 'reverse-proxy-guard') {
|
||||
throw new UnsupportedWithReverseProxyException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate the email for the given request.
|
||||
*
|
||||
|
@ -11,6 +11,7 @@
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
use Carbon\Carbon;
|
||||
use App\Exceptions\UnsupportedWithReverseProxyException;
|
||||
|
||||
|
||||
class LoginController extends Controller
|
||||
@ -28,6 +29,20 @@ class LoginController extends Controller
|
||||
|
||||
use AuthenticatesUsers;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$authGuard = config('auth.defaults.guard');
|
||||
|
||||
if ($authGuard === 'reverse-proxy-guard') {
|
||||
throw new UnsupportedWithReverseProxyException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle a login request to the application.
|
||||
*
|
||||
|
@ -6,10 +6,25 @@
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Exceptions\UnsupportedWithReverseProxyException;
|
||||
|
||||
class PasswordController extends Controller
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$authGuard = config('auth.defaults.guard');
|
||||
|
||||
if ($authGuard === 'reverse-proxy-guard') {
|
||||
throw new UnsupportedWithReverseProxyException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the user's password.
|
||||
*
|
||||
|
29
app/Http/Controllers/Auth/PersonalAccessTokenController.php
Normal file
29
app/Http/Controllers/Auth/PersonalAccessTokenController.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Laravel\Passport\Http\Controllers\PersonalAccessTokenController as PassportPersonalAccessTokenController;
|
||||
|
||||
class PersonalAccessTokenController extends PassportPersonalAccessTokenController
|
||||
{
|
||||
/**
|
||||
* Get all of the personal access tokens for the authenticated user.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public function forUser(Request $request)
|
||||
{
|
||||
// WebAuthn is useless when authentication is handle by
|
||||
// a reverse proxy so we return a 202 response to tell the
|
||||
// client nothing more will happen
|
||||
if (config('auth.defaults.guard') === 'reverse-proxy-guard') {
|
||||
return response()->json([
|
||||
'message' => 'no personal access token with reverse proxy'], 202);
|
||||
}
|
||||
|
||||
parent::forUser($request);
|
||||
}
|
||||
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
// use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||
use App\Exceptions\UnsupportedWithReverseProxyException;
|
||||
|
||||
class RegisterController extends Controller
|
||||
{
|
||||
@ -26,6 +27,19 @@ class RegisterController extends Controller
|
||||
use RegistersUsers;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$authGuard = config('auth.defaults.guard');
|
||||
|
||||
if ($authGuard === 'reverse-proxy-guard') {
|
||||
throw new UnsupportedWithReverseProxyException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle a registration request for the application.
|
||||
*
|
||||
|
@ -5,6 +5,7 @@
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use App\Exceptions\UnsupportedWithReverseProxyException;
|
||||
|
||||
class ResetPasswordController extends Controller
|
||||
{
|
||||
@ -21,4 +22,17 @@ class ResetPasswordController extends Controller
|
||||
|
||||
use ResetsPasswords;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$authGuard = config('auth.defaults.guard');
|
||||
|
||||
if ($authGuard === 'reverse-proxy-guard') {
|
||||
throw new UnsupportedWithReverseProxyException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,9 +8,23 @@
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Exceptions\UnsupportedWithReverseProxyException;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$authGuard = config('auth.defaults.guard');
|
||||
|
||||
if ($authGuard === 'reverse-proxy-guard') {
|
||||
throw new UnsupportedWithReverseProxyException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update the user's profile information.
|
||||
*
|
||||
|
@ -5,6 +5,7 @@
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use DarkGhostHunter\Larapass\Http\ConfirmsWebAuthn;
|
||||
use App\Exceptions\UnsupportedWithReverseProxyException;
|
||||
|
||||
class WebAuthnConfirmController extends Controller
|
||||
{
|
||||
@ -35,7 +36,10 @@ class WebAuthnConfirmController extends Controller
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('auth');
|
||||
$this->middleware('throttle:10,1')->only('options', 'confirm');
|
||||
$authGuard = config('auth.defaults.guard');
|
||||
|
||||
if ($authGuard === 'reverse-proxy-guard') {
|
||||
throw new UnsupportedWithReverseProxyException();
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
use DarkGhostHunter\Larapass\Http\SendsWebAuthnRecoveryEmail;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use App\Exceptions\UnsupportedWithReverseProxyException;
|
||||
|
||||
class WebAuthnDeviceLostController extends Controller
|
||||
{
|
||||
@ -22,9 +23,16 @@ class WebAuthnDeviceLostController extends Controller
|
||||
|
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// $this->middleware('guest');
|
||||
$authGuard = config('auth.defaults.guard');
|
||||
|
||||
if ($authGuard === 'reverse-proxy-guard') {
|
||||
throw new UnsupportedWithReverseProxyException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use DarkGhostHunter\Larapass\Http\AuthenticatesWebAuthn;
|
||||
use App\Exceptions\UnsupportedWithReverseProxyException;
|
||||
|
||||
class WebAuthnLoginController extends Controller
|
||||
{
|
||||
@ -26,9 +27,16 @@ class WebAuthnLoginController extends Controller
|
||||
|
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// $this->middleware(['guest', 'throttle:10,1']);
|
||||
$authGuard = config('auth.defaults.guard');
|
||||
|
||||
if ($authGuard === 'reverse-proxy-guard') {
|
||||
throw new UnsupportedWithReverseProxyException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\WebauthnRenameRequest;
|
||||
use DarkGhostHunter\Larapass\Eloquent\WebAuthnCredential;
|
||||
use App\Exceptions\UnsupportedWithReverseProxyException;
|
||||
|
||||
class WebAuthnManageController extends Controller
|
||||
{
|
||||
@ -34,6 +35,14 @@ public function __construct()
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
// WebAuthn is useless when authentication is handle by
|
||||
// a reverse proxy so we return a 202 response to tell the
|
||||
// client nothing more will happen
|
||||
if (config('auth.defaults.guard') === 'reverse-proxy-guard') {
|
||||
return response()->json([
|
||||
'message' => 'no webauthn with reverse proxy'], 202);
|
||||
}
|
||||
|
||||
$user = $request->user();
|
||||
$allUserCredentials = $user->webAuthnCredentials()
|
||||
->enabled()
|
||||
|
@ -9,6 +9,7 @@
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use App\Exceptions\UnsupportedWithReverseProxyException;
|
||||
|
||||
class WebAuthnRecoveryController extends Controller
|
||||
{
|
||||
@ -32,15 +33,17 @@ class WebAuthnRecoveryController extends Controller
|
||||
*/
|
||||
protected $redirectTo = RouteServiceProvider::HOME;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// $this->middleware('guest');
|
||||
// $this->middleware('throttle:10,1')->only('options', 'recover');
|
||||
$authGuard = config('auth.defaults.guard');
|
||||
|
||||
if ($authGuard === 'reverse-proxy-guard') {
|
||||
throw new UnsupportedWithReverseProxyException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use DarkGhostHunter\Larapass\Http\RegistersWebAuthn;
|
||||
use App\Exceptions\UnsupportedWithReverseProxyException;
|
||||
|
||||
class WebAuthnRegisterController extends Controller
|
||||
{
|
||||
@ -22,11 +23,13 @@ class WebAuthnRegisterController extends Controller
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
// $this->middleware('auth');
|
||||
$authGuard = config('auth.defaults.guard');
|
||||
|
||||
if ($authGuard === 'reverse-proxy-guard') {
|
||||
throw new UnsupportedWithReverseProxyException();
|
||||
}
|
||||
}
|
||||
}
|
@ -36,10 +36,19 @@ class Kernel extends HttpKernel
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
// \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
\App\Http\Middleware\LogUserLastSeen::class,
|
||||
\App\Http\Middleware\CustomCreateFreshApiToken::class,
|
||||
],
|
||||
|
||||
'behind-auth' => [
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
\App\Http\Middleware\Authenticate::class,
|
||||
\App\Http\Middleware\KickOutInactiveUser::class,
|
||||
\App\Http\Middleware\CustomCreateFreshApiToken::class,
|
||||
],
|
||||
|
||||
|
@ -3,8 +3,43 @@
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Auth\Middleware\Authenticate as Middleware;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class Authenticate extends Middleware
|
||||
{
|
||||
/**
|
||||
* Determine if the user is logged in to any of the given guards.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param array $guards
|
||||
* @return void
|
||||
*
|
||||
* @throws \Illuminate\Auth\AuthenticationException
|
||||
*/
|
||||
protected function authenticate($request, array $guards)
|
||||
{
|
||||
if (empty($guards)) {
|
||||
// Will retreive the default guard
|
||||
$guards = [null];
|
||||
}
|
||||
else {
|
||||
// We inject the reserve-proxy guard to ensure it will be available for every routes
|
||||
// besides their declared guards. This way we ensure priority to declared guards and
|
||||
// a fallback to the reverse-proxy guard
|
||||
$proxyGuard = 'reverse-proxy-guard';
|
||||
|
||||
}
|
||||
if (config('auth.defaults.guard') === $proxyGuard && !Arr::has($guards, $proxyGuard)) {
|
||||
$guards[] = $proxyGuard;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($guards as $guard) {
|
||||
if ($this->auth->guard($guard)->check()) {
|
||||
return $this->auth->shouldUse($guard);
|
||||
}
|
||||
}
|
||||
|
||||
$this->unauthenticated($request, $guards);
|
||||
}
|
||||
|
||||
}
|
@ -18,10 +18,13 @@ class KickOutInactiveUser
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next, $guard = null)
|
||||
public function handle($request, Closure $next, ...$quards)
|
||||
{
|
||||
// We do not track activity of guest or user authenticated against a bearer token
|
||||
if (Auth::guest() || $request->bearerToken()) {
|
||||
// We do not track activity of:
|
||||
// - Guest
|
||||
// - User authenticated against a bearer token
|
||||
// - User authenticated via a reverse-proxy
|
||||
if (Auth::guest() || $request->bearerToken() || config('auth.defaults.guard') === 'reverse-proxy-guard') {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,11 @@ public function handle($request, Closure $next, ...$quards)
|
||||
$guards = empty($guards) ? [null] : $guards;
|
||||
|
||||
foreach ($guards as $guard) {
|
||||
// Activity coming from a client authenticated with a personal access token is not logged
|
||||
if( Auth::guard($guard)->check() && !$request->bearerToken()) {
|
||||
// We do not track activity of:
|
||||
// - Guest
|
||||
// - User authenticated against a bearer token
|
||||
// - User authenticated via a reverse-proxy
|
||||
if (Auth::guard($guard)->check() && !$request->bearerToken() && config('auth.defaults.guard') !== 'reverse-proxy-guard') {
|
||||
Auth::guard($guard)->user()->last_seen_at = Carbon::now()->format('Y-m-d H:i:s');
|
||||
Auth::guard($guard)->user()->save();
|
||||
break;
|
||||
|
@ -2,12 +2,11 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Providers\RouteServiceProvider;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
class RejectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
@ -18,9 +18,9 @@ class AuthServiceProvider extends ServiceProvider
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $policies = [
|
||||
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
|
||||
];
|
||||
// protected $policies = [
|
||||
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
|
||||
// ];
|
||||
|
||||
/**
|
||||
* Register any authentication / authorization services.
|
||||
@ -46,15 +46,17 @@ static function ($app, $config) {
|
||||
$app[Hasher::class],
|
||||
$config['model']
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
//
|
||||
// Register a custom provider for reverse-proxy authentication
|
||||
Auth::provider('remote-user', function ($app, array $config) {
|
||||
// Return an instance of Illuminate\Contracts\Auth\UserProvider...
|
||||
|
||||
return new RemoteUserProvider;
|
||||
});
|
||||
|
||||
// Register a custom driver for reverse-proxy authentication
|
||||
Auth::extend('reverse-proxy', function ($app, string $name, array $config) {
|
||||
// Return an instance of Illuminate\Contracts\Auth\Guard...
|
||||
|
||||
|
@ -73,7 +73,7 @@ private function getApiNamespace($version)
|
||||
protected function configureRateLimiting()
|
||||
{
|
||||
RateLimiter::for('api', function (Request $request) {
|
||||
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
|
||||
return Limit::perMinute(60)->by($request->ip());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
<?php
|
||||
|
||||
// Largely inspired by Firefly III remote user implementation (https://github.com/firefly-iii)
|
||||
// See https://github.com/firefly-iii/firefly-iii/blob/main/app/Support/Authentication/RemoteUserGuard.php
|
||||
|
||||
namespace App\Services\Auth;
|
||||
|
||||
use Exception;
|
||||
@ -66,16 +69,26 @@ public function user()
|
||||
$user = null;
|
||||
|
||||
// Get the user identifier from $_SERVER or apache filtered headers
|
||||
$header = config('auth.guard_header', 'REMOTE_USER');
|
||||
$userID = request()->server($header) ?? apache_request_headers()[$header] ?? null;
|
||||
$remoteUserHeader = config('auth.auth_proxy_headers.user', 'REMOTE_USER');
|
||||
$identifier['user'] = request()->server($remoteUserHeader) ?? apache_request_headers()[$remoteUserHeader] ?? null;
|
||||
|
||||
if (null === $userID) {
|
||||
Log::error(sprintf('No user in header "%s".', $header));
|
||||
if ($identifier['user'] === null) {
|
||||
Log::error(sprintf('No user in header "%s".', $remoteUserHeader));
|
||||
return $this->user = null;
|
||||
// throw new Exception('The guard header was unexpectedly empty. See the logs.');
|
||||
}
|
||||
|
||||
$user = $this->provider->retrieveById($userID);
|
||||
// Get the email identifier from $_SERVER
|
||||
$remoteEmailHeader = config('auth.auth_proxy_headers.email');
|
||||
|
||||
if ($remoteEmailHeader) {
|
||||
$remoteEmail = (string)(request()->server($remoteEmailHeader) ?? apache_request_headers()[$remoteEmailHeader] ?? null);
|
||||
|
||||
if ($remoteEmail) {
|
||||
$identifier['email'] = $remoteEmail;
|
||||
}
|
||||
}
|
||||
|
||||
$user = $this->provider->retrieveById($identifier);
|
||||
|
||||
return $this->user = $user;
|
||||
}
|
||||
|
@ -14,11 +14,24 @@
|
||||
*/
|
||||
|
||||
'defaults' => [
|
||||
'guard' => env('AUTHENTICATION_GUARD', 'web'),
|
||||
'guard' => env('AUTHENTICATION_GUARD', 'web-guard'),
|
||||
'passwords' => 'users',
|
||||
],
|
||||
'guard_header' => env('AUTHENTICATION_GUARD_HEADER', 'REMOTE_USER'),
|
||||
// 'guard_email' => env('AUTHENTICATION_GUARD_EMAIL_HEADER', null),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Proxy Headers
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When using a reverse proxy for authentication this option controls the
|
||||
| default name of the headers sent by the proxy.
|
||||
|
|
||||
*/
|
||||
|
||||
'auth_proxy_headers' => [
|
||||
'user' => env('AUTH_PROXY_HEADER_FOR_USER', 'REMOTE_USER'),
|
||||
'email' => env('AUTH_PROXY_HEADER_FOR_EMAIL', null),
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@ -38,18 +51,18 @@
|
||||
*/
|
||||
|
||||
'guards' => [
|
||||
'web' => [
|
||||
'web-guard' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'users',
|
||||
],
|
||||
|
||||
'api' => [
|
||||
'api-guard' => [
|
||||
'driver' => 'passport',
|
||||
'provider' => 'users',
|
||||
'hash' => false,
|
||||
],
|
||||
|
||||
'reverse-proxy' => [
|
||||
'reverse-proxy-guard' => [
|
||||
'driver' => 'reverse-proxy',
|
||||
'provider' => 'remote-user',
|
||||
],
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="field">
|
||||
<input :id="fieldName" type="checkbox" :name="fieldName" class="is-checkradio is-info" v-model="form[fieldName]" v-on:change="$emit(fieldName, form[fieldName])" >
|
||||
<input :id="fieldName" type="checkbox" :name="fieldName" class="is-checkradio is-info" v-model="form[fieldName]" v-on:change="$emit(fieldName, form[fieldName])" v-bind="$attrs">
|
||||
<label :for="fieldName" class="label" v-html="label"></label>
|
||||
<p class="help" v-html="help" v-if="help"></p>
|
||||
</div>
|
||||
@ -9,6 +9,7 @@
|
||||
<script>
|
||||
export default {
|
||||
name: 'FormCheckbox',
|
||||
inheritAttrs: false,
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
@ -129,6 +129,8 @@
|
||||
const { data } = await vm.axios.get('api/v1/user/name')
|
||||
|
||||
if( data.name ) {
|
||||
// The email property is only sent when the user is logged in.
|
||||
// In this case we push the user to the index view.
|
||||
if( data.email ) {
|
||||
return next({ name: 'accounts' });
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- webauthn registration -->
|
||||
<form-wrapper v-if="showWebauthnRegistration" :title="$t('auth.authentication')" :punchline="$t('auth.webauthn.enforce_security_using_webauthn')">
|
||||
<form-wrapper v-if="showWebauthnRegistration" :title="$t('auth.authentication')" :punchline="$t('auth.webauthn.enhance_security_using_webauthn')">
|
||||
<div v-if="deviceRegistered" class="field">
|
||||
<label class="label mb-5">{{ $t('auth.webauthn.device_successfully_registered') }} <font-awesome-icon :icon="['fas', 'check']" /></label>
|
||||
<form @submit.prevent="handleDeviceSubmit" @keydown="deviceForm.onKeydown($event)">
|
||||
|
@ -4,18 +4,23 @@
|
||||
<div class="options-tabs">
|
||||
<form-wrapper>
|
||||
<form @submit.prevent="submitProfile" @keydown="formProfile.onKeydown($event)">
|
||||
<div v-if="isRemoteUser" class="notification is-warning has-text-centered" v-html="$t('auth.user_account_controlled_by_proxy')" />
|
||||
<h4 class="title is-4 has-text-grey-light">{{ $t('settings.profile') }}</h4>
|
||||
<form-field :form="formProfile" fieldName="name" :label="$t('auth.forms.name')" autofocus />
|
||||
<form-field :form="formProfile" fieldName="email" inputType="email" :label="$t('auth.forms.email')" />
|
||||
<form-field :form="formProfile" fieldName="password" inputType="password" :label="$t('auth.forms.current_password.label')" :help="$t('auth.forms.current_password.help')" />
|
||||
<form-buttons :isBusy="formProfile.isBusy" :caption="$t('commons.update')" />
|
||||
<fieldset :disabled="isRemoteUser">
|
||||
<form-field :form="formProfile" fieldName="name" :label="$t('auth.forms.name')" autofocus />
|
||||
<form-field :form="formProfile" fieldName="email" inputType="email" :label="$t('auth.forms.email')" />
|
||||
<form-field :form="formProfile" fieldName="password" inputType="password" :label="$t('auth.forms.current_password.label')" :help="$t('auth.forms.current_password.help')" />
|
||||
<form-buttons :isBusy="formProfile.isBusy" :caption="$t('commons.update')" />
|
||||
</fieldset>
|
||||
</form>
|
||||
<form @submit.prevent="submitPassword" @keydown="formPassword.onKeydown($event)">
|
||||
<h4 class="title is-4 pt-6 has-text-grey-light">{{ $t('settings.change_password') }}</h4>
|
||||
<form-field :form="formPassword" fieldName="password" inputType="password" :label="$t('auth.forms.new_password')" />
|
||||
<form-field :form="formPassword" fieldName="password_confirmation" inputType="password" :label="$t('auth.forms.confirm_new_password')" />
|
||||
<form-field :form="formPassword" fieldName="currentPassword" inputType="password" :label="$t('auth.forms.current_password.label')" :help="$t('auth.forms.current_password.help')" />
|
||||
<form-buttons :isBusy="formPassword.isBusy" :caption="$t('auth.forms.change_password')" />
|
||||
<fieldset :disabled="isRemoteUser">
|
||||
<form-field :form="formPassword" fieldName="password" inputType="password" :label="$t('auth.forms.new_password')" />
|
||||
<form-field :form="formPassword" fieldName="password_confirmation" inputType="password" :label="$t('auth.forms.confirm_new_password')" />
|
||||
<form-field :form="formPassword" fieldName="currentPassword" inputType="password" :label="$t('auth.forms.current_password.label')" :help="$t('auth.forms.current_password.help')" />
|
||||
<form-buttons :isBusy="formPassword.isBusy" :caption="$t('auth.forms.change_password')" />
|
||||
</fieldset>
|
||||
</form>
|
||||
</form-wrapper>
|
||||
</div>
|
||||
@ -46,13 +51,16 @@
|
||||
currentPassword : '',
|
||||
password : '',
|
||||
password_confirmation : '',
|
||||
})
|
||||
}),
|
||||
isRemoteUser: false,
|
||||
}
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
const { data } = await this.formProfile.get('/api/v1/user')
|
||||
|
||||
if( data.id === null ) this.isRemoteUser = true
|
||||
|
||||
this.formProfile.fill(data)
|
||||
},
|
||||
|
||||
|
@ -2,56 +2,55 @@
|
||||
<div>
|
||||
<setting-tabs :activeTab="'settings.oauth'"></setting-tabs>
|
||||
<div class="options-tabs">
|
||||
<div class="columns is-centered">
|
||||
<div class="form-column column is-two-thirds-tablet is-half-desktop is-one-third-widescreen is-one-third-fullhd">
|
||||
<h4 class="title is-4 has-text-grey-light">{{ $t('settings.personal_access_tokens') }}</h4>
|
||||
<div class="is-size-7-mobile">
|
||||
{{ $t('settings.token_legend')}}
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<router-link class="is-link" :to="{ name: 'settings.oauth.generatePAT' }">
|
||||
<font-awesome-icon :icon="['fas', 'plus-circle']" /> {{ $t('settings.generate_new_token')}}
|
||||
</router-link>
|
||||
</div>
|
||||
<div v-if="tokens.length > 0">
|
||||
<div v-for="token in tokens" :key="token.id" class="group-item has-text-light is-size-5 is-size-6-mobile">
|
||||
<font-awesome-icon v-if="token.value" class="has-text-success" :icon="['fas', 'check']" /> {{ token.name }}
|
||||
<!-- revoke link -->
|
||||
<div class="tags is-pulled-right">
|
||||
<a v-if="token.value" class="tag" v-clipboard="() => token.value" v-clipboard:success="clipboardSuccessHandler">{{ $t('commons.copy') }}</a>
|
||||
<a class="tag is-dark " @click="revokeToken(token.id)" :title="$t('settings.revoke')">{{ $t('settings.revoke') }}</a>
|
||||
</div>
|
||||
<!-- edit link -->
|
||||
<!-- <router-link :to="{ name: 'settings.oauth.editPAT' }" class="has-text-grey pl-1" :title="$t('commons.edit')">
|
||||
<font-awesome-icon :icon="['fas', 'pen-square']" />
|
||||
</router-link> -->
|
||||
<!-- warning msg -->
|
||||
<span v-if="token.value" class="is-size-7-mobile is-size-6 my-3">
|
||||
{{ $t('settings.make_sure_copy_token') }}
|
||||
</span>
|
||||
<!-- token value -->
|
||||
<span v-if="token.value" class="pat is-family-monospace is-size-6 is-size-7-mobile has-text-success">
|
||||
{{ token.value }}
|
||||
</span>
|
||||
<form-wrapper>
|
||||
<div v-if="isRemoteUser" class="notification is-warning has-text-centered" v-html="$t('auth.auth_handled_by_proxy')" />
|
||||
<h4 class="title is-4 has-text-grey-light">{{ $t('settings.personal_access_tokens') }}</h4>
|
||||
<div class="is-size-7-mobile">
|
||||
{{ $t('settings.token_legend')}}
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<a class="is-link" @click="createToken()">
|
||||
<font-awesome-icon :icon="['fas', 'plus-circle']" /> {{ $t('settings.generate_new_token')}}
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="tokens.length > 0">
|
||||
<div v-for="token in tokens" :key="token.id" class="group-item has-text-light is-size-5 is-size-6-mobile">
|
||||
<font-awesome-icon v-if="token.value" class="has-text-success" :icon="['fas', 'check']" /> {{ token.name }}
|
||||
<!-- revoke link -->
|
||||
<div class="tags is-pulled-right">
|
||||
<a v-if="token.value" class="tag" v-clipboard="() => token.value" v-clipboard:success="clipboardSuccessHandler">{{ $t('commons.copy') }}</a>
|
||||
<a class="tag is-dark " @click="revokeToken(token.id)" :title="$t('settings.revoke')">{{ $t('settings.revoke') }}</a>
|
||||
</div>
|
||||
<div class="mt-2 is-size-7 is-pulled-right">
|
||||
{{ $t('settings.revoking_a_token_is_permanent')}}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isFetching && tokens.length === 0" class="has-text-centered mt-6">
|
||||
<span class="is-size-4">
|
||||
<font-awesome-icon :icon="['fas', 'spinner']" spin />
|
||||
<!-- edit link -->
|
||||
<!-- <router-link :to="{ name: 'settings.oauth.editPAT' }" class="has-text-grey pl-1" :title="$t('commons.edit')">
|
||||
<font-awesome-icon :icon="['fas', 'pen-square']" />
|
||||
</router-link> -->
|
||||
<!-- warning msg -->
|
||||
<span v-if="token.value" class="is-size-7-mobile is-size-6 my-3">
|
||||
{{ $t('settings.make_sure_copy_token') }}
|
||||
</span>
|
||||
<!-- token value -->
|
||||
<span v-if="token.value" class="pat is-family-monospace is-size-6 is-size-7-mobile has-text-success">
|
||||
{{ token.value }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- footer -->
|
||||
<vue-footer :showButtons="true">
|
||||
<!-- close button -->
|
||||
<p class="control">
|
||||
<router-link :to="{ name: 'accounts', params: { toRefresh: false } }" class="button is-dark is-rounded">{{ $t('commons.close') }}</router-link>
|
||||
</p>
|
||||
</vue-footer>
|
||||
<div class="mt-2 is-size-7 is-pulled-right">
|
||||
{{ $t('settings.revoking_a_token_is_permanent')}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isFetching && tokens.length === 0" class="has-text-centered mt-6">
|
||||
<span class="is-size-4">
|
||||
<font-awesome-icon :icon="['fas', 'spinner']" spin />
|
||||
</span>
|
||||
</div>
|
||||
<!-- footer -->
|
||||
<vue-footer :showButtons="true">
|
||||
<!-- close button -->
|
||||
<p class="control">
|
||||
<router-link :to="{ name: 'accounts', params: { toRefresh: false } }" class="button is-dark is-rounded">{{ $t('commons.close') }}</router-link>
|
||||
</p>
|
||||
</vue-footer>
|
||||
</form-wrapper>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -67,7 +66,8 @@
|
||||
isFetching: false,
|
||||
form: new Form({
|
||||
token : '',
|
||||
})
|
||||
}),
|
||||
isRemoteUser: false,
|
||||
}
|
||||
},
|
||||
|
||||
@ -84,7 +84,12 @@
|
||||
|
||||
this.isFetching = true
|
||||
|
||||
await this.axios.get('/api/v1/oauth/personal-access-tokens').then(response => {
|
||||
await this.axios.get('/oauth/personal-access-tokens').then(response => {
|
||||
if (response.status === 202) {
|
||||
this.isRemoteUser = true
|
||||
return
|
||||
}
|
||||
|
||||
const tokens = []
|
||||
|
||||
response.data.forEach((data) => {
|
||||
@ -124,7 +129,17 @@
|
||||
this.$notify({ type: 'is-success', text: this.$t('settings.token_revoked') })
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Open the PAT creation view
|
||||
*/
|
||||
createToken() {
|
||||
if (this.isRemoteUser) {
|
||||
this.$notify({ type: 'is-warning', text: this.$t('errors.unsupported_with_reverseproxy') })
|
||||
}
|
||||
else this.$router.push({ name: 'settings.oauth.generatePAT' })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
@ -3,6 +3,7 @@
|
||||
<setting-tabs :activeTab="'settings.webauthn'"></setting-tabs>
|
||||
<div class="options-tabs">
|
||||
<form-wrapper>
|
||||
<div v-if="isRemoteUser" class="notification is-warning has-text-centered" v-html="$t('auth.auth_handled_by_proxy')" />
|
||||
<h4 class="title is-4 has-text-grey-light">{{ $t('auth.webauthn.security_devices') }}</h4>
|
||||
<div class="is-size-7-mobile">
|
||||
{{ $t('auth.webauthn.security_devices_legend')}}
|
||||
@ -37,9 +38,9 @@
|
||||
<h4 class="title is-4 pt-6 has-text-grey-light">{{ $t('settings.options') }}</h4>
|
||||
<form>
|
||||
<!-- use webauthn only -->
|
||||
<form-checkbox v-on:useWebauthnOnly="saveSetting('useWebauthnOnly', $event)" :form="form" fieldName="useWebauthnOnly" :label="$t('auth.webauthn.use_webauthn_only.label')" :help="$t('auth.webauthn.use_webauthn_only.help')" />
|
||||
<form-checkbox v-on:useWebauthnOnly="saveSetting('useWebauthnOnly', $event)" :form="form" fieldName="useWebauthnOnly" :label="$t('auth.webauthn.use_webauthn_only.label')" :help="$t('auth.webauthn.use_webauthn_only.help')" :disabled="isRemoteUser" />
|
||||
<!-- default sign in method -->
|
||||
<form-checkbox v-on:useWebauthnAsDefault="saveSetting('useWebauthnAsDefault', $event)" :form="form" fieldName="useWebauthnAsDefault" :label="$t('auth.webauthn.use_webauthn_as_default.label')" :help="$t('auth.webauthn.use_webauthn_as_default.help')" />
|
||||
<form-checkbox v-on:useWebauthnAsDefault="saveSetting('useWebauthnAsDefault', $event)" :form="form" fieldName="useWebauthnAsDefault" :label="$t('auth.webauthn.use_webauthn_as_default.label')" :help="$t('auth.webauthn.use_webauthn_as_default.help')" :disabled="isRemoteUser" />
|
||||
</form>
|
||||
<!-- footer -->
|
||||
<vue-footer :showButtons="true">
|
||||
@ -66,6 +67,7 @@
|
||||
}),
|
||||
credentials: [],
|
||||
isFetching: false,
|
||||
isRemoteUser: false,
|
||||
}
|
||||
},
|
||||
|
||||
@ -102,7 +104,10 @@
|
||||
this.isFetching = true
|
||||
|
||||
await this.axios.get('/webauthn/credentials').then(response => {
|
||||
this.credentials = response.data
|
||||
if (response.status === 202) {
|
||||
this.isRemoteUser = true
|
||||
}
|
||||
else this.credentials = response.data
|
||||
})
|
||||
|
||||
this.isFetching = false
|
||||
@ -113,6 +118,12 @@
|
||||
* Register a new security device
|
||||
*/
|
||||
async register() {
|
||||
|
||||
if (this.isRemoteUser) {
|
||||
this.$notify({ type: 'is-warning', text: this.$t('errors.unsupported_with_reverseproxy') })
|
||||
return false
|
||||
}
|
||||
|
||||
// Check https context
|
||||
if (!window.isSecureContext) {
|
||||
this.$notify({ type: 'is-danger', text: this.$t('errors.https_required') })
|
||||
|
@ -28,6 +28,8 @@
|
||||
'already_authenticated' => 'Already authenticated',
|
||||
'authentication' => 'Authentication',
|
||||
'maybe_later' => 'Maybe later',
|
||||
'user_account_controlled_by_proxy' => 'User account made available by an authentication proxy.<br />Manage the account at proxy level.',
|
||||
'auth_handled_by_proxy' => 'Authentication handled by a reverse proxy, below settings are disabled.<br />Manage authentication at proxy level.',
|
||||
'confirm' => [
|
||||
'logout' => 'Are you sure you want to log out?',
|
||||
'revoke_device' => 'Are you sure you want to revoke this device?',
|
||||
@ -36,7 +38,7 @@
|
||||
'security_device' => 'a security device',
|
||||
'security_devices' => 'Security devices',
|
||||
'security_devices_legend' => 'Authentication devices you can use to sign in 2FAuth, like security keys (i.e Yubikey) or smartphones with biometric capabilities (i.e. Apple FaceId/TouchId)',
|
||||
'enforce_security_using_webauthn' => 'You can enforce the security of your 2FAuth account by enabling WebAuthn authentication.<br /><br />
|
||||
'enhance_security_using_webauthn' => 'You can enhance the security of your 2FAuth account by enabling WebAuthn authentication.<br /><br />
|
||||
WebAuthn allows you to use trusted devices (like Yubikeys or smartphones with biometric capabilities) to sign in quickly and more securely.',
|
||||
'use_security_device_to_sign_in' => 'Get ready to authenticate yourself using (one of) your security devices. Plug your key in, remove face mask or gloves, etc.',
|
||||
'lost_your_device' => 'Lost your device?',
|
||||
@ -57,7 +59,7 @@
|
||||
'unknown_device' => 'Unknown device',
|
||||
'use_webauthn_only' => [
|
||||
'label' => 'Use WebAuthn only (recommended)',
|
||||
'help' => 'Make WebAuthn the only available method to sign in 2FAuth. This is the recommended setup to take advantage of the WebAuthn enforced security.<br />
|
||||
'help' => 'Make WebAuthn the only available method to sign in 2FAuth. This is the recommended setup to take advantage of the WebAuthn enhanced security.<br />
|
||||
In case of device lost you will always be able to register a new security device to recover your account.'
|
||||
],
|
||||
'use_webauthn_as_default' => [
|
||||
|
@ -35,4 +35,5 @@
|
||||
'browser_does_not_support_webauthn' => 'Your device does not support webauthn. Try again later using a more modern browser',
|
||||
'aborted_by_user' => 'Aborted by user',
|
||||
'security_device_unsupported' => 'Security device unsupported',
|
||||
'unsupported_with_reverseproxy' => 'Not applicable when using an auth proxy',
|
||||
];
|
@ -1,27 +1,24 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| API Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you can register API routes for your application. These
|
||||
| routes are loaded by the RouteServiceProvider within a group which
|
||||
| is assigned the "api" middleware group. Enjoy building your API!
|
||||
|
|
||||
*/
|
||||
|
||||
/**
|
||||
* Unprotected routes
|
||||
*/
|
||||
Route::get('user/name', 'UserController@show')->name('user.show.name');
|
||||
|
||||
Route::group(['middleware' => 'auth:reverse-proxy,api'], function() {
|
||||
|
||||
Route::get('oauth/personal-access-tokens', '\Laravel\Passport\Http\Controllers\PersonalAccessTokenController@forUser')->name('passport.personal.tokens.index');
|
||||
Route::post('oauth/personal-access-tokens', '\Laravel\Passport\Http\Controllers\PersonalAccessTokenController@store')->name('passport.personal.tokens.store');
|
||||
Route::delete('oauth/personal-access-tokens/{token_id}', '\Laravel\Passport\Http\Controllers\PersonalAccessTokenController@destroy')->name('passport.personal.tokens.destroy');
|
||||
|
||||
Route::get('user', 'UserController@show')->name('user.show');
|
||||
/**
|
||||
* Routes protected by the api authentication guard
|
||||
*/
|
||||
Route::group(['middleware' => 'auth:api-guard'], function () {
|
||||
Route::get('user', 'UserController@show')->name('user.show'); // Returns email address in addition to the username
|
||||
|
||||
Route::get('settings/{settingName}', 'SettingController@show')->name('settings.show');
|
||||
Route::get('settings', 'SettingController@index')->name('settings.index');
|
||||
@ -47,5 +44,4 @@
|
||||
|
||||
Route::post('icons', 'IconController@upload')->name('icons.upload');
|
||||
Route::delete('icons/{icon}', 'IconController@delete')->name('icons.delete');
|
||||
|
||||
});
|
@ -11,24 +11,42 @@
|
||||
|--------------------------------------------------------------------------
|
||||
| Web Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here is where you can register web routes for your application. These
|
||||
| routes are loaded by the RouteServiceProvider within a group which
|
||||
| contains the "web" middleware group. Now create something great!
|
||||
|
|
||||
*/
|
||||
|
||||
Route::post('user', 'Auth\RegisterController@register')->name('user.register');
|
||||
Route::post('user/password/lost', 'Auth\ForgotPasswordController@sendResetLinkEmail')->middleware('AvoidResetPassword')->name('user.password.lost');;
|
||||
Route::post('user/password/reset', 'Auth\ResetPasswordController@reset')->name('user.password.reset');
|
||||
Route::post('webauthn/lost', [WebAuthnDeviceLostController::class, 'sendRecoveryEmail'])->name('webauthn.lost');
|
||||
Route::post('webauthn/recover/options', [WebAuthnRecoveryController::class, 'options'])->name('webauthn.recover.options');
|
||||
Route::post('webauthn/recover', [WebAuthnRecoveryController::class, 'recover'])->name('webauthn.recover');
|
||||
/**
|
||||
* Routes that only work for unauthenticated user (return an error otherwise)
|
||||
*/
|
||||
Route::group(['middleware' => 'guest'], function () {
|
||||
Route::post('user', 'Auth\RegisterController@register')->name('user.register');
|
||||
Route::post('user/password/lost', 'Auth\ForgotPasswordController@sendResetLinkEmail')->middleware('AvoidResetPassword')->name('user.password.lost');;
|
||||
Route::post('user/password/reset', 'Auth\ResetPasswordController@reset')->name('user.password.reset');
|
||||
Route::post('webauthn/login/options', [WebAuthnLoginController::class, 'options'])->name('webauthn.login.options');
|
||||
Route::post('webauthn/lost', [WebAuthnDeviceLostController::class, 'sendRecoveryEmail'])->name('webauthn.lost');
|
||||
Route::post('webauthn/recover/options', [WebAuthnRecoveryController::class, 'options'])->name('webauthn.recover.options');
|
||||
Route::post('webauthn/recover', [WebAuthnRecoveryController::class, 'recover'])->name('webauthn.recover');
|
||||
});
|
||||
|
||||
Route::group(['middleware' => 'auth:reverse-proxy,web'], function () {
|
||||
/**
|
||||
* Routes that only work for unauthenticated user (return an error otherwise)
|
||||
* that can be requested max 10 times per minute by the same IP
|
||||
*/
|
||||
Route::group(['middleware' => ['guest', 'throttle:10,1']], function () {
|
||||
Route::post('user/login', 'Auth\LoginController@login')->name('user.login');
|
||||
Route::post('webauthn/login', [WebAuthnLoginController::class, 'login'])->name('webauthn.login');
|
||||
});
|
||||
|
||||
/**
|
||||
* Routes protected by an authentication guard
|
||||
*/
|
||||
Route::group(['middleware' => 'behind-auth'], function () {
|
||||
Route::put('user', 'Auth\UserController@update')->name('user.update');
|
||||
Route::patch('user/password', 'Auth\PasswordController@update')->name('user.password.update');
|
||||
Route::get('user/logout', 'Auth\LoginController@logout')->name('user.logout');
|
||||
|
||||
Route::get('oauth/personal-access-tokens', 'Auth\PersonalAccessTokenController@forUser')->name('passport.personal.tokens.index');
|
||||
Route::post('oauth/personal-access-tokens', 'Auth\PersonalAccessTokenController@store')->name('passport.personal.tokens.store');
|
||||
Route::delete('oauth/personal-access-tokens/{token_id}', 'Auth\PersonalAccessTokenController@destroy')->name('passport.personal.tokens.destroy');
|
||||
|
||||
Route::post('webauthn/register/options', [WebAuthnRegisterController::class, 'options'])->name('webauthn.register.options');
|
||||
Route::post('webauthn/register', [WebAuthnRegisterController::class, 'register'])->name('webauthn.register');
|
||||
Route::get('webauthn/credentials', [WebAuthnManageController::class, 'index'])->name('webauthn.credentials.index');
|
||||
@ -36,10 +54,7 @@
|
||||
Route::delete('webauthn/credentials/{credential}', [WebAuthnManageController::class, 'delete'])->name('webauthn.credentials.delete');
|
||||
});
|
||||
|
||||
Route::group(['middleware' => ['guest:web', 'throttle:10,1']], function () {
|
||||
Route::post('user/login', 'Auth\LoginController@login')->name('user.login');
|
||||
Route::post('webauthn/login/options', [WebAuthnLoginController::class, 'options'])->name('webauthn.login.options');
|
||||
Route::post('webauthn/login', [WebAuthnLoginController::class, 'login'])->name('webauthn.login');
|
||||
});
|
||||
|
||||
/**
|
||||
* Route for the main landing view
|
||||
*/
|
||||
Route::get('/{any}', 'SinglePageController@index')->where('any', '.*')->name('landing');
|
Loading…
Reference in New Issue
Block a user