Skip to content

Commit

Permalink
Log failed login attempts to the database
Browse files Browse the repository at this point in the history
  • Loading branch information
molefrog committed Sep 25, 2022
1 parent 408ab58 commit 3548c72
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 9 deletions.
Binary file modified app/db/db.sqlite
Binary file not shown.
37 changes: 33 additions & 4 deletions app/db/queries.server.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
import { open } from "sqlite";
import sqlite3 from "sqlite3";

/*
* The `users` table DB schema
*/
// The `users` table DB schema
interface User {
id: number;
email: string;
password: string;
username: string;
}

// The `login_attempts` table DB schema
interface LoginAttempt {
id: number;
email: string;
visitor_id?: string;
created_at: number;
}

/*
* Loads user that matches the email and password provided
*/
export async function findUserByCredentials(email: string, password: string) {
export async function findUserByCredentials(
email: string,
password: string
): Promise<User | undefined> {
const db = await openDB();

return await db.get<User>(
Expand All @@ -28,6 +37,26 @@ export async function findUserByCredentials(email: string, password: string) {
);
}

/**
* Creates new `login_attempt` record in the DB
*
* @param email - email used for authentication
* @param visitorId - the unique browser/visitor identifier
*/
export async function logFailedLoginAttempt(
email: string,
visitorId: string
): Promise<void> {
const db = await openDB();

await db.run(
"INSERT INTO login_attempts (email, visitor_id, created_at) VALUES (?, ?, ?)",
email,
visitorId,
Date.now()
);
}

const openDB = async () => {
return await open({
filename: "./app/db/db.sqlite",
Expand Down
16 changes: 11 additions & 5 deletions app/routes/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
} from "@remix-run/react";

import { delay } from "../utils/delay";
import { findUserByCredentials } from "../db/queries.server";
import {
findUserByCredentials,
logFailedLoginAttempt,
} from "../db/queries.server";

// describes the reponse type of the action
interface FormResponse {
Expand All @@ -24,15 +27,18 @@ export const action: ActionFunction = async ({ request }) => {
// An artificial delay for the demonstration purposes
await delay(1000);

// parse form data provided
const data = await request.formData();
const email = data.get("email") as string;
const password = data.get("password") as string;

const user = await findUserByCredentials(
data.get("email") as string,
data.get("password") as string
);
const user = await findUserByCredentials(email, password);

// no such user found, return an error
if (!user) {
const visitorId = "fake"; // TODO: replace with the real one
await logFailedLoginAttempt(email, visitorId);

return json<FormResponse>({
errorMessage: "Bad luck, please try different login or password!",
errorCode: LoginErrorCode.wrongCredentials,
Expand Down

0 comments on commit 3548c72

Please sign in to comment.