Commit 2beaef7d authored by David Burke's avatar David Burke

Start activate mfa

parent a1b693f6
Pipeline #63146813 passed with stage
in 8 minutes and 11 seconds
......@@ -52,6 +52,7 @@ import { AccountRoutingModule } from "./account-routing.module";
import { ManageMfaContainer } from "./manage-mfa/manage-mfa.container";
import { ManageMfaComponent } from "./manage-mfa/manage-mfa.component";
import { SplitMfaLinkPipe } from "./manage-mfa/split-mfa-link.pipe";
import { ManageMFAEffects } from "./manage-mfa/manage-mfa.effects";
export const COMPONENTS = [
RegisterComponent,
......@@ -105,7 +106,8 @@ export const SERVICES = [BackupCodePdfService, UserService, ConfirmEmailGuard];
SetPasswordEffects,
ManageBackupCodeEffects,
ChangePasswordEffects,
DeleteEffects
DeleteEffects,
ManageMFAEffects
]),
AccountRoutingModule
],
......
......@@ -317,3 +317,7 @@ export const getMfaErrorMessage = createSelector(
selectManageMfaState,
fromManageMfa.getErrorMessage
);
export const getMfaProvisioningURI = createSelector(
selectManageMfaState,
fromManageMfa.getProvisioningURI
);
......@@ -4,6 +4,9 @@ export enum ManageMfaActionTypes {
ENABLE_MFA = "[ENABLE MFA] Enable",
ENABLE_MFA_SUCCESS = "[ENABLE MFA] Enable Success",
ENABLE_MFA_FAILURE = "[ENABLE MFA] Enable Failure",
ACTIVATE_MFA = "[ENABLE MFA] Activate",
ACTIVATE_MFA_SUCCESS = "[ENABLE MFA] Activate Success",
ACTIVATE_MFA_FAILURE = "[ENABLE MFA] Activate Failure",
RESET_FORM = "[Reset] = Reset Form"
}
......@@ -13,6 +16,8 @@ export class EnableMfa implements Action {
export class EnableMfaSuccess implements Action {
readonly type = ManageMfaActionTypes.ENABLE_MFA_SUCCESS;
constructor(public payload: string) {}
}
export class EnableMfaFailure implements Action {
......@@ -21,6 +26,18 @@ export class EnableMfaFailure implements Action {
constructor(public payload: string[]) {}
}
export class ActivateMfa implements Action {
readonly type = ManageMfaActionTypes.ACTIVATE_MFA;
}
export class ActivateMfaSuccess implements Action {
readonly type = ManageMfaActionTypes.ACTIVATE_MFA_SUCCESS;
}
export class ActivateMfaFailure implements Action {
readonly type = ManageMfaActionTypes.ACTIVATE_MFA_FAILURE;
}
export class ResetForm implements Action {
readonly type = ManageMfaActionTypes.RESET_FORM;
}
......@@ -29,4 +46,7 @@ export type ManageMfaActions =
| EnableMfa
| EnableMfaSuccess
| EnableMfaFailure
| ActivateMfa
| ActivateMfaSuccess
| ActivateMfaFailure
| ResetForm;
......@@ -28,8 +28,8 @@
Two-factor authentication is currently enabled on your account.
</div>
<div class="u-margin-b-15">
If you no longer wish to use two-factor authentication, enter your
current password to disable your account.
If you no longer wish to use two-factor authentication, enter your current
password to disable your account.
</div>
</div>
......@@ -75,14 +75,13 @@
target="_blank"
link="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=en_US"
>Google Authenticator</app-text-link
>, or
>,
<app-text-link
isExternal="true"
target="_blank"
link="https://www.microsoft.com/en-us/p/microsoft-authenticator/9nblgggzmcj6?activetab=pivot:overviewtab"
>Windows Authenticator</app-text-link
>
on you mobile device.
>, or your favorite two-factor app on you mobile device.
</div>
<div class="text--large text--less-bottom-margin">
Once you have the app running, proceed to the next step.
......@@ -108,11 +107,11 @@
Scan the QR code in your authenticator app or enter the code below.
</div>
<div>
<img style="margin-left: -14px" [src]="qrCode" />
<img style="margin-left: -14px" *ngIf="qrCode" [src]="qrCode" />
</div>
<div class="code-spacing">
<a [href]="uri">
<a [href]="getURI()" *ngIf="uri">
{{ uri | splitMfaLink }}
</a>
</div>
......
import { Component, Input, Output, EventEmitter } from "@angular/core";
import * as QRCode from "qrcode";
import { DomSanitizer, SafeUrl } from "@angular/platform-browser";
@Component({
selector: "app-manage-mfa",
......@@ -11,14 +13,33 @@ import { Component, Input, Output, EventEmitter } from "@angular/core";
]
})
export class ManageMfaComponent {
@Input() uri: string;
@Input() qrCode: string;
constructor(private _sanitizer: DomSanitizer) {}
private _uri: string;
@Input() mfaEnabled: boolean;
qrCode: string | null;
@Output() generateMfa = new EventEmitter();
@Input()
set uri(value: string) {
this._uri = value;
if (value) {
QRCode.toDataURL(value).then(dataUrl => (this.qrCode = dataUrl));
} else {
this.qrCode = null;
}
}
get uri() {
return this._uri;
}
stepDownloadApp = 0;
getURI(): SafeUrl {
return this._sanitizer.bypassSecurityTrustUrl(this._uri);
}
nextStep() {
this.stepDownloadApp++;
if (this.stepDownloadApp === 2) {
......@@ -27,13 +48,11 @@ export class ManageMfaComponent {
}
onSubmit() {
console.log("form submitted");
this.stepDownloadApp++;
this.mfaEnabled = true;
}
disableMfa() {
this.mfaEnabled = false;
console.log("mfa has been disabled");
}
}
import { Component } from "@angular/core";
import * as QRCode from "qrcode";
import { UserService } from "../user";
import { Store, select } from "@ngrx/store";
import { IState } from "../../app.reducers";
import { EnableMfa } from "./manage-mfa.actions";
import { getMfaProvisioningURI } from "../account.reducer";
import { VerifyMfa } from "../../login/verify-mfa/verify-mfa.actions";
@Component({
template: `
<app-manage-mfa
[uri]="uri"
[qrCode]="qrCode"
[uri]="uri$ | async"
(generateMfa)="generateMfa()"
></app-manage-mfa>
`
})
export class ManageMfaContainer {
uri: string;
qrCode: string;
constructor(private service: UserService) {}
uri$ = this.store.pipe(select(getMfaProvisioningURI));
constructor(private store: Store<IState>) {}
generateMfa() {
this.service
.generateMfa()
.toPromise()
.then(resp => {
this.uri = resp.provisioning_uri;
QRCode.toDataURL(this.uri).then(dataUrl => (this.qrCode = dataUrl));
});
this.store.dispatch(new EnableMfa());
}
verifyMfa() {
this.store.dispatch(new VerifyMfa());
}
}
import { Injectable } from "@angular/core";
import { Effect, Actions, ofType } from "@ngrx/effects";
import { UserService } from "../user";
import {
ManageMfaActionTypes,
EnableMfaSuccess,
EnableMfaFailure,
ActivateMfaFailure,
ActivateMfaSuccess
} from "./manage-mfa.actions";
import { exhaustMap, map, catchError } from "rxjs/operators";
import { of } from "rxjs";
@Injectable()
export class ManageMFAEffects {
@Effect()
generateMfa$ = this.actions$.pipe(
ofType(ManageMfaActionTypes.ENABLE_MFA),
exhaustMap(action =>
this.service.generateMfa().pipe(
map(resp => new EnableMfaSuccess(resp.provisioning_uri)),
catchError(resp => of(new EnableMfaFailure(resp)))
)
)
);
@Effect()
activateMfa$ = this.actions$.pipe(
ofType(ManageMfaActionTypes.ACTIVATE_MFA),
exhaustMap(action =>
this.service.activateMfa(1, 2).pipe(
map(resp => new ActivateMfaSuccess()),
catchError(resp => of(new ActivateMfaFailure()))
)
)
);
constructor(private actions$: Actions, private service: UserService) {}
}
......@@ -15,6 +15,7 @@ export interface IEnableMfaForm {
}
export interface IEnbleMfaState {
form: FormGroupState<IEnableMfaForm>;
provisioningURI: string | null;
mfaEnabled: boolean;
errorMessage: string[] | null;
}
......@@ -31,6 +32,7 @@ export const initialFormState = validateAndUpdateFormState(
export const initialState: IEnbleMfaState = {
form: initialFormState,
provisioningURI: null,
errorMessage: null,
mfaEnabled: false
};
......@@ -47,14 +49,10 @@ export function reducer(
state = { ...state, form };
switch (action.type) {
case ManageMfaActionTypes.ENABLE_MFA:
return {
...state
};
case ManageMfaActionTypes.ENABLE_MFA_SUCCESS:
return {
...state,
provisioningURI: action.payload,
mfaEnabled: true
};
......@@ -73,3 +71,5 @@ export function reducer(
export const getForm = (state: IEnbleMfaState) => state.form;
export const getMfaEnabled = (state: IEnbleMfaState) => state.mfaEnabled;
export const getErrorMessage = (state: IEnbleMfaState) => state.errorMessage;
export const getProvisioningURI = (state: IEnbleMfaState) =>
state.provisioningURI;
......@@ -428,6 +428,14 @@ export class UserService {
return this.http.post<{ provisioning_uri: string }>(url, "");
}
/** Activate a generated MFA code */
activateMfa(otp: number, id: number) {
const url = this.sdk.formUrl("activate-mfa/");
const data = { otp, id };
return this.http.post(url, data);
}
/** Verify a OTP, used for login */
verifyMfa(otp: string) {
const url = this.sdk.formUrl("verify-mfa/");
const data = { otp };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment