Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
],
"license": "MIT",
"require": {
"php": ">=7.1.0",
"php": ">=8.0.0",
"bacon/bacon-qr-code": "^2.0",
"pragmarx/recovery": "^0.1.0",
"pragmarx/google2fa-laravel": "^0.2.0"
"pragmarx/google2fa-laravel": "^2.0.1",
"pragmarx/google2fa-qrcode": "^3.0",
"pragmarx/recovery": "^0.2.0"
},
"autoload": {
"psr-4": {
Expand Down
10 changes: 1 addition & 9 deletions config/lifeonscreen2fa.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,5 @@
* Number of characters in each block in recovery code.
*/
'chars_in_block' => 16,

/**
* The following algorithms are currently supported:
* - PASSWORD_DEFAULT
* - PASSWORD_BCRYPT
* - PASSWORD_ARGON2I // available from php 7.2
*/
'hashing_algorithm' => PASSWORD_BCRYPT,
],
];
];
1 change: 1 addition & 0 deletions dist/css/tool.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1,297 changes: 1,297 additions & 0 deletions dist/js/tool.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions dist/mix-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"/js/tool.js": "/js/tool.js",
"/css/tool.css": "/css/tool.css"
}
24 changes: 24 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production"
},
"devDependencies": {
"cross-env": "^7.0",
"laravel-mix": "^6.0.41",
"resolve-url-loader": "^5.0.0",
"sass": "^1.49.0",
"sass-loader": "^12.4.0",
"vue-loader": "^15.9.8"
},
"dependencies": {
"vue": "^2.5.0",
"vue-template-compiler": "^2.6.14"
}
}
86 changes: 86 additions & 0 deletions resources/js/components/RecoveryCodesModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<template>
<modal
role="dialog"
@modal-close="handleClose"
>
<div class="bg-white max-w-md mx-auto my-10 rounded shadow z-10">
<div class="p-8">
<h2 class="text-2xl font-semibold text-gray-900 mb-6">View Recovery Codes</h2>

<div v-if="codes">
<p class="mb-6">Here are your recovery codes, keep them safe:</p>

<textarea :disabled="true" rows="8" cols="30" v-text="codesText" ref="codes" class="mb-6 text-white bg-90 border border-gray-100 rounded p-4 text-center"></textarea>

<div class="text-center"><button type="button" @click.prevent="hideCodes" class="btn btn-default btn-primary">I'm done. Hide them.</button></div>
</div>

<div v-else>
<p class="mb-6">To view your recovery codes, we need you to confirm your password.</p>
<form @submit.prevent="handleSubmit">
<div class="mb-6">
<div>
<label class="block font-bold mb-2" for="password">Password</label>
<input ref="passwordInput" class="w-full form-control form-input form-input-bordered" :class="{'border-danger': Boolean(errors.password)}" id="password" type="password"
v-model="password" :placeholder="__('Enter your password...')" autofocus="">
<p class="mt-2 text-danger" v-if="errors.password">{{ errors.password[0] }}</p>
</div>
</div>
<div class="text-center"><button :disabled="confirming" type="submit" class="btn btn-default btn-primary">Confirm & View Codes</button></div>
</form>
</div>
</div>
</div>
</modal>
</template>

<script>
export default {
name: "RecoveryCodesModal",
data() {
return {
codes: null,
confirming: false,
password: '',
errors: {},
}
},
methods: {
handleClose() {
this.$emit('close')
},
handleConfirm() {
this.$emit('confirm')
},
handleSubmit() {
this.confirming = true
this.errors = {}

Nova.request().post('/los/2fa/unlocked-recovery-codes', {
password: this.password,
}).then(({ data }) => {
this.codes = data.recovery_codes
}).catch(({ response }) => {
this.errors = response.data.errors
}).finally(() => {
this.confirming = false
})
},
hideCodes() {
this.codes = null
this.handleClose()
},
},
computed: {
codesText() {
return this.codes.join("\n")
},
},
mounted() {
this.$refs.passwordInput.focus()
},
}
</script>

<style scoped>
</style>
68 changes: 68 additions & 0 deletions resources/js/components/Tool.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<template>
<div>
<heading class="mb-6">Nova Two Factor</heading>

<card
class="bg-white flex flex-col items-center justify-center px-2"
style="min-height: 300px"
>
<h1 class="text-gray-900 text-4xl text-90 font-light mb-6">
Recovery Codes
</h1>

<p class="text-gray-800 text-lg mb-6">
Every time you use a recovery code, that one gets removed from your list of recovery codes and a new one is generated to take its place.
</p>

<button @click.prevent="openModal" class="btn btn-default btn-primary">View Recovery Codes</button>
</card>

<portal to="modals">
<transition name="fade">
<recovery-codes-modal
v-if="showRecoveryCodesModal"
@confirm="confirmModal"
@close="closeModal"
/>
</transition>
</portal>
</div>
</template>

<script>
import RecoveryCodesModal from './RecoveryCodesModal.vue'

export default {
components: {
RecoveryCodesModal,
},
metaInfo() {
return {
title: 'Nova Two Factor',
}
},
data() {
return {
showRecoveryCodesModal: false,
}
},
mounted() {
//
},
methods: {
openModal() {
this.showRecoveryCodesModal = true;
},
closeModal() {
this.showRecoveryCodesModal = false;
},
confirmModal() {
this.closeModal();
},
},
}
</script>

