-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature flagged mouse movement collection (#922)
* Collector * comments * Add type for react * Trigger only on form interaction * latest * Cleaned up collector, fixing ref issue * Collecting data for mouse, touch and keyboard events * Lint fix * Correcting types and adding mongo atlas event adding * Adding provider middleman for mongo * Provider with mongo setup * bundle side api not used * Moving db config into db package * Exporting db * fixing db import * cypress tests fix * bundling error fix --------- Co-authored-by: Chris Taylor <forgetso86@gmail.com>
- Loading branch information
Showing
20 changed files
with
281 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { StoredEvents } from '@prosopo/types' | ||
import mongoose from 'mongoose' | ||
|
||
const captchaEventSchema = new mongoose.Schema({ | ||
touchEvents: [ | ||
{ | ||
x: Number, | ||
y: Number, | ||
timestamp: Number, | ||
}, | ||
], | ||
mouseEvents: [ | ||
{ | ||
x: Number, | ||
y: Number, | ||
timestamp: Number, | ||
}, | ||
], | ||
keyboardEvents: [ | ||
{ | ||
key: String, | ||
timestamp: Number, | ||
isShiftKey: Boolean, | ||
isCtrlKey: Boolean, | ||
}, | ||
], | ||
accountId: String, | ||
}) | ||
|
||
const CaptchaEvent = mongoose.model('CaptchaEvent', captchaEventSchema) | ||
|
||
const addCaptchaEventRecord = async (record: { | ||
touchEvents?: { x: number; y: number; timestamp: number }[] | ||
mouseEvents?: { x: number; y: number; timestamp: number }[] | ||
keyboardEvents?: { key: string; timestamp: number; isShiftKey?: boolean; isCtrlKey?: boolean }[] | ||
accountId: string | ||
}): Promise<void> => { | ||
try { | ||
const newRecord = new CaptchaEvent(record) | ||
await newRecord.save() | ||
console.log('Record added successfully') | ||
} catch (error) { | ||
console.error('Error adding record to the database:', error) | ||
} | ||
} | ||
|
||
export const saveCaptchaEvent = async (events: StoredEvents, accountId: string, atlasUri: string) => { | ||
await mongoose | ||
.connect(atlasUri) | ||
.then(() => console.log('Connected to MongoDB Atlas')) | ||
.catch((err) => console.error('Error connecting to MongoDB:', err)) | ||
|
||
const captchaEventData = { | ||
...events, | ||
accountId, | ||
} | ||
|
||
addCaptchaEventRecord(captchaEventData) | ||
.then(() => console.log('Captcha event data saved')) | ||
.catch((error) => console.error('Error saving captcha event data:', error)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Copyright 2021-2023 Prosopo (UK) Ltd. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
export * from './eventsDatabase.js' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { MutableRefObject, useEffect, useRef, useState } from 'react' | ||
import { ProsopoKeyboardEvent, ProsopoMouseEvent, ProsopoTouchEvent, StoredEvents } from '@prosopo/types' | ||
import { startCollector } from '@prosopo/procaptcha' | ||
|
||
type CollectorProps = { | ||
onProcessData: (data: StoredEvents) => void | ||
sendData: boolean | ||
} | ||
|
||
const Collector = ({ onProcessData, sendData }: CollectorProps) => { | ||
const [mouseEvents, setStoredMouseEvents] = useState<ProsopoMouseEvent[]>([]) | ||
const [touchEvents, setStoredTouchEvents] = useState<ProsopoTouchEvent[]>([]) | ||
const [keyboardEvents, setStoredKeyboardEvents] = useState<ProsopoKeyboardEvent[]>([]) | ||
|
||
const ref: MutableRefObject<HTMLDivElement | null> = useRef<HTMLDivElement>(null) | ||
|
||
useEffect(() => { | ||
if (ref && ref.current) { | ||
startCollector(setStoredMouseEvents, setStoredTouchEvents, setStoredKeyboardEvents, ref.current) | ||
} | ||
}, []) | ||
|
||
useEffect(() => { | ||
const userEvents = { | ||
mouseEvents, | ||
touchEvents, | ||
keyboardEvents, | ||
} | ||
|
||
onProcessData(userEvents) | ||
}, [sendData]) | ||
|
||
return <div ref={ref}></div> | ||
} | ||
|
||
export default Collector |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { ProsopoKeyboardEvent, ProsopoMouseEvent, ProsopoTouchEvent } from '@prosopo/types' | ||
|
||
const COLLECTOR_LIMIT = 1000 | ||
|
||
type SetStateAction<T> = T | ((prevState: T) => T) | ||
type SetStateEvent<T> = (setValueFunc: SetStateAction<T[]>) => void | ||
type SetMouseEvent = (setValueFunc: SetStateAction<ProsopoMouseEvent[]>) => void | ||
type SetKeyboardEvent = (setValueFunc: SetStateAction<ProsopoKeyboardEvent[]>) => void | ||
type SetTouchEvent = (setValueFunc: SetStateAction<ProsopoTouchEvent[]>) => void | ||
|
||
const storeLog = <T>(event: T, setEvents: SetStateEvent<T>) => { | ||
setEvents((currentEvents) => { | ||
let newEvents = [...currentEvents, event] | ||
if (newEvents.length > COLLECTOR_LIMIT) { | ||
newEvents = newEvents.slice(1) | ||
} | ||
return newEvents | ||
}) | ||
} | ||
|
||
const logMouseEvent = (event: globalThis.MouseEvent, setMouseEvent: SetMouseEvent) => { | ||
const storedEvent: ProsopoMouseEvent = { | ||
x: event.x, | ||
y: event.y, | ||
timestamp: event.timeStamp, | ||
} | ||
storeLog(storedEvent, setMouseEvent) | ||
} | ||
|
||
const logKeyboardEvent = (event: globalThis.KeyboardEvent, setKeyboardEvent: SetKeyboardEvent) => { | ||
const storedEvent: ProsopoKeyboardEvent = { | ||
key: event.key, | ||
timestamp: event.timeStamp, | ||
isShiftKey: event.shiftKey, | ||
isCtrlKey: event.ctrlKey, | ||
} | ||
storeLog(storedEvent, setKeyboardEvent) | ||
} | ||
|
||
const logTouchEvent = (event: globalThis.TouchEvent, setTouchEvent: SetTouchEvent) => { | ||
// Iterate over the TouchList (map doesn't work on TouchList) | ||
for (let i = 0; i < event.touches.length; i++) { | ||
const touch = event.touches[i] | ||
if (!touch) { | ||
continue | ||
} | ||
storeLog({ x: touch.clientX, y: touch.clientY, timestamp: event.timeStamp }, setTouchEvent) | ||
} | ||
} | ||
|
||
export const startCollector = ( | ||
setStoredMouseEvents: SetMouseEvent, | ||
setStoredTouchEvents: SetTouchEvent, | ||
setStoredKeyboardEvents: SetKeyboardEvent, | ||
rootElement: HTMLDivElement | ||
) => { | ||
const form = findContainingForm(rootElement) | ||
if (form) { | ||
// Add listeners to mouse | ||
form.addEventListener('mousemove', (e) => logMouseEvent(e, setStoredMouseEvents)) | ||
|
||
// Add listeners to keyboard | ||
form.addEventListener('keydown', (e) => logKeyboardEvent(e, setStoredKeyboardEvents)) | ||
form.addEventListener('keyup', (e) => logKeyboardEvent(e, setStoredKeyboardEvents)) | ||
|
||
// Add listeners to touch | ||
form.addEventListener('touchstart', (e) => logTouchEvent(e, setStoredTouchEvents)) | ||
form.addEventListener('touchend', (e) => logTouchEvent(e, setStoredTouchEvents)) | ||
form.addEventListener('touchcancel', (e) => logTouchEvent(e, setStoredTouchEvents)) | ||
form.addEventListener('touchmove', (e) => logTouchEvent(e, setStoredTouchEvents)) | ||
} | ||
} | ||
|
||
const findContainingForm = (element: Element): HTMLFormElement | null => { | ||
if (element.tagName === 'FORM') { | ||
return element as HTMLFormElement | ||
} | ||
if (element.parentElement) { | ||
return findContainingForm(element.parentElement) | ||
} | ||
return null | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.