diff --git a/src/box/authentication.cc b/src/box/authentication.cc
index dfdf8aaf7b86610e0f6bf1eff6509d2e1da6149e..5d5c9b10453e8a86c6ddbeaec264a2599a76b88e 100644
--- a/src/box/authentication.cc
+++ b/src/box/authentication.cc
@@ -69,6 +69,9 @@ authenticate(const char *user_name, uint32_t len,
 	if (scramble_check(scramble, session->salt, user->hash2))
 		tnt_raise(ClientError, ER_PASSWORD_MISMATCH, user->name);
 
+	/* check and run auth triggers on success */
+	if (! rlist_empty(&session_on_auth))
+		session_run_on_auth_triggers(user->name);
 ok:
 	credentials_init(&session->credentials, user);
 }
diff --git a/src/box/lua/session.cc b/src/box/lua/session.cc
index 082c53a9a1aaffd8593069ae5ec8d82b02a72da8..1021b22c6167692e0f6f3b8a83276a4219b2ee15 100644
--- a/src/box/lua/session.cc
+++ b/src/box/lua/session.cc
@@ -211,6 +211,17 @@ lbox_session_run_trigger(struct trigger *trigger, void * /* event */)
 	lbox_call(L, 0, 0);
 }
 
+static void
+lbox_session_run_auth_trigger(struct trigger *trigger, void *event)
+{
+	lua_State *L = lua_newthread(tarantool_L);
+	LuarefGuard coro_guard(tarantool_L);
+	lua_rawgeti(L, LUA_REGISTRYINDEX, (intptr_t) trigger->data);
+	/* now only the user name comes in as an on_auth argument */
+	lua_pushstring(L, (const char *)event);
+	lbox_call(L, 1, 0);
+}
+
 static int
 lbox_session_on_connect(struct lua_State *L)
 {
@@ -225,6 +236,13 @@ lbox_session_on_disconnect(struct lua_State *L)
 				  lbox_session_run_trigger);
 }
 
+static int
+lbox_session_on_auth(struct lua_State *L)
+{
+	return lbox_trigger_reset(L, 2, &session_on_auth,
+				  lbox_session_run_auth_trigger);
+}
+
 void
 session_storage_cleanup(int sid)
 {
@@ -270,6 +288,7 @@ box_lua_session_init(struct lua_State *L)
 		{"peer", lbox_session_peer},
 		{"on_connect", lbox_session_on_connect},
 		{"on_disconnect", lbox_session_on_disconnect},
+		{"on_auth", lbox_session_on_auth},
 		{NULL, NULL}
 	};
 	luaL_register_module(L, sessionlib_name, sessionlib);
diff --git a/src/box/session.cc b/src/box/session.cc
index 607f0d759780060084a1d6330bd9b4de4310f7fd..8f18cca19482b42a452945463b59e2e92d07bba1 100644
--- a/src/box/session.cc
+++ b/src/box/session.cc
@@ -42,6 +42,7 @@ struct mempool session_pool;
 
 RLIST_HEAD(session_on_connect);
 RLIST_HEAD(session_on_disconnect);
+RLIST_HEAD(session_on_auth);
 
 static inline  uint32_t
 sid_max()
@@ -137,6 +138,7 @@ session_run_on_disconnect_triggers(struct session *session)
 void
 session_run_on_connect_triggers(struct session *session)
 {
+	/* Run on_connect with admin credentals */
 	struct fiber *fiber = fiber();
 	fiber_set_session(fiber, session);
 	fiber_set_user(fiber, &admin_credentials);
@@ -144,6 +146,12 @@ session_run_on_connect_triggers(struct session *session)
 	/* Set session user to guest, until it is authenticated. */
 }
 
