Added fingerprinting + store in firebase for user auth. BOD-132
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"dinero.js": "^1.8.1",
|
"dinero.js": "^1.8.1",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
|
"fingerprintjs2": "^2.1.0",
|
||||||
"firebase": "^7.14.3",
|
"firebase": "^7.14.3",
|
||||||
"graphql": "^15.0.0",
|
"graphql": "^15.0.0",
|
||||||
"i18next": "^19.4.4",
|
"i18next": "^19.4.4",
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class FcmNotificationComponent extends Component {
|
|||||||
.requestPermission()
|
.requestPermission()
|
||||||
.then(async function () {
|
.then(async function () {
|
||||||
const token = await messaging.getToken();
|
const token = await messaging.getToken();
|
||||||
|
console.log("Instance Token", token);
|
||||||
client.mutate({
|
client.mutate({
|
||||||
mutation: UPDATE_FCM_TOKEN,
|
mutation: UPDATE_FCM_TOKEN,
|
||||||
variables: { authEmail: currentUser.email, token: { [token]: true } },
|
variables: { authEmail: currentUser.email, token: { [token]: true } },
|
||||||
|
|||||||
@@ -1,55 +1,69 @@
|
|||||||
import UserActionTypes from "./user.types";
|
import UserActionTypes from "./user.types";
|
||||||
|
|
||||||
export const signInSuccess = user => ({
|
export const signInSuccess = (user) => ({
|
||||||
type: UserActionTypes.SIGN_IN_SUCCESS,
|
type: UserActionTypes.SIGN_IN_SUCCESS,
|
||||||
payload: user
|
payload: user,
|
||||||
});
|
});
|
||||||
export const signInFailure = errorMsg => ({
|
export const signInFailure = (errorMsg) => ({
|
||||||
type: UserActionTypes.SIGN_IN_FAILURE,
|
type: UserActionTypes.SIGN_IN_FAILURE,
|
||||||
payload: errorMsg
|
payload: errorMsg,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const emailSignInStart = emailAndPassword => ({
|
export const emailSignInStart = (emailAndPassword) => ({
|
||||||
type: UserActionTypes.EMAIL_SIGN_IN_START,
|
type: UserActionTypes.EMAIL_SIGN_IN_START,
|
||||||
payload: emailAndPassword
|
payload: emailAndPassword,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const checkUserSession = () => ({
|
export const checkUserSession = () => ({
|
||||||
type: UserActionTypes.CHECK_USER_SESSION
|
type: UserActionTypes.CHECK_USER_SESSION,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const signOutStart = () => ({
|
export const signOutStart = () => ({
|
||||||
type: UserActionTypes.SIGN_OUT_START
|
type: UserActionTypes.SIGN_OUT_START,
|
||||||
});
|
});
|
||||||
export const signOutSuccess = () => ({
|
export const signOutSuccess = () => ({
|
||||||
type: UserActionTypes.SIGN_OUT_SUCCESS
|
type: UserActionTypes.SIGN_OUT_SUCCESS,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const signOutFailure = error => ({
|
export const signOutFailure = (error) => ({
|
||||||
type: UserActionTypes.SIGN_OUT_FAILURE,
|
type: UserActionTypes.SIGN_OUT_FAILURE,
|
||||||
payload: error
|
payload: error,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const unauthorizedUser = () => ({
|
export const unauthorizedUser = () => ({
|
||||||
type: UserActionTypes.UNAUTHORIZED_USER
|
type: UserActionTypes.UNAUTHORIZED_USER,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setUserLanguage = language => ({
|
export const setUserLanguage = (language) => ({
|
||||||
type: UserActionTypes.SET_USER_LANGUAGE,
|
type: UserActionTypes.SET_USER_LANGUAGE,
|
||||||
payload: language
|
payload: language,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const updateUserDetails = userDetails => ({
|
export const updateUserDetails = (userDetails) => ({
|
||||||
type: UserActionTypes.UPDATE_USER_DETAILS,
|
type: UserActionTypes.UPDATE_USER_DETAILS,
|
||||||
payload: userDetails
|
payload: userDetails,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const updateUserDetailsSuccess = userDetails => ({
|
export const updateUserDetailsSuccess = (userDetails) => ({
|
||||||
type: UserActionTypes.UPDATE_USER_DETAILS_SUCCESS,
|
type: UserActionTypes.UPDATE_USER_DETAILS_SUCCESS,
|
||||||
payload: userDetails
|
payload: userDetails,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const setBodyshop = bodyshop => ({
|
export const setBodyshop = (bodyshop) => ({
|
||||||
type: UserActionTypes.SET_SHOP_DETAILS,
|
type: UserActionTypes.SET_SHOP_DETAILS,
|
||||||
payload: bodyshop
|
payload: bodyshop,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setInstanceId = (userInfo) => ({
|
||||||
|
type: UserActionTypes.SET_INSTANCE_ID,
|
||||||
|
payload: userInfo,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const checkInstanceId = (uid) => ({
|
||||||
|
type: UserActionTypes.CHECK_INSTANCE_ID,
|
||||||
|
payload: uid,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const setInstanceConflict = () => ({
|
||||||
|
type: UserActionTypes.SET_INSTANCE_CONFLICT,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,41 +6,44 @@ const INITIAL_STATE = {
|
|||||||
//language: "en-US"
|
//language: "en-US"
|
||||||
},
|
},
|
||||||
bodyshop: null,
|
bodyshop: null,
|
||||||
error: null
|
error: null,
|
||||||
|
conflict: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const userReducer = (state = INITIAL_STATE, action) => {
|
const userReducer = (state = INITIAL_STATE, action) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
|
case UserActionTypes.SET_INSTANCE_CONFLICT:
|
||||||
|
return { ...state, conflict: true };
|
||||||
case UserActionTypes.SIGN_IN_SUCCESS:
|
case UserActionTypes.SIGN_IN_SUCCESS:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentUser: action.payload,
|
currentUser: action.payload,
|
||||||
error: null
|
error: null,
|
||||||
};
|
};
|
||||||
case UserActionTypes.SIGN_OUT_SUCCESS:
|
case UserActionTypes.SIGN_OUT_SUCCESS:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentUser: { authorized: false },
|
currentUser: { authorized: false },
|
||||||
error: null
|
error: null,
|
||||||
};
|
};
|
||||||
case UserActionTypes.UNAUTHORIZED_USER:
|
case UserActionTypes.UNAUTHORIZED_USER:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
error: null,
|
error: null,
|
||||||
currentUser: { authorized: false }
|
currentUser: { authorized: false },
|
||||||
};
|
};
|
||||||
case UserActionTypes.SET_USER_LANGUAGE:
|
case UserActionTypes.SET_USER_LANGUAGE:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
language: action.payload
|
language: action.payload,
|
||||||
};
|
};
|
||||||
case UserActionTypes.UPDATE_USER_DETAILS_SUCCESS:
|
case UserActionTypes.UPDATE_USER_DETAILS_SUCCESS:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
currentUser: {
|
currentUser: {
|
||||||
...state.currentUser,
|
...state.currentUser,
|
||||||
...action.payload //Spread current user details in.
|
...action.payload, //Spread current user details in.
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
case UserActionTypes.SET_SHOP_DETAILS:
|
case UserActionTypes.SET_SHOP_DETAILS:
|
||||||
@@ -50,7 +53,7 @@ const userReducer = (state = INITIAL_STATE, action) => {
|
|||||||
case UserActionTypes.EMAIL_SIGN_UP_FAILURE:
|
case UserActionTypes.EMAIL_SIGN_UP_FAILURE:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
error: action.payload
|
error: action.payload,
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { all, call, put, takeLatest } from "redux-saga/effects";
|
import { all, call, put, takeLatest, delay } from "redux-saga/effects";
|
||||||
import LogRocket from "logrocket";
|
import LogRocket from "logrocket";
|
||||||
|
import { firestore } from "../../firebase/firebase.utils";
|
||||||
|
import Fingerprint2 from "fingerprintjs2";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
auth,
|
auth,
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
@@ -12,12 +15,18 @@ import {
|
|||||||
signOutSuccess,
|
signOutSuccess,
|
||||||
unauthorizedUser,
|
unauthorizedUser,
|
||||||
updateUserDetailsSuccess,
|
updateUserDetailsSuccess,
|
||||||
|
setInstanceId,
|
||||||
|
checkInstanceId,
|
||||||
|
setInstanceConflict,
|
||||||
} from "./user.actions";
|
} from "./user.actions";
|
||||||
import UserActionTypes from "./user.types";
|
import UserActionTypes from "./user.types";
|
||||||
|
|
||||||
export function* signInWithEmail({ payload: { email, password } }) {
|
export function* signInWithEmail({ payload: { email, password } }) {
|
||||||
try {
|
try {
|
||||||
const { user } = yield auth.signInWithEmailAndPassword(email, password);
|
const { user } = yield auth.signInWithEmailAndPassword(email, password);
|
||||||
|
LogRocket.identify(user.email);
|
||||||
|
|
||||||
|
yield put(setInstanceId("123"));
|
||||||
yield put(
|
yield put(
|
||||||
signInSuccess({
|
signInSuccess({
|
||||||
uid: user.uid,
|
uid: user.uid,
|
||||||
@@ -35,7 +44,6 @@ export function* signInWithEmail({ payload: { email, password } }) {
|
|||||||
export function* onEmailSignInStart() {
|
export function* onEmailSignInStart() {
|
||||||
yield takeLatest(UserActionTypes.EMAIL_SIGN_IN_START, signInWithEmail);
|
yield takeLatest(UserActionTypes.EMAIL_SIGN_IN_START, signInWithEmail);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* isUserAuthenticated() {
|
export function* isUserAuthenticated() {
|
||||||
try {
|
try {
|
||||||
const user = yield getCurrentUser();
|
const user = yield getCurrentUser();
|
||||||
@@ -43,6 +51,7 @@ export function* isUserAuthenticated() {
|
|||||||
yield put(unauthorizedUser());
|
yield put(unauthorizedUser());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
yield put(setInstanceId(user.uid));
|
||||||
LogRocket.identify(user.email);
|
LogRocket.identify(user.email);
|
||||||
yield put(
|
yield put(
|
||||||
signInSuccess({
|
signInSuccess({
|
||||||
@@ -57,11 +66,9 @@ export function* isUserAuthenticated() {
|
|||||||
yield put(signInFailure(error));
|
yield put(signInFailure(error));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* onCheckUserSession() {
|
export function* onCheckUserSession() {
|
||||||
yield takeLatest(UserActionTypes.CHECK_USER_SESSION, isUserAuthenticated);
|
yield takeLatest(UserActionTypes.CHECK_USER_SESSION, isUserAuthenticated);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* signOutStart() {
|
export function* signOutStart() {
|
||||||
try {
|
try {
|
||||||
yield auth.signOut();
|
yield auth.signOut();
|
||||||
@@ -71,11 +78,9 @@ export function* signOutStart() {
|
|||||||
yield put(signOutFailure(error.message));
|
yield put(signOutFailure(error.message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* onSignOutStart() {
|
export function* onSignOutStart() {
|
||||||
yield takeLatest(UserActionTypes.SIGN_OUT_START, signOutStart);
|
yield takeLatest(UserActionTypes.SIGN_OUT_START, signOutStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* onUpdateUserDetails() {
|
export function* onUpdateUserDetails() {
|
||||||
yield takeLatest(UserActionTypes.UPDATE_USER_DETAILS, updateUserDetails);
|
yield takeLatest(UserActionTypes.UPDATE_USER_DETAILS, updateUserDetails);
|
||||||
}
|
}
|
||||||
@@ -88,6 +93,70 @@ export function* updateUserDetails(userDetails) {
|
|||||||
//TODO error handling
|
//TODO error handling
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export function* onSetInstanceId() {
|
||||||
|
yield takeLatest(UserActionTypes.SET_INSTANCE_ID, setInstanceIdSaga);
|
||||||
|
}
|
||||||
|
export function* setInstanceIdSaga({ payload: uid }) {
|
||||||
|
try {
|
||||||
|
const userInstanceRef = firestore.doc(`userInstance/${uid}`);
|
||||||
|
|
||||||
|
const fingerprint = Fingerprint2.x64hash128(
|
||||||
|
(yield Fingerprint2.getPromise({
|
||||||
|
excludes: { fonts: true, audio: true, webgl: true },
|
||||||
|
})).join(""),
|
||||||
|
31
|
||||||
|
);
|
||||||
|
|
||||||
|
yield userInstanceRef.set({
|
||||||
|
timestamp: new Date(),
|
||||||
|
fingerprint,
|
||||||
|
});
|
||||||
|
|
||||||
|
yield delay(5000);
|
||||||
|
yield put(checkInstanceId(uid));
|
||||||
|
} catch (error) {
|
||||||
|
console.log("error", error);
|
||||||
|
//yield put(signOutFailure(error.message));
|
||||||
|
//TODO error handling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* onCheckInstanceId() {
|
||||||
|
yield takeLatest(UserActionTypes.CHECK_INSTANCE_ID, checkInstanceIdSaga);
|
||||||
|
}
|
||||||
|
export function* checkInstanceIdSaga({ payload: uid }) {
|
||||||
|
try {
|
||||||
|
const userInstanceRef = firestore.doc(`userInstance/${uid}`);
|
||||||
|
|
||||||
|
const fingerprint = Fingerprint2.x64hash128(
|
||||||
|
(yield Fingerprint2.getPromise({
|
||||||
|
excludes: { fonts: true, audio: true, webgl: true },
|
||||||
|
})).join(""),
|
||||||
|
31
|
||||||
|
);
|
||||||
|
|
||||||
|
const snapshot = yield userInstanceRef.get();
|
||||||
|
console.log("function*checkInstanceIdSaga -> snapshot", snapshot.data());
|
||||||
|
console.log("fingerprint", fingerprint);
|
||||||
|
|
||||||
|
if (snapshot.data().fingerprint === fingerprint) {
|
||||||
|
console.log("Waiting and checking.");
|
||||||
|
yield delay(5000);
|
||||||
|
yield put(checkInstanceId(uid));
|
||||||
|
} else {
|
||||||
|
console.log("Didnt match");
|
||||||
|
yield put(setInstanceConflict());
|
||||||
|
}
|
||||||
|
// yield userInstanceRef.set({
|
||||||
|
// timestamp: new Date(),
|
||||||
|
// fingerprint,
|
||||||
|
// });
|
||||||
|
} catch (error) {
|
||||||
|
console.log("error", error);
|
||||||
|
//yield put(signOutFailure(error.message));
|
||||||
|
//TODO error handling
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function* userSagas() {
|
export function* userSagas() {
|
||||||
yield all([
|
yield all([
|
||||||
@@ -95,5 +164,7 @@ export function* userSagas() {
|
|||||||
call(onCheckUserSession),
|
call(onCheckUserSession),
|
||||||
call(onSignOutStart),
|
call(onSignOutStart),
|
||||||
call(onUpdateUserDetails),
|
call(onUpdateUserDetails),
|
||||||
|
call(onSetInstanceId),
|
||||||
|
call(onCheckInstanceId),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
import { createSelector } from "reselect";
|
import { createSelector } from "reselect";
|
||||||
|
|
||||||
const selectUser = state => state.user;
|
const selectUser = (state) => state.user;
|
||||||
|
|
||||||
export const selectCurrentUser = createSelector(
|
export const selectCurrentUser = createSelector(
|
||||||
[selectUser],
|
[selectUser],
|
||||||
user => user.currentUser
|
(user) => user.currentUser
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectSignInError = createSelector(
|
export const selectSignInError = createSelector(
|
||||||
[selectUser],
|
[selectUser],
|
||||||
user => user.error
|
(user) => user.error
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectBodyshop = createSelector(
|
export const selectBodyshop = createSelector(
|
||||||
[selectUser],
|
[selectUser],
|
||||||
user => user.bodyshop
|
(user) => user.bodyshop
|
||||||
|
);
|
||||||
|
|
||||||
|
export const selectInstanceConflict = createSelector(
|
||||||
|
[selectUser],
|
||||||
|
(user) => user.conflict
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ const UserActionTypes = {
|
|||||||
SET_USER_LANGUAGE: "SET_USER_LANGUAGE",
|
SET_USER_LANGUAGE: "SET_USER_LANGUAGE",
|
||||||
UPDATE_USER_DETAILS: "UPDATE_USER_DETAILS",
|
UPDATE_USER_DETAILS: "UPDATE_USER_DETAILS",
|
||||||
UPDATE_USER_DETAILS_SUCCESS: "UPDATE_USER_DETAILS_SUCCESS",
|
UPDATE_USER_DETAILS_SUCCESS: "UPDATE_USER_DETAILS_SUCCESS",
|
||||||
SET_SHOP_DETAILS: "SET_SHOP_DETAILS"
|
SET_SHOP_DETAILS: "SET_SHOP_DETAILS",
|
||||||
|
SET_INSTANCE_ID: "SET_INSTANCE_ID",
|
||||||
|
CHECK_INSTANCE_ID: "CHECK_INSTANCE_ID",
|
||||||
|
SET_INSTANCE_CONFLICT: "SET_INSTANCE_CONFLICT"
|
||||||
};
|
};
|
||||||
export default UserActionTypes;
|
export default UserActionTypes;
|
||||||
|
|||||||
@@ -5924,6 +5924,11 @@ find-up@^3.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
locate-path "^3.0.0"
|
locate-path "^3.0.0"
|
||||||
|
|
||||||
|
fingerprintjs2@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fingerprintjs2/-/fingerprintjs2-2.1.0.tgz#21dc3fee27d3b199056ef8eb873debccd8e06323"
|
||||||
|
integrity sha512-H1k/ESTD2rJ3liupyqWBPjZC+LKfCGixQzz/NDN4dkgbmG1bVFyMOh7luKSkVDoyfhgvRm62pviNMPI+eJTZcQ==
|
||||||
|
|
||||||
firebase@^7.14.3:
|
firebase@^7.14.3:
|
||||||
version "7.14.3"
|
version "7.14.3"
|
||||||
resolved "https://registry.yarnpkg.com/firebase/-/firebase-7.14.3.tgz#dfe6fa3e5982a6d6d6d44bfc50dba23568a5a777"
|
resolved "https://registry.yarnpkg.com/firebase/-/firebase-7.14.3.tgz#dfe6fa3e5982a6d6d6d44bfc50dba23568a5a777"
|
||||||
|
|||||||
Reference in New Issue
Block a user