Skip to content

Commit

Permalink
cleanup mfa logic
Browse files Browse the repository at this point in the history
  • Loading branch information
tinohager committed Jun 17, 2024
1 parent 3b40c73 commit fffec90
Show file tree
Hide file tree
Showing 11 changed files with 241 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
<PackageReference Include="Nager.Authentication.AspNet" Version="2.0.1" />
<PackageReference Include="Nager.Authentication.AspNet" Version="2.0.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
</ItemGroup>

Expand Down
13 changes: 0 additions & 13 deletions src/Backend/Nager.AuthenticationService.WebApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,17 +209,4 @@
app.MapFallbackToFile("index.html");
app.MapControllers();

//using (var serviceScope = app.Services.CreateScope())
//{
// var services = serviceScope.ServiceProvider;

// var userManagementService = services.GetRequiredService<IUserRepository>();
// var xxx = await userManagementService.GetAsync(o => o.EmailAddress.Equals("[email protected]"));

// var userAccountService = services.GetRequiredService<IUserAccountService>();
// await userAccountService.GetMfaInformationAsync("[email protected]");

//}


app.Run();
2 changes: 1 addition & 1 deletion src/Frontend/src/components/AuthenticatedAccount.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ async function logout () {
to="/account"
square
icon="person"
label="Account"
label="My Account"
class="full-width q-pa-md"
flat
/>
Expand Down
199 changes: 163 additions & 36 deletions src/Frontend/src/components/MfaConfigurationBox.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,77 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useQuasar, QStepper } from 'quasar'
import { apiHelper } from '../helpers/apiHelper'
const mfa = ref()
import { instanceOfMfaError } from 'src/models/MfaResponse'
import { MfaError } from 'src/models/MfaError'
import { MfaInformation } from 'src/models/MfaInformation'
const $q = useQuasar()
const step = ref(1)
const stepper = ref<QStepper>()
const mfa = ref<MfaInformation>()
const token = ref('')
onMounted(async () => {
mfa.value = await apiHelper.mfa()
await getMfaStatus()
})
function activate () {
apiHelper.mfaActivate(token.value)
async function getMfaStatus () {
mfa.value = await apiHelper.mfaInfo()
}
async function activate () {
try {
const mfaResponse = await apiHelper.mfaActivate(token.value)
if (instanceOfMfaError(mfaResponse)) {
$q.notify({
type: 'negative',
caption: (mfaResponse as MfaError).error
})
return
}
token.value = ''
step.value = 1
} catch (e) {
console.error(e)
}
await getMfaStatus()
}
async function deactivate () {
try {
const mfaResponse = await apiHelper.mfaDeactivate(token.value)
if (instanceOfMfaError(mfaResponse)) {
$q.notify({
type: 'negative',
caption: (mfaResponse as MfaError).error
})
}
token.value = ''
} catch (e) {
console.error(e)
}
await getMfaStatus()
}
function deactivate () {
apiHelper.mfaDeactivate(token.value)
async function stepperNext () {
stepper.value?.next()
if (step.value === 3) {
await activate()
}
}
function stepperPrevious () {
stepper.value?.previous()
}
</script>
Expand All @@ -24,36 +80,107 @@ function deactivate () {
<h2>Multi-factor authentication</h2>

<div v-if="mfa">
<q-checkbox
v-model="mfa.isActive"
disable
label="multi-factor authentication active"
/>
<br>

<img :src="mfa.activationQrCode">

<q-input
v-model="token"
outlined
label="Activation code"
hint="Enter the code from your authenticator app"
/>
<div class="q-mt-md">
<q-btn
v-if="!mfa.isActive"
color="black"
outline
label="Activate"
@click="activate()"
/>
<q-btn
v-if="mfa.isActive"
color="black"
outline
label="DEactivate"
@click="deactivate()"
/>
<q-stepper
v-if="!mfa.isActive"
ref="stepper"
v-model="step"
flat
bordered
color="primary"
>
<q-step
:name="1"
title="Scan code"
icon="qr_code"
active-icon="qr_code"
:done="step > 1"
class="text-center"
>
<div class="q-mb-md">
Please scan the QR code with your Authenticator app to complete the MFA setup.
</div>
<img :src="mfa.activationQrCode">
</q-step>

<q-step
:name="2"
title="Activate"
icon="password"
:done="step > 2"
>
<q-input
v-model="token"
outlined
label="Activation code"
hint="Enter the code from your authenticator app"
/>
</q-step>

<template #navigation>
<q-stepper-navigation>
<q-btn
v-if="step < 2"
outline
color="black"
label="Continue"
@click="stepperNext()"
/>
<q-btn
v-if="step === 2"
outline
color="black"
label="Activate"
@click="activate()"
/>
<q-btn
v-if="step > 1"

outline
color="black"
label="Back"
class="q-ml-sm"
@click="stepperPrevious()"
/>
</q-stepper-navigation>
</template>
</q-stepper>
<div v-else>
<div>
<div class="mfa-box bg-green q-pa-md rounded-borders">
<q-icon
name="security"
color="white"
size="3rem"
class="q-pr-md"
/>
MFA is active
</div>

<div class="q-mt-md">
To deactivate MFA, please provide another code.
<q-input
v-model="token"
outlined
dense
label="Deactivation code"
hint="Enter the code from your authenticator app"
/>
<q-btn
class="q-mt-md"
color="black"
outline
label="Deactivate"
@click="deactivate()"
/>
</div>
</div>
</div>
</div>
</template>

<style scoped>
.mfa-box {
font-size: 2rem;
color: white;
}
</style>
62 changes: 35 additions & 27 deletions src/Frontend/src/components/PasswordChangeBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,32 +48,40 @@ async function changePassword () {

<template>
<h2>Password</h2>
<div class="text-caption">
Choose a strong password that you do not use for other accounts.
</div>

<q-form>
<q-input
v-model="newPassword"
label="NewPassword"
autocomplete="new-password"
type="password"
class="q-mb-sm text-white bg-white"
outlined
/>
<q-input
v-model="newPasswordConfirm"
label="Confirm NewPassword"
autocomplete="new-password"
type="password"
class="q-mb-sm text-white bg-white"
outlined
/>
<q-btn
color="black"
outline
label="Change"
@click="changePassword()"
/>
</q-form>
<q-card
flat
bordered
>
<q-card-section>
<div class="text-caption q-mb-md">
Choose a strong password that you do not use for other accounts.
</div>

<q-form>
<q-input
v-model="newPassword"
label="NewPassword"
autocomplete="new-password"
type="password"
class="q-mb-sm text-white bg-white"
outlined
/>
<q-input
v-model="newPasswordConfirm"
label="Confirm NewPassword"
autocomplete="new-password"
type="password"
class="q-mb-sm text-white bg-white"
outlined
/>
<q-btn
color="black"
outline
label="Change"
@click="changePassword()"
/>
</q-form>
</q-card-section>
</q-card>
</template>
27 changes: 20 additions & 7 deletions src/Frontend/src/helpers/apiHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import { UserAdd } from 'src/models/UserAdd'
import { UserEdit } from 'src/models/UserEdit'
import { AuthenticationResponse } from 'src/models/AuthenticationResponse'

import { MfaError } from 'src/models/MfaError'
import { MfaSuccess } from 'src/models/MfaSuccess'
import { MfaResponse } from 'src/models/MfaResponse'
import { MfaInformation } from 'src/models/MfaInformation'

import { tokenHelper } from './tokenHelper'

const apiBaseUrl = process.env.NODE_ENV === 'development' ? '/api/v1/' : '/auth/api/v1/'
Expand Down Expand Up @@ -245,7 +250,7 @@ async function changePassword (newPassword : string) : Promise<boolean> {
return true
}

async function mfa () : Promise<undefined> {
async function mfaInfo () : Promise<MfaInformation> {
const token = tokenHelper.getToken()

const response = await fetch(`${apiBaseUrl}UserAccount/Mfa`, {
Expand All @@ -255,10 +260,10 @@ async function mfa () : Promise<undefined> {
}
})

return await response.json()
return await response.json() as MfaInformation
}

async function mfaActivate (mfaToken: string) : Promise<undefined> {
async function mfaActivate (mfaToken: string) : Promise<MfaResponse> {
const token = tokenHelper.getToken()

const response = await fetch(`${apiBaseUrl}UserAccount/Mfa/Activate`, {
Expand All @@ -272,10 +277,14 @@ async function mfaActivate (mfaToken: string) : Promise<undefined> {
})
})

return await response.json()
if (response.status === 204) {
return { success: true } as MfaSuccess
}

return await response.json() as MfaError
}

async function mfaDeactivate (mfaToken: string) : Promise<undefined> {
async function mfaDeactivate (mfaToken: string) : Promise<MfaResponse> {
const token = tokenHelper.getToken()

const response = await fetch(`${apiBaseUrl}UserAccount/Mfa/Deactivate`, {
Expand All @@ -289,7 +298,11 @@ async function mfaDeactivate (mfaToken: string) : Promise<undefined> {
})
})

return await response.json()
if (response.status === 204) {
return { success: true } as MfaSuccess
}

return await response.json() as MfaError
}

export const apiHelper = {
Expand All @@ -301,7 +314,7 @@ export const apiHelper = {
addRoleToUser,
removeRoleFromUser,
changePassword,
mfa,
mfaInfo,
mfaActivate,
mfaDeactivate
}
Loading

0 comments on commit fffec90

Please sign in to comment.