+void
+session_run_on_auth_triggers(const char *user_name)
+{
+	trigger_run(&session_on_auth, (void *)user_name);
+}
+
 void
 session_destroy(struct session *session)
 {
diff --git a/src/box/session.h b/src/box/session.h
index 09a9ea34a52ba27c76a1a2d44497830a192b59c2..b78f566b7a78f605712b11df82dc8a4b574befee 100644
--- a/src/box/session.h
+++ b/src/box/session.h
@@ -120,6 +120,11 @@ extern struct rlist session_on_disconnect;
 void
 session_run_on_disconnect_triggers(struct session *session);
 
+extern struct rlist session_on_auth;
+
+void
+session_run_on_auth_triggers(const char *user_name);
+
 void
 session_init();
 
diff --git a/test/box/auth.result b/test/box/auth.result
new file mode 100644
index 0000000000000000000000000000000000000000..478ef191fdd5c51418c4f3baccb17af7638e0945
--- /dev/null
+++ b/test/box/auth.result
@@ -0,0 +1,133 @@
+session = box.session
+---
+...
+fiber = require('fiber')
+---
+...
+space = box.schema.space.create('tweedledum')
+---
+...
+index = space:create_index('primary', { type = 'hash' })
+---
+...
+box.schema.user.create('test', {password='pass'})
+---
+...
+box.schema.user.grant('test', 'read,write,execute', 'universe')
+---
+...
+box.schema.user.grant('guest', 'read,write,execute', 'universe')
+---
+...
+-- check how authentication trigger work
+-- no args trigger
+function auth_trigger() counter = counter + 1 end
+---
+...
+-- get user name as argument
+function auth_trigger2(user_name) msg = 'user ' .. user_name .. ' is there' end
+---
+...
+net = { box = require('net.box') }
+---
+...
+-- set trigger
+handle = session.on_auth(auth_trigger)
+---
+...
+-- check handle
+type(handle)
+---
+- function
+...
+-- check triggers list
+#session.on_auth()
+---
+- 1
+...
+handle2 = session.on_auth(auth_trigger2)
+---
+...
+counter = 0
+---
+...
+msg = ''
+---
+...
+LISTEN = require('uri').parse(box.cfg.listen)
+---
+...
+-- check connection with authentication(counter must be incremented)
+c = net.box:new('test:pass@' .. LISTEN.host .. ':' .. LISTEN.service)
+---
+...
+while counter < 1 do fiber.sleep(0.001) end
+---
+...
+counter
+---
+- 1
+...
+msg
+---
+- user test is there
+...
+-- check guest connection without authentication(no increment)
+c1 = net.box:new(LISTEN.host, LISTEN.service)
+---
+...
+c1:ping()
+---
+- true
+...
+counter
+---
+- 1
+...
+-- cleanup
+c:close()
+---
+...
+c1:close()
+---
+...
+session.on_auth(nil, auth_trigger)
+---
+...
+session.on_auth(nil, auth_trigger2)
+---
+...
+session.on_auth()
+---
+- []
+...
+space:drop()
+---
+...
+session.uid()
+---
+- 1
+...
+session.user()
+---
+- admin
+...
+session.sync()
+---
+- 0
+...
+session = nil
+---
+...
+fiber = nil
+---
+...
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
+---
+...
+box.schema.user.revoke('test', 'read,write,execute', 'universe')
+---
+...
+box.schema.user.drop('test', { if_exists = true})
+---
+...
diff --git a/test/box/auth.test.lua b/test/box/auth.test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..a8d65465dfd656aa136e5e6457399b146075f992
--- /dev/null
+++ b/test/box/auth.test.lua
@@ -0,0 +1,54 @@
+session = box.session
+fiber = require('fiber')
+
+space = box.schema.space.create('tweedledum')
+index = space:create_index('primary', { type = 'hash' })
+box.schema.user.create('test', {password='pass'})
+box.schema.user.grant('test', 'read,write,execute', 'universe')
+box.schema.user.grant('guest', 'read,write,execute', 'universe')
+
+-- check how authentication trigger work
+-- no args trigger
+function auth_trigger() counter = counter + 1 end
+-- get user name as argument
+function auth_trigger2(user_name) msg = 'user ' .. user_name .. ' is there' end
+net = { box = require('net.box') }
+
+-- set trigger
+handle = session.on_auth(auth_trigger)
+-- check handle
+type(handle)
+-- check triggers list
+#session.on_auth()
+handle2 = session.on_auth(auth_trigger2)
+counter = 0
+msg = ''
+
+LISTEN = require('uri').parse(box.cfg.listen)
+
+-- check connection with authentication(counter must be incremented)
+c = net.box:new('test:pass@' .. LISTEN.host .. ':' .. LISTEN.service)
+while counter < 1 do fiber.sleep(0.001) end
+counter
+msg
+
+-- check guest connection without authentication(no increment)
+c1 = net.box:new(LISTEN.host, LISTEN.service)
+c1:ping()
+counter
+
+-- cleanup
+c:close()
+c1:close()
+session.on_auth(nil, auth_trigger)
+session.on_auth(nil, auth_trigger2)
+session.on_auth()
+space:drop()
+session.uid()
+session.user()
+session.sync()
+session = nil
+fiber = nil
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
+box.schema.user.revoke('test', 'read,write,execute', 'universe')
+box.schema.user.drop('test', { if_exists = true})