diff --git a/lib/srdb1/schema/subscriber.xml b/lib/srdb1/schema/subscriber.xml
index 726db61..c1f095c 100644
--- a/lib/srdb1/schema/subscriber.xml
+++ b/lib/srdb1/schema/subscriber.xml
@@ -9,7 +9,7 @@
 
 <table id="subscriber" xmlns:db="http://docbook.org/ns/docbook">
     <name>subscriber</name>
-    <version>6</version>
+    <version>7</version>
     <type db="mysql">&MYSQL_TABLE_TYPE;</type>
     <description>
         <db:para>This table is used to provide authentication information. More information about the auth_db module can be found at: &KAMAILIO_MOD_DOC;auth_db.html
@@ -85,6 +85,14 @@
         <description>The SIP Remote-Party-ID header identifies the calling party and includes user, party, screen and privacy headers that specify how a call is presented and screened.</description>
     </column>
 
+    <column>
+        <name>last_login</name>
+        <type>datetime</type>
+        <default>&DEFAULT_DATETIME;</default>
+        <default db="oracle">to_date('&DEFAULT_DATETIME;','yyyy-mm-dd hh24:mi:ss')</default>
+        <description>Date and time of last successful login</description>
+    </column>
+
     <index>
         <name>account_idx</name>
         <colref linkend="username"/>
diff --git a/modules/auth_db/authdb_mod.c b/modules/auth_db/authdb_mod.c
index 074c851..28d1b68 100644
--- a/modules/auth_db/authdb_mod.c
+++ b/modules/auth_db/authdb_mod.c
@@ -45,11 +45,16 @@
 #include "../../mem/mem.h"
 #include "../../parser/parse_uri.h"
 #include "../../modules/auth/api.h"
+#include "../usrloc/usrloc.h"
+#include "../usrloc/ul_callback.h"
 #include "authorize.h"
 
 MODULE_VERSION
 
-#define TABLE_VERSION 6
+#define TABLE_VERSION 7
+
+usrloc_api_t ul;
+void db_update_last_login(ucontact_t *c, int type, void *param);
 
 /*
  * Module destroy function prototype
@@ -87,6 +92,9 @@ int parse_aaa_pvs(char *definition, pv_elem_t **pv_def, int *cnt);
 #define PASS_COL_2 "ha1b"
 #define PASS_COL_2_LEN (sizeof(PASS_COL_2) - 1)
 
+#define LAST_LOGIN_COL "last_login"
+#define LAST_LOGIN_COL_LEN (sizeof(LAST_LOGIN_COL) -1)
+
 #define DEFAULT_CRED_LIST "rpid"
 
 /*
@@ -97,6 +105,8 @@ str user_column             = {USER_COL, USER_COL_LEN};
 str domain_column           = {DOMAIN_COL, DOMAIN_COL_LEN};
 str pass_column             = {PASS_COL, PASS_COL_LEN};
 str pass_column_2           = {PASS_COL_2, PASS_COL_2_LEN};
+str last_login_column       = {LAST_LOGIN_COL, LAST_LOGIN_COL_LEN};
+str last_login_table        = {0, 0};
 
 static int version_table_check = 1;
 
@@ -148,6 +158,8 @@ static param_export_t params[] = {
 	{"use_domain",        INT_PARAM, &use_domain          },
 	{"load_credentials",  STR_PARAM, &credentials_list    },
 	{"version_table",     INT_PARAM, &version_table_check },
+	{"last_login_column", STR_PARAM, &last_login_column.s },
+	{"last_login_table",  STR_PARAM, &last_login_table    },
 	{0, 0, 0}
 };
 
@@ -188,6 +200,7 @@ static int child_init(int rank)
 
 static int mod_init(void)
 {
+	bind_usrloc_t bind_usrloc;
 	bind_auth_s_t bind_auth;
 
 	db_url.len = strlen(db_url.s);
@@ -195,6 +208,8 @@ static int mod_init(void)
 	domain_column.len = strlen(domain_column.s);
 	pass_column.len = strlen(pass_column.s);
 	pass_column_2.len = strlen(pass_column_2.s);
+	last_login_column.len = strlen(last_login_column.s);
+	if (last_login_table.s) last_login_table.len = strlen(last_login_table.s);
 
 	/* Find a database module */
 	if (db_bind_mod(&db_url, &auth_dbf) < 0){
@@ -221,6 +236,47 @@ static int mod_init(void)
 		return -5;
 	}
 
