I've been trying to create a basic user authentication system within postgreSQL 9.4, but have been coming unstuck. My users table looks like this:
-- Users table
CREATE TABLE users (
user_id SERIAL NOT NULL PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT NOT NULL,
password TEXT NOT NULL,
failed_login_attempts INT NOT NULL DEFAULT 0
CONSTRAINT positive_login_attempts CHECK (failed_login_attempts >= 0),
last_failed_login_attempt TIMESTAMP NULL,
UNIQUE(email),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL,
deleted_at TIMESTAMP NULL
);
These functions work fine:
-- check to see if a user exists
CREATE OR REPLACE FUNCTION user_exists (auth_email VARCHAR(254))
RETURNS SETOF users AS $$
SELECT *
FROM users
WHERE email = auth_email
$$ LANGUAGE SQL;
-- authenticates a user against the system
CREATE OR REPLACE FUNCTION authenticate_user (auth_email VARCHAR(254), auth_password VARCHAR(72))
RETURNS SETOF users AS $$
SELECT *
FROM users
WHERE email = auth_email
AND password = crypt(auth_password, password))
$$ LANGUAGE SQL;
But then, when I try and combine these, I fall flat on my face. In semi-pseudocode, what I'd like to do is this:
-- login function
CREATE OR REPLACE FUNCTION user_login (auth_email VARCHAR(254), auth_password VARCHAR(72)) RETURNS SETOF users AS $$
IF EXISTS (SELECT COUNT(*) FROM user_exists(auth_email))
IF EXISTS (SELECT COUNT(*) FROM authenticate_user (auth_email, auth_password))
-- set the failed_login_attempts value to 0
-- set the last failed login attempt as NULL
-- return the user details
ELSE
-- increment the failed_login_attempts value
-- set the last failed login attempt as the current time
-- return nothing
END IF;
ELSE
-- return nothing
END IF;
$$ LANGUAGE SQL;
Is that possible? Am I going down entirely the wrong lines?
The purpose of the 'failed login attempts' would be to set it to incrementally longer cooling off periods - eg failed attempts:
- 1 second
- 2 seconds
- 4s
- 8s
- 16s
- ...