Merge pull request #4331 from pixelfed/staging

Staging
This commit is contained in:
daniel 2023-04-26 03:17:32 -06:00 committed by GitHub
commit d40d48aa76
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 572 additions and 178 deletions

View file

@ -31,6 +31,11 @@
- Update RegisterController, store client ip during registration ([d4c967de](https://github.com/pixelfed/pixelfed/commit/d4c967de))
- Update ApiV1Controller, fix account blocks. Closes #4304 ([98739139](https://github.com/pixelfed/pixelfed/commit/98739139))
- Update RegisterController, improve max_users calculation and add kb page to redirect to if conditions are met ([1bbee6d0](https://github.com/pixelfed/pixelfed/commit/1bbee6d0))
- Update SecuritySettings, remove imagick depdency for 2FA qr code generation image ([506f95c6](https://github.com/pixelfed/pixelfed/commit/506f95c6))
- Update 2fa checkpoint view design ([86c472ac](https://github.com/pixelfed/pixelfed/commit/86c472ac))
- Update sudo mode checkpoint view design ([091e0b2c](https://github.com/pixelfed/pixelfed/commit/091e0b2c))
- Update ForgotPasswordController, add captcha support, improve security and a new redesigned view ([f6e7ff64](https://github.com/pixelfed/pixelfed/commit/f6e7ff64))
- Update ResetPasswordController, add captcha support, improve security and a new redesigned view ([0ab5b96a](https://github.com/pixelfed/pixelfed/commit/0ab5b96a))
- ([](https://github.com/pixelfed/pixelfed/commit/))
## [v0.11.5 (2023-03-25)](https://github.com/pixelfed/pixelfed/compare/v0.11.4...v0.11.5)

View file

@ -43,6 +43,8 @@ class ForgotPasswordController extends Controller
abort_if(BouncerService::checkIp(request()->ip()), 404);
}
usleep(random_int(100000, 300000));
return view('auth.passwords.email');
}
@ -52,12 +54,51 @@ class ForgotPasswordController extends Controller
* @param \Illuminate\Http\Request $request
* @return void
*/
protected function validateEmail(Request $request)
public function validateEmail(Request $request)
{
if(config('pixelfed.bouncer.cloud_ips.ban_logins')) {
abort_if(BouncerService::checkIp($request->ip()), 404);
}
$request->validate(['email' => 'required|email']);
usleep(random_int(100000, 3000000));
if(config('captcha.enabled')) {
$rules = [
'email' => 'required|email',
'h-captcha-response' => 'required|captcha'
];
} else {
$rules = [
'email' => 'required|email'
];
}
$request->validate($rules, [
'h-captcha-response' => 'Failed to validate the captcha.',
]);
}
/**
* Get the response for a failed password reset link.
*
* @param \Illuminate\Http\Request $request
* @param string $response
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Illuminate\Validation\ValidationException
*/
public function sendResetLinkFailedResponse(Request $request, $response)
{
if ($request->wantsJson()) {
throw ValidationException::withMessages([
'email' => [trans($response)],
]);
}
return back()
->withInput($request->only('email'))
->withErrors([
'email' => trans($response),
]);
}
}

View file

@ -7,6 +7,7 @@ use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Support\Facades\Password;
use Illuminate\Http\Request;
use App\Services\BouncerService;
use Illuminate\Validation\Rules;
class ResetPasswordController extends Controller
{
@ -40,6 +41,46 @@ class ResetPasswordController extends Controller
$this->middleware('guest');
}
/**
* Get the password reset validation rules.
*
* @return array
*/
protected function rules()
{
usleep(random_int(100000, 3000000));
if(config('captcha.enabled')) {
return [
'token' => 'required',
'email' => 'required|email',
'password' => ['required', 'confirmed', 'max:72', Rules\Password::defaults()],
'h-captcha-response' => ['required' ,'filled', 'captcha']
];
}
return [
'token' => 'required',
'email' => 'required|email',
'password' => ['required', 'confirmed', 'max:72', Rules\Password::defaults()],
];
}
/**
* Get the password reset validation error messages.
*
* @return array
*/
protected function validationErrorMessages()
{
return [
'password.max' => 'Passwords should not exceed 72 characters.',
'h-captcha-response.required' => 'Failed to validate the captcha.',
'h-captcha-response.filled' => 'Failed to validate the captcha.',
'h-captcha-response.captcha' => 'Failed to validate the captcha.',
];
}
/**
* Display the password reset view for the given token.
*
@ -54,6 +95,8 @@ class ResetPasswordController extends Controller
abort_if(BouncerService::checkIp($request->ip()), 404);
}
usleep(random_int(100000, 300000));
$token = $request->route()->parameter('token');
return view('auth.passwords.reset')->with(
@ -86,4 +129,34 @@ class ResetPasswordController extends Controller
: $this->sendResetFailedResponse($request, $response);
}
/**
* Get the password reset credentials from the request.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
protected function credentials(Request $request)
{
return $request->only(
'email', 'password', 'password_confirmation', 'token'
);
}
/**
* Get the response for a failed password reset.
*
* @param \Illuminate\Http\Request $request
* @param string $response
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
protected function sendResetFailedResponse(Request $request, $response)
{
if ($request->wantsJson()) {
throw ValidationException::withMessages(['email' => [trans($response)]]);
}
return redirect()->back()
->withInput($request->only('email'))
->withErrors(['email' => [trans($response)]]);
}
}

View file

@ -16,7 +16,7 @@ use Carbon\Carbon;
use Illuminate\Http\Request;
use PragmaRX\Google2FA\Google2FA;
use BaconQrCode\Renderer\ImageRenderer;
use BaconQrCode\Renderer\Image\ImagickImageBackEnd;
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
use BaconQrCode\Writer;
@ -56,13 +56,15 @@ trait SecuritySettings
$key,
500
);
$writer = new Writer(
new ImageRenderer(
new RendererStyle(400),
new ImagickImageBackEnd()
new SvgImageBackEnd()
)
);
$qrcode = base64_encode($writer->writeString($qrcode));
$qrcode = $writer->writeString($qrcode);
$user->{'2fa_secret'} = $key;
$user->{'2fa_backup_codes'} = json_encode($backups);
$user->save();

View file

@ -1,55 +1,105 @@
@extends('layouts.blank')
@push('styles')
<link href="{{ mix('css/landing.css') }}" rel="stylesheet">
<link rel="preload" as="image" href="{{ url('/_landing/bg.jpg')}}" />
@endpush
@section('content')
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-lg-5">
<div class="text-center">
<img src="/img/pixelfed-icon-color.svg" height="60px">
<p class="font-weight-light h3 py-4">Verify Two Factor Code</p>
</div>
<div class="alert alert-info small">
If you lose access to your 2FA device, contact the admins.
</div>
<div class="card">
<div class="card-body">
<form method="POST">
@csrf
<div class="page-wrapper">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-lg-5">
<div class="text-center">
<a href="/">
<img src="/img/pixelfed-icon-white.svg" height="60px">
</a>
<h1 class="pt-4 pb-1">2FA Checkpoint</h1>
<p class="font-weight-light lead">
Enter the 2FA code from your device.
</p>
<p class="text-muted small pb-3">
If you lose access to your 2FA device, contact the admins.
</p>
</div>
<div class="card bg-glass">
<div class="card-body">
<form method="POST" id="2faForm">
@csrf
<div class="form-group row">
<div class="form-group row">
<div class="col-md-12">
<input id="code" type="text" class="form-control{{ $errors->has('code') ? ' is-invalid' : '' }}" name="code" placeholder="{{__('Two-Factor Authentication Code')}}" required autocomplete="off" autofocus="" inputmode="numeric" minlength="6">
<div class="col-md-12">
<label class="font-weight-bold small text-muted">2FA Code</label>
<input
id="code"
type="text"
class="form-control{{ $errors->has('code') ? ' is-invalid' : '' }}"
name="code"
placeholder="{{__('Two-Factor Authentication Code')}}"
required
autocomplete="new-password"
autofocus=""
inputmode="numeric"
minlength="6">
@if ($errors->has('code'))
<span class="invalid-feedback">
<strong>{{ $errors->first('code') }}</strong>
</span>
@endif
</div>
</div>
@if ($errors->has('code'))
<span class="invalid-feedback">
<strong>{{ $errors->first('code') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-12">
<button type="submit" class="btn btn-success btn-block font-weight-bold">
{{ __('Verify') }}
</button>
<div class="form-group row mb-0">
<div class="col-md-12">
<button
type="button"
id="sbtn"
class="btn btn-success btn-block rounded-pill font-weight-bold"
onclick="event.preventDefault();handleSubmit()"
>
{{ __('Verify') }}
</button>
</div>
</div>
</form>
</div>
</div>
<div class="d-flex justify-content-between pt-4 small">
<span class="text-lighter text-decoration-none">Logged in as: <span class="font-weight-bold text-muted">{{Auth::user()->username}}</span></span>
<span>
<a class="text-decoration-none text-muted font-weight-bold" href="{{ route('logout') }}" onclick="event.preventDefault();document.getElementById('logout-form').submit();">Logout</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf
</form>
</span>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
<div class="d-flex justify-content-between my-3">
<p class="mb-0 small">
<span class="text-muted">Logged in as:</span> {{request()->user()->username}}
</p>
<form action="/logout" method="post">
@csrf
<button type="submit" class="btn btn-link p-0 btn-sm text-white font-weight-bold">Logout</button>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script type="text/javascript">
function handleSubmit() {
let warning = document.querySelector('.invalid-feedback');
if(warning) {
warning.style.display = 'none';
}
let code = document.getElementById('code');
code.setAttribute('readonly', 'readonly');
code.style.opacity = '20%';
let btn = document.getElementById('sbtn');
btn.classList.add('disabled');
btn.setAttribute('disabled', 'disabled');
btn.innerHTML = '<div class="spinner-border spinner-border-sm" role="status"><span class="sr-only">Loading...</span></div>';
document.getElementById('2faForm').submit()
}
</script>
@endpush

View file

@ -1,47 +1,123 @@
@extends('layouts.app')
@extends('layouts.blank')
@push('styles')
<link href="{{ mix('css/landing.css') }}" rel="stylesheet">
<link rel="preload" as="image" href="{{ url('/_landing/bg.jpg')}}" />
@endpush
@section('content')
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-lg-5">
<div class="card">
<div class="card-header bg-white p-3 text-center font-weight-bold">{{ __('Reset Password') }}</div>
<div class="page-wrapper">
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-lg-5">
<div class="text-center">
<a href="/">
<img src="/img/pixelfed-icon-white.svg" height="60px">
</a>
<h1 class="pt-4 pb-1">Reset Password</h1>
<p class="font-weight-light pb-2">Send a password reset mail to reset your password</p>
</div>
<div class="card-body">
@if (session('status') || $errors->has('email'))
<div class="alert alert-success">
{{ session('status') ?? $errors->first('email') }}
@if(session('status') || $errors->has('email'))
<div class="alert alert-info small">
<div class="d-flex align-items-center font-weight-bold" style="gap:0.5rem;">
<i class="far fa-exclamation-triangle fa-lg" style="opacity:20%"></i>
{{ session('status') ?? $errors->first('email') }}
</div>
@endif
</div>
@endif
<form method="POST" action="{{ route('password.email') }}">
@csrf
<div class="card bg-glass">
<div class="card-header bg-transparent p-3 text-center font-weight-bold" style="border-bottom:1px solid #ffffff20">{{ __('Reset Password') }}</div>
<div class="form-group row">
<div class="col-md-12">
<input id="email" type="email" class="form-control" name="email" placeholder="{{ __('E-Mail Address') }}" required>
</div>
</div>
<div class="card-body">
<div class="form-group row mb-0">
<div class="col-md-12">
<button type="submit" class="btn btn-primary btn-block py-0 font-weight-bold">
{{ __('Send Password Reset Link') }}
</button>
</div>
</div>
</form>
</div>
</div>
<form id="passwordReset" method="POST" action="{{ route('password.email') }}">
@csrf
<div class="card mt-3">
<div class="card-body text-center">
<a class="btn btn-link font-weight-bold" href="{{ route('login') }}">
{{ __('Back to Login') }}
<div class="form-group row">
<div class="col-md-12">
<label class="font-weight-bold small text-muted">Email</label>
<input id="email" type="email" class="form-control" name="email" placeholder="{{ __('E-Mail Address') }}" required>
@if ($errors->has('email') && $errors->first('email') === 'The email must be a valid email address.')
<span class="text-danger small mb-3">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
</div>
@if(config('captcha.enabled'))
<label class="font-weight-bold small text-muted">Captcha</label>
<div class="d-flex flex-grow-1">
{!! Captcha::display(['data-theme' => 'dark']) !!}
</div>
@if ($errors->has('h-captcha-response'))
<div class="text-danger small mb-3">
<strong>{{ $errors->first('h-captcha-response') }}</strong>
</div>
@endif
@endif
<div class="form-group row pt-4 mb-0">
<div class="col-md-12">
<button type="button" id="sbtn" class="btn btn-primary btn-block rounded-pill font-weight-bold" onclick="event.preventDefault();handleSubmit()">
{{ __('Send Password Reset Link') }}
</button>
</div>
</div>
</form>
</div>
</div>
<div class="mt-3 d-flex justify-content-between align-items-center">
<a class="btn btn-link text-white font-weight-bold text-decoration-none" href="{{ route('login') }}">
<i class="far fa-long-arrow-left fa-lg mr-1"></i> {{ __('Back to Login') }}
</a>
</div>
</div>
</div>
</div>
<a href="#" class="text-white font-weight-bold text-decoration-none" onclick="event.preventDefault();forgotUsername()">Forgot email?</a>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script type="text/javascript">
function forgotUsername() {
swal({
title: 'Forgot email?',
text: 'Contact the instance admins to assist you in recovering your account.',
icon: 'info',
buttons: {
contact: {
text: "Contact Admins",
value: "contact",
className: "bg-danger"
},
cancel: "Close",
},
})
.then((value) => {
switch(value) {
case 'contact':
window.location.href = '/site/contact';
break;
}
});
}
function handleSubmit() {
let email = document.getElementById('email');
email.classList.add('disabled');
let btn = document.getElementById('sbtn');
btn.classList.add('disabled');
btn.setAttribute('disabled', 'disabled');
btn.innerHTML = '<div class="spinner-border spinner-border-sm" role="status"><span class="sr-only">Loading...</span></div>';
document.getElementById('passwordReset').submit()
}
</script>
@endpush

View file

@ -1,64 +1,154 @@
@extends('layouts.app')
@extends('layouts.blank')
@push('styles')
<link href="{{ mix('css/landing.css') }}" rel="stylesheet">
<link rel="preload" as="image" href="{{ url('/_landing/bg.jpg')}}" />
@endpush
@section('content')
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-lg-5">
<div class="card">
<div class="card-header bg-white p-3 text-center font-weight-bold">{{ __('Reset Password') }}</div>
<div class="page-wrapper">
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-lg-5">
<div class="text-center">
<a href="/">
<img src="/img/pixelfed-icon-white.svg" height="60px">
</a>
<h1 class="pt-4 pb-1">Reset Password</h1>
<p class="font-weight-light pb-2">Use this form to reset your password.</p>
</div>
<div class="card-body">
<form method="POST" action="{{ route('password.request') }}">
@csrf
@if ($errors)
@foreach($errors as $error)
<span class="invalid-feedback">
<strong>{{ $error }}</strong>
</span>
@endforeach
@endif
<input type="hidden" name="token" value="{{ $token }}">
<div class="card bg-glass">
<div class="card-header bg-transparent p-3 text-center font-weight-bold" style="border-bottom:1px solid #ffffff20">{{ __('Reset Password') }}</div>
<div class="form-group row">
<div class="col-md-12">
<input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ $email ?? old('email') }}" placeholder="{{ __('E-Mail Address') }}" required autofocus>
@if ($errors->has('email'))
<span class="invalid-feedback">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
</div>
<hr>
<div class="form-group row">
<div class="col-md-12">
<input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" placeholder="{{ __('Password') }}" required>
<div class="card-body">
<form id="passwordReset" method="POST" action="{{ route('password.request') }}">
@csrf
@if ($errors->has('password'))
<span class="invalid-feedback">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>
</div>
<input type="hidden" name="token" value="{{ $token }}">
<input type="hidden" name="email" value="{{ $email ?? old('email') }}">
<div class="form-group row">
<div class="col-md-12">
<input id="password-confirm" type="password" class="form-control{{ $errors->has('password_confirmation') ? ' is-invalid' : '' }}" name="password_confirmation" placeholder="{{ __('Confirm Password') }}" required>
<div class="form-group row">
<div class="col-md-12">
<label class="font-weight-bold small text-muted">Email</label>
<input
id="email"
type="email"
class="form-control {{ $errors->has('email') ? ' is-invalid' : '' }}"
name="email"
value="{{ $email ?? old('email') }}"
placeholder="{{ __('E-Mail Address') }}"
required
disabled
style="opacity: 20%;">
@if ($errors->has('password_confirmation'))
<span class="invalid-feedback">
<strong>{{ $errors->first('password_confirmation') }}</strong>
</span>
@endif
</div>
</div>
@if ($errors->has('email'))
<span class="invalid-feedback">
<strong>{{ $errors->first('email') }}</strong>
</span>
@endif
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-12">
<button type="submit" class="btn btn-primary btn-block py-0 font-weight-bold">
{{ __('Reset Password') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<hr class="bg-muted">
<div class="form-group row">
<div class="col-md-12">
<label class="font-weight-bold small text-muted">New Password</label>
<input
id="password"
type="password"
class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}"
name="password"
placeholder="{{ __('Password') }}"
minlength="{{config('pixelfed.min_password_length')}}"
maxlength="72"
autocomplete="new-password"
autofocus
required>
@if ($errors->has('password'))
<span class="invalid-feedback">
<strong>{{ $errors->first('password') }}</strong>
</span>
@else
<p class="help-text small text-muted mb-0 mt-1">Enter a new password between {{config('pixelfed.min_password_length')}}-72 characters long.</p>
@endif
</div>
</div>
<div class="form-group row">
<div class="col-md-12">
<label class="font-weight-bold small text-muted">Confirm New Password</label>
<input
id="password-confirm"
type="password"
class="form-control{{ $errors->has('password_confirmation') ? ' is-invalid' : '' }}"
name="password_confirmation"
placeholder="{{ __('Confirm Password') }}"
minlength="{{config('pixelfed.min_password_length')}}"
autocomplete="new-password"
maxlength="72"
required>
@if ($errors->has('password_confirmation'))
<span class="invalid-feedback">
<strong>{{ $errors->first('password_confirmation') }}</strong>
</span>
@endif
</div>
</div>
@if(config('captcha.enabled'))
<label class="font-weight-bold small pt-3 text-muted">Captcha</label>
<div class="d-flex flex-grow-1">
{!! Captcha::display(['data-theme' => 'dark']) !!}
</div>
@if ($errors->has('h-captcha-response'))
<div class="text-danger small mb-3">
<strong>{{ $errors->first('h-captcha-response') }}</strong>
</div>
@endif
@endif
<div class="form-group row pt-4 mb-0">
<div class="col-md-12">
<button
type="button"
id="sbtn"
class="btn btn-success btn-block rounded-pill font-weight-bold"
onclick="event.preventDefault();handleSubmit()">
{{ __('Reset Password') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script type="text/javascript">
function handleSubmit() {
let btn = document.getElementById('sbtn');
btn.classList.add('disabled');
btn.setAttribute('disabled', 'disabled');
btn.innerHTML = '<div class="spinner-border spinner-border-sm" role="status"><span class="sr-only">Loading...</span></div>';
document.getElementById('passwordReset').submit()
}
</script>
@endpush

View file

@ -1,47 +1,104 @@
@extends('layouts.blank')
@push('styles')
<link href="{{ mix('css/landing.css') }}" rel="stylesheet">
<link rel="preload" as="image" href="{{ url('/_landing/bg.jpg')}}" />
@endpush
@section('content')
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-lg-5">
<div class="text-center">
<img src="/img/pixelfed-icon-color.svg" height="60px">
<p class="font-weight-light h3 py-4">Confirm password to continue</p>
</div>
<div class="card">
<div class="card-body">
<form method="POST">
@csrf
<div class="page-wrapper">
<div class="container mt-5">
<div class="row justify-content-center">
<div class="col-lg-5">
<div class="text-center">
<a href="/">
<img src="/img/pixelfed-icon-white.svg" height="60px">
</a>
<h1 class="pt-4 pb-1">Sudo Mode</h1>
<p class="font-weight-light lead pb-2">Confirm password to continue</p>
</div>
<div class="card bg-glass">
<div class="card-body">
<form method="POST" id="sudoForm">
@csrf
<div class="form-group">
<input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" placeholder="{{__('Password')}}" required>
<div class="form-group">
<label class="font-weight-bold small text-muted">Confirm Password</label>
<input
id="password"
type="password"
class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}"
name="password"
autocomplete="new-password"
placeholder="{{__('Password')}}"
required>
@if ($errors->has('password'))
<span class="invalid-feedback">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>
@if ($errors->has('password'))
<span class="invalid-feedback">
<strong>{{ $errors->first('password') }}</strong>
</span>
@endif
</div>
<div class="form-group">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="trusted-device" name="trustDevice">
<label class="custom-control-label text-muted" for="trusted-device">Trust this device and don't ask again</label>
</div>
</div>
<div class="form-group">
<div class="custom-control custom-checkbox" id="trusted-device">
<input type="checkbox" class="custom-control-input" name="trustDevice">
<label class="custom-control-label text-muted" for="trusted-device">Trust this device and don't ask again</label>
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-12">
<button type="submit" class="btn btn-success btn-block font-weight-bold">
{{ __('Confirm Password') }}
</button>
<div class="form-group row mb-0">
<div class="col-md-12">
<button
type="button"
id="sbtn"
class="btn btn-success rounded-pill btn-block font-weight-bold"
onclick="event.preventDefault();handleSubmit()">
{{ __('Confirm Password') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</div>
</div>
<div class="d-flex justify-content-between my-3">
<p class="mb-0 small">
<span class="text-muted">Logged in as:</span> {{request()->user()->username}}
</p>
<form action="/logout" method="post">
@csrf
<button type="submit" class="btn btn-link p-0 btn-sm text-white font-weight-bold">Logout</button>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script type="text/javascript">
function handleSubmit() {
let warning = document.querySelector('.invalid-feedback');
if(warning) {
warning.style.display = 'none';
}
let email = document.getElementById('password');
email.setAttribute('readonly', 'readonly');
email.style.opacity = '20%';
let trustedDevice = document.getElementById('trusted-device');
trustedDevice.style.opacity = '20%';
let btn = document.getElementById('sbtn');
btn.classList.add('disabled');
btn.setAttribute('disabled', 'disabled');
btn.innerHTML = '<div class="spinner-border spinner-border-sm" role="status"><span class="sr-only">Loading...</span></div>';
document.getElementById('sudoForm').submit()
}
</script>
@endpush

View file

@ -50,7 +50,7 @@
<div class="card-body text-center">
<div class="pb-3">
<p class="font-weight-bold">QR Code</p>
<img src="data:image/png;base64,{{$qrcode}}" class="img-fluid" width="200px">
{!!$qrcode!!}
</div>
<div>
<p class="font-weight-bold">OTP Secret</p>