<style>
/* Scoped Styles */
</style>
9 changes: 9 additions & 0 deletions resources/js/tool.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Nova.booting((Vue, router, store) => {
router.addRoutes([
{
name: '2fa-recovery-codes',
path: '/recovery-codes',
component: require('./components/Tool.vue').default,
},
])
})
1 change: 1 addition & 0 deletions resources/sass/tool.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Nova Tool CSS
77 changes: 51 additions & 26 deletions resources/views/authenticate.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,32 @@
<!-- Styles -->
<link rel="stylesheet" href="{{ mix('app.css', 'vendor/nova') }}">

<style>
body {
font-family: "Montserrat", sans-serif !important;
}

.btn,
.form-input,
.rounded-lg {
border-radius: 0 !important;
}
</style>
<script>
function checkAutoSubmit(el) {
window.checkAutoSubmit = function checkAutoSubmit(el) {
if (el.value.length === 6) {
document.getElementById('authenticate_form').submit();
}
}

window.showRecoveryInput = function showRecoveryInput(el) {
document.getElementById('secret_div').style.display = 'none';
if (document.getElementById('error_text')) {
document.getElementById('error_text').style.display = 'none';
}
document.getElementById('recover_div').style.display = null;
document.getElementById('cancel-recover-button').style.display = null;
el.style.display = 'none';
};

window.cancelRecoveryInput = function cancelRecoveryInput(el) {
document.getElementById('secret_div').style.display = null;
if (document.getElementById('error_text')) {
document.getElementById('error_text').style.display = null;
}
document.getElementById('recover_div').style.display = 'none';
document.getElementById('recover-button').style.display = null;
el.style.display = 'none';
};
</script>
</head>
<body class="bg-40 text-black h-full">
Expand All @@ -48,38 +56,55 @@ function checkAutoSubmit(el) {
Two factor authentication protects against phishing, social engineering and password brute force attacks
and secures your logins from attackers
exploiting weak or stolen credentials.</p>
<p class="p-2"><strong>Enter the pin from Google Authenticator Enable 2FA</strong></p>
<p class="p-2"><strong>Enter the pin from your Authenticator app</strong></p>

<div class="text-center pt-3">
<div class="mb-6 w-1/2" style="display:inline-block">
@if (isset($error))
<p id="error_text" class="text-center font-semibold text-danger my-3">
{{ $error }}
<button
onclick="
document.getElementById('secret_div').style.display = 'none';
document.getElementById('error_text').style.display = 'none';
document.getElementById('recover_div').style.display = 'block';
"
class="w-1/4 btn btn-default btn-primary hover:bg-primary-dark" type="button">
Recover
</button>
</p>
@endif

<div id="secret_div">
<label class="block font-bold mb-2" for="co">One Time Password</label>
<input class="form-control form-input form-input-bordered w-full" id="secret" type="number"
name="secret" value="" onkeyup="checkAutoSubmit(this)" autofocus="">
<input class="w-full form-control form-input form-input-bordered" id="secret" type="number"
name="{{ config('google2fa.otp_input') }}" value="" onkeyup="checkAutoSubmit(this)" placeholder="{{ __('Enter the code...') }}" autofocus="">
</div>

<div id="recover_div" style="display: none;">
<label class="block font-bold mb-2" for="co">Recovery code</label>
<input class="form-control form-input form-input-bordered w-full" id="recover" type="text"
<input class="w-full form-control form-input form-input-bordered" placeholder="{{ __('Enter the recovery code...') }}" id="recover" type="text"
name="recover" value="" autofocus="">
</div>
</div>
<button class="w-1/2 btn btn-default btn-primary hover:bg-primary-dark" type="submit">
<button class="mb-6 w-1/2 btn btn-default btn-primary hover:bg-primary-dark" type="submit">
Authenticate
</button>
<div>
<button
id="recover-button"
onclick="
showRecoveryInput(this);
return false;
"
class="font-bold no-underline text-primary dim"
>
Recover
</button>

<button
style="display: none;"
id="cancel-recover-button"
onclick="
cancelRecoveryInput(this);
return false;
"
class="font-bold no-underline text-primary dim"
>
Cancel Recover
</button>
</div>
</div>
</form>
</div>
Expand Down
6 changes: 6 additions & 0 deletions resources/views/navigation.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<router-link tag="h3" :to="{name: '2fa-recovery-codes'}" class="cursor-pointer flex items-center font-normal dim text-white mb-6 text-base no-underline">
<svg class="sidebar-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path></svg>
<span class="sidebar-label">
Recovery Codes
</span>
</router-link>
19 changes: 0 additions & 19 deletions resources/views/recovery.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,6 @@

<!-- Styles -->
<link rel="stylesheet" href="{{ mix('app.css', 'vendor/nova') }}">

<style>
body {
font-family: "Montserrat", sans-serif !important;
}

.btn,
.form-input,
.rounded-lg {
border-radius: 0 !important;
}
@media print
{
.no-print, .no-print *
{
display: none !important;
}
}
</style>
</head>
<body class="bg-40 text-black h-full">
<div class="h-full">
Expand Down
Loading