+	if (last_login_table.len > 0)
+	{
+		db1_con_t* dbh = NULL;
+
+		if (version_table_check != 0) {
+			dbh = auth_dbf.init(&db_url);
+			if (!dbh) {
+				LM_ERR("unable to open database connection\n");
+				return -1;
+			}
+			if (db_check_table_version(&auth_dbf, dbh, &last_login_table, TABLE_VERSION) < 0) {
+				LM_ERR("error during table version check.\n");
+				auth_dbf.close(dbh);
+				return -1;
+			}
+			auth_dbf.close(dbh);
+		}
+
+		bind_usrloc = (bind_usrloc_t)find_export("ul_bind_usrloc", 1, 0);
+		if (!bind_usrloc)
+		{
+			LM_ERR("Can't bind usrloc\n");
+			return -1;
+		}
+		if (bind_usrloc(&ul) < 0)
+		{
+			LM_ERR("Can't bind usrloc\n");
+			return -1;
+		}
+		if (!ul.register_ulcb)
+		{
+			LM_ERR("Can't import register_ulcb\n");
+			return -1;
+		}
+		if (ul.register_ulcb(UL_CONTACT_INSERT, db_update_last_login, 0) < 0)
+		{
+			LM_ERR("Could not register callback for usrloc insert\n");
+			return -1;
+		}
+	}
+
 	return 0;
 }
 
@@ -447,3 +503,60 @@ error:
 	*cnt = 0;
 	return -1;
 }
+
+void db_update_last_login(ucontact_t *c, int type, void *param)
+{
+	str user, domain;
+	char *at;
+	db_key_t keys[3];
+	db_val_t vals[3];
+
+	if ((type & UL_CONTACT_INSERT) == 0)
+	{
+		LM_ERR("This function should only be called for usrloc inserts\n");
+		return;
+	}
+
+	at = memchr(c->aor->s, '@', c->aor->len);
+	if (at)
+	{
+		user.s = c->aor->s;
+		user.len = at - c->aor->s;
+		domain.s = at + 1;
+		domain.len = c->aor->len - user.len - 1;
+	}
+	else
+	{
+		LM_WARN("Cannot update logged in time for %.*s\n",
+				c->aor->len, c->aor->s);
+		return;
+	}
+
+	LM_INFO("User %.*s@%.*s logged in\n",
+			user.len, user.s,
+			domain.len, domain.s);
+
+	keys[0] = &user_column;
+	keys[1] = &domain_column;
+	keys[2] = &last_login_column;
+	VAL_TYPE(&vals[0]) = DB1_STR;
+	VAL_NULL(&vals[0]) = 0;
+	VAL_STR(&vals[0]) = user;
+	VAL_TYPE(&vals[1]) = DB1_STR;
+	VAL_NULL(&vals[1]) = 0;
+	VAL_STR(&vals[1]) = domain;
+	VAL_TYPE(&vals[2]) = DB1_DATETIME;
+	VAL_NULL(&vals[2]) = 0;
+	VAL_TIME(&vals[2]) = c->last_modified;
+
+	if (auth_dbf.use_table(auth_db_handle, &last_login_table) < 0) {
+		LM_ERR("failed to use_table\n");
+		return;
+	}
+
+	if (auth_dbf.update(auth_db_handle, keys, 0, vals, &keys[2], &vals[2], 2, 1) < 0) {
+		LM_ERR("Failed to update subscriber last_login database\n");
+		return;
+	}
+	return;
+}
