Sep 162010
 

Quite some time ago, FreshPorts and The FreeBSD Diary both moved from clear text passwords to password hashes. As such, you are no longer able to recover your password. You had to email me. This is extra work for everyone. Let us automate this.

The first, step, is a table:

create table user_password_reset(
  user_id        int not null,
  date_requested timestamp with time zone default ('now'::text)::timestamp(6) with time zone,
  ip_address     inet not null,
  token          text not null
);

alter table user_password_reset
    add foreign key  (user_id)
       references users (id) on update cascade on delete cascade;

With this, I can record:

  • the user requesting the password change
  • the date/time it was requested
  • the IP address from which the request came
  • the token sent to the user which will permit them to change the password

Next, we need a function to populate the token field, I could do this with a default value on the column, but I think this solution better. There is more flexibility with a trigger.

CREATE OR REPLACE FUNCTION user_password_reset_token() RETURNS TRIGGER AS $$
  BEGIN
    NEW.token := ENCODE(DIGEST((random() * NEW.user_id )::text || now(), 'sha256'), 'hex');
    RETURN NEW;
  END
$$ LANGUAGE 'plpgsql';


  DROP TRIGGER user_password_reset_token ON user_password_reset;
CREATE TRIGGER user_password_reset_token
    BEFORE INSERT on user_password_reset
    FOR EACH ROW
    EXECUTE PROCEDURE user_password_reset_token();

The token is created from a random value, multiplied by the user id and appended with the current time. The resulting string is then hashed using sha256 and encoded into hex and stored in the column. At any time, I can change the token scheme by adjusting that function.

An example of what this produces:

$ psql freshports.org
psql (8.4.3)
Type "help" for help.

freshports.org=# select * from user_password_reset ;
 user_id | date_requested | ip_address | token
---------+----------------+------------+-------
(0 rows)

freshports.org=# insert into user_password_reset (user_id, ip_address) values (1, '127.0.0.1') returning token;
                              token
------------------------------------------------------------------
 a781b65b027e447e67b37f45ea6f150fade0b75f89652577953bbdc1784e8e50
(1 row)

INSERT 0 1
freshports.org=# select * from user_password_reset ;
 user_id |        date_requested        | ip_address |                              token
---------+------------------------------+------------+------------------------------------------------------------------
       1 | 2010-09-16 19:51:18.37022+01 | 127.0.0.1  | a781b65b027e447e67b37f45ea6f150fade0b75f89652577953bbdc1784e8e50
(1 row)

freshports.org=# insert into user_password_reset (user_id, ip_address) values (1, '127.0.0.1') returning token;
                              token
------------------------------------------------------------------
 1df39361bd1ac458da7c23a3de64fe0277cb247846dc2a654f5b0123d9069188
(1 row)

INSERT 0 1
freshports.org=# select * from user_password_reset ;
 user_id |        date_requested         | ip_address |                              token
---------+-------------------------------+------------+------------------------------------------------------------------
       1 | 2010-09-16 19:51:18.37022+01  | 127.0.0.1  | a781b65b027e447e67b37f45ea6f150fade0b75f89652577953bbdc1784e8e50
       1 | 2010-09-16 19:51:24.505328+01 | 127.0.0.1  | 1df39361bd1ac458da7c23a3de64fe0277cb247846dc2a654f5b0123d9069188
(2 rows)

freshports.org=#
Website Pin Facebook Twitter Myspace Friendfeed Technorati del.icio.us Digg Google StumbleUpon Premium Responsive

  One Response to “Sending out a URL for password reset”

  1. This is now in production.