Commit c7880df9 authored by David Burke's avatar David Burke

Store mfa required info

parent d67ca397
Pipeline #63363579 failed with stage
in 6 minutes
......@@ -4174,6 +4174,12 @@
"xregexp": "4.0.0"
}
},
"deepmerge": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==",
"dev": true
},
"execa": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz",
......@@ -7354,10 +7360,9 @@
"dev": true
},
"deepmerge": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==",
"dev": true
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.2.0.tgz",
"integrity": "sha512-6+LuZGU7QCNUnAJyX8cIrlzoEgggTM6B7mm+znKOX4t5ltluT9KLjN6g61ECMS0LTsLW7yDpNoxhix5FZcrIow=="
},
"default-gateway": {
"version": "2.7.2",
......@@ -16074,6 +16079,14 @@
"next-tick": "^1.0.0",
"request": "^2.83.0",
"stream-parser": "~0.3.1"
},
"dependencies": {
"deepmerge": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==",
"dev": true
}
}
},
"process": {
......@@ -20232,7 +20245,7 @@
},
"text-encoding": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
"resolved": "http://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
"integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk="
},
"text-encoding-utf-8": {
......@@ -20572,6 +20585,12 @@
"strip-bom": "^3.0.0"
},
"dependencies": {
"deepmerge": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==",
"dev": true
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
......
......@@ -6,6 +6,7 @@ export interface IAuthStore {
userToken: string;
rememberMe: boolean;
optInErrorReporting: boolean;
mfaRequired: boolean;
}
export interface IResetPasswordVerifyResponse {
......
......@@ -133,9 +133,9 @@ export class UserService {
email,
userToken: resp.token,
rememberMe,
optInErrorReporting: resp.user.opt_in_error_reporting
optInErrorReporting: resp.user.opt_in_error_reporting,
mfaRequired: resp.user.mfa_required
};
this.setUp(auth);
return auth;
}),
......@@ -206,7 +206,8 @@ export class UserService {
email,
userToken: resp.token,
rememberMe,
optInErrorReporting: resp.user.opt_in_error_reporting
optInErrorReporting: resp.user.opt_in_error_reporting,
mfaRequired: resp.user.mfa_required
};
this.setUp(auth);
......@@ -269,7 +270,8 @@ export class UserService {
publicKey: auth.publicKey,
userToken: auth.userToken,
rememberMe: auth.rememberMe,
optInErrorReporting: auth.optInErrorReporting
optInErrorReporting: auth.optInErrorReporting,
mfaRequired: auth.mfaRequired
};
this.setUp(authStore);
}
......@@ -340,7 +342,8 @@ export class UserService {
email,
userToken: resp.token,
rememberMe: false,
optInErrorReporting: resp.user.opt_in_error_reporting
optInErrorReporting: resp.user.opt_in_error_reporting,
mfaRequired: resp.user.mfa_required
};
return auth;
})
......
......@@ -18,6 +18,7 @@ export const routes: Routes = [
{
path: "account/login/verify-mfa",
component: VerifyMfaContainer,
canActivate: [LoggedInGuard],
data: {
title: "Verify MFA",
showNavBar: false
......
......@@ -163,3 +163,7 @@ export const getOptInErrorReporting = createSelector(
selectAuthState,
(state: fromAuth.IAuthState) => state.optInErrorReporting
);
export const getMfaRequired = createSelector(
selectAuthState,
(state: fromAuth.IAuthState) => state.mfaRequired
);
......@@ -32,6 +32,7 @@ export interface IAuthState {
publicKey: string | null;
rememberMe: boolean;
optInErrorReporting: boolean;
mfaRequired: boolean;
/** Always redirect logged in user to set password page */
forceSetPassword: boolean;
}
......@@ -45,6 +46,7 @@ export const initialState: IAuthState = {
publicKey: null,
rememberMe: false,
optInErrorReporting: false,
mfaRequired: false,
forceSetPassword: false
};
......@@ -78,7 +80,8 @@ export function authReducer(
privateKey: action.payload.privateKey,
publicKey: action.payload.publicKey,
rememberMe: action.payload.rememberMe,
optInErrorReporting: action.payload.optInErrorReporting
optInErrorReporting: action.payload.optInErrorReporting,
mfaRequired: action.payload.mfaRequired
};
case ChangePasswordActionTypes.SUBMIT_FORM_SUCCESS:
......
......@@ -2,7 +2,7 @@
<div class="account-column__inner account-column__inner--right">
<div class="account-column__right-heading">
<h1 class="heading-medium">Log&nbsp;In</h1>
<div *ngIf="linkRoute && linkText" class="account-column__link">
<div *ngIf="linkText" class="account-column__link">
<app-text-link
caret="right"
id="btn-signup"
......
......@@ -28,7 +28,10 @@ import {
LoginSuccessAction,
LoginFailureAction
} from "../app.actions";
import { SetPasswordActionTypes } from "../account/reset-password/set-password/set-password.actions";
import {
SetPasswordActionTypes,
SetPasswordSuccess
} from "../account/reset-password/set-password/set-password.actions";
import { IS_EXTENSION } from "../constants";
@Injectable()
......@@ -96,14 +99,20 @@ export class LoginEffects {
@Effect({ dispatch: false })
loginSuccess$ = this.actions$.pipe(
ofType(
ofType<LoginSuccessAction | SetPasswordSuccess>(
AppActionTypes.LOGIN_SUCCESS,
SetPasswordActionTypes.SET_PASSWORD_SUCCESS
),
tap(() => {
tap(action => {
if (IS_EXTENSION) {
this.router.navigate(["/popup"]);
} else {
if (action.type === AppActionTypes.LOGIN_SUCCESS) {
if (action.payload.mfaRequired) {
this.router.navigate(["/account/login/verify-mfa"]);
return;
}
}
this.router.navigate(["/list"]);
}
})
......
<app-login-wrapper
[isPopup]="isPopup"
(clickLink)="goToLogin.emit()"
linkRoute="/account/login"
linkText="Return to Login"
>
<form
......
......@@ -11,6 +11,8 @@ import { VerifyMfa } from "./verify-mfa.actions";
import * as fromRoot from "../../app.reducers";
import { IS_EXTENSION } from "../../constants";
import { Router } from "@angular/router";
import { LogoutAction } from "../../account/account.actions";
import { ResetFormContainer } from "../../form";
@Component({
template: `
......@@ -28,7 +30,7 @@ import { Router } from "@angular/router";
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class VerifyMfaContainer {
export class VerifyMfaContainer extends ResetFormContainer {
form$ = this.store.pipe(select(selectVerifyMfaForm));
hasStarted$ = this.store.pipe(select(getVerifyMfaHasStarted));
hasFinished$ = this.store.pipe(select(getVerifyMfaFinished));
......@@ -37,13 +39,16 @@ export class VerifyMfaContainer {
isExtension = IS_EXTENSION;
isPopup = false;
constructor(private router: Router, private store: Store<IState>) {
constructor(private router: Router, public store: Store<IState>) {
super(store);
store
.pipe(select(fromRoot.getIsPopup))
.subscribe(isPopup => (this.isPopup = isPopup));
}
goToLogin() {
// Technically, the user is logged in during this stage. Log them out before redirecting.
this.store.dispatch(new LogoutAction());
if (this.isPopup) {
browser.tabs.create({
url: "/index.html#/account/login"
......
......@@ -9,6 +9,10 @@ import {
import { minLength, maxLength, required } from "ngrx-forms/validation";
import { IBaseFormState } from "../../utils/interfaces";
import { VerifyMfaActionTypes, VerifyMfaActions } from "./verify-mfa.actions";
import {
ResetFormActionTypes,
ResetFormActionsUnion
} from "../../form/reset-form.actions";
const FORM_ID = "Login Form";
......@@ -44,11 +48,14 @@ export const formReducer = createFormStateReducerWithUpdate<IVerifyMfaForm>(
export function reducer(
state = initialState,
action: VerifyMfaActions
action: VerifyMfaActions | ResetFormActionsUnion
): IVerifyMfaState {
const form = formReducer(state.form, action);
state = { ...state, form };
switch (action.type) {
case ResetFormActionTypes.RESET_FORMS:
return { ...initialState };
case VerifyMfaActionTypes.VERIFY_MFA:
return {
...state,
......
......@@ -5,6 +5,7 @@ export interface IUser {
private_key: string;
client_salt: string;
opt_in_error_reporting: boolean;
mfa_required: boolean;
}
export interface ILoginResonse {
......@@ -77,70 +78,70 @@ export interface ICreateGroupUser {
}
export interface IContact {
id: number;
email: string;
first_name: string;
last_name: string;
id: number;
email: string;
first_name: string;
last_name: string;
}
export interface IData {
[propName: string]: string | undefined;
[propName: string]: string | undefined;
}
export interface ICreateSecretThrough {
/** Encrypted data */
data: IData;
/** Group id. Null indicates it belongs to a user instead */
group?: number | null;
key_ciphertext: string;
/** Encrypted data */
data: IData;
/** Group id. Null indicates it belongs to a user instead */
group?: number | null;
key_ciphertext: string;
}
export interface ICreateSecretThroughGroup extends ICreateSecretThrough {
group: number;
group: number;
}
export interface ICreateSecret {
name: string;
/** Classification of secret, for example a website or note. */
type?: string;
/** Unencrypted data for less sensative infomation. */
data: IData;
/** Encrypted data */
secret_through_set: ICreateSecretThrough[];
name: string;
/** Classification of secret, for example a website or note. */
type?: string;
/** Unencrypted data for less sensative infomation. */
data: IData;
/** Encrypted data */
secret_through_set: ICreateSecretThrough[];
}
export interface ISecretThrough {
id: number;
/** Encrypted data */
data: IData;
/** Group id. Null indicates it belongs to a user instead */
group: number | null;
key_ciphertext: string;
public_key: string;
is_mine: boolean;
id: number;
/** Encrypted data */
data: IData;
/** Group id. Null indicates it belongs to a user instead */
group: number | null;
key_ciphertext: string;
public_key: string;
is_mine: boolean;
}
export interface ISecretThroughGroup extends ISecretThrough {
group: number;
group: number;
}
export interface ISecret {
id: number;
name: string;
/** Classification of secret, for example a website or note. */
type: string;
/** Unencrypted data for less sensative infomation. */
data: IData;
/** Encrypted data */
secret_through_set: ISecretThrough[];
id: number;
name: string;
/** Classification of secret, for example a website or note. */
type: string;
/** Unencrypted data for less sensative infomation. */
data: IData;
/** Encrypted data */
secret_through_set: ISecretThrough[];
}
export interface IUpdateSecret {
name?: string;
/** Classification of secret, for example a website or note. */
type?: string;
/** Unencrypted data for less sensative infomation. */
data?: IData;
name?: string;
/** Classification of secret, for example a website or note. */
type?: string;
/** Unencrypted data for less sensative infomation. */
data?: IData;
}
export interface IUpdateSecretThrough extends Partial<ICreateSecretThrough> {
......@@ -152,7 +153,7 @@ export interface IUpdateSecretWithThroughs extends IUpdateSecret {
}
export interface IChangePasswordSecretThrough extends ISecretThrough {
hash: string;
hash: string;
}
export interface IChangePasswordGroupUser extends IGroupUser {
......
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