User login and refresh on auth_session (phase 2). Refs EventHub/EventHubBack#26
This commit is contained in:
@@ -1,62 +1,95 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @doc Бизнес-логика единой session-модели (refresh JWT + rotation).
|
||||
%%% Фаза 1: admin API. Фазы 2–3: user / client web / mobile.
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(logic_auth_session).
|
||||
-include("records.hrl").
|
||||
-export([issue_admin_tokens/2, refresh_admin/1]).
|
||||
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @doc Выдать пару access + refresh JWT при входе администратора.
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-spec issue_admin_tokens(AdminId :: binary(), Role :: binary()) ->
|
||||
{ok, AccessToken :: binary(), RefreshToken :: binary()}.
|
||||
issue_admin_tokens(AdminId, Role) ->
|
||||
{ok, Session} = core_auth_session:create(AdminId, admin, <<"admin">>),
|
||||
AccessToken = eventhub_auth:generate_admin_token(AdminId, Role),
|
||||
{RefreshToken, _} = eventhub_auth:generate_admin_refresh_token(
|
||||
Session#auth_session.session_id,
|
||||
Session#auth_session.family_id,
|
||||
Session#auth_session.current_jti,
|
||||
AdminId,
|
||||
<<"admin">>
|
||||
),
|
||||
{ok, AccessToken, RefreshToken}.
|
||||
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @doc Обновить пару токенов администратора по refresh JWT.
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-spec refresh_admin(RefreshToken :: binary()) ->
|
||||
{ok, AccessToken :: binary(), NewRefreshToken :: binary()} |
|
||||
{error, atom()}.
|
||||
refresh_admin(RefreshToken) ->
|
||||
case eventhub_auth:verify_admin_refresh_token(RefreshToken) of
|
||||
{ok, Claims} ->
|
||||
do_refresh_admin(Claims);
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
do_refresh_admin(Claims) ->
|
||||
SessionId = maps:get(<<"sid">>, Claims),
|
||||
Jti = maps:get(<<"jti">>, Claims),
|
||||
AdminId = maps:get(<<"sub">>, Claims),
|
||||
FamilyId = maps:get(<<"fid">>, Claims),
|
||||
case core_auth_session:rotate(SessionId, Jti) of
|
||||
{ok, NewJti, _Session} ->
|
||||
{ok, Admin} = core_admin:get_by_id(AdminId),
|
||||
Role = atom_to_binary(Admin#admin.role, utf8),
|
||||
AccessToken = eventhub_auth:generate_admin_token(AdminId, Role),
|
||||
{NewRefresh, _} = eventhub_auth:generate_admin_refresh_token(
|
||||
SessionId, FamilyId, NewJti, AdminId, <<"admin">>
|
||||
),
|
||||
{ok, AccessToken, NewRefresh};
|
||||
{error, reuse_detected} ->
|
||||
core_auth_session:revoke_family(FamilyId),
|
||||
{error, reuse_detected};
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @doc Бизнес-логика единой session-модели (refresh JWT + rotation).
|
||||
%%% Фаза 1: admin API. Фаза 2: user API. Фаза 3: client web + mobile.
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(logic_auth_session).
|
||||
-include("records.hrl").
|
||||
-export([issue_admin_tokens/2, refresh_admin/1,
|
||||
issue_user_tokens/2, refresh_user/1]).
|
||||
|
||||
-define(USER_CLIENT_WEB, <<"web">>).
|
||||
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% Admin (фаза 1)
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-spec issue_admin_tokens(AdminId :: binary(), Role :: binary()) ->
|
||||
{ok, AccessToken :: binary(), RefreshToken :: binary()}.
|
||||
issue_admin_tokens(AdminId, Role) ->
|
||||
issue_tokens(AdminId, admin, <<"admin">>, Role, fun eventhub_auth:generate_admin_token/2,
|
||||
fun eventhub_auth:generate_admin_refresh_token/5).
|
||||
|
||||
-spec refresh_admin(RefreshToken :: binary()) ->
|
||||
{ok, AccessToken :: binary(), NewRefreshToken :: binary()} |
|
||||
{error, atom()}.
|
||||
refresh_admin(RefreshToken) ->
|
||||
refresh_subject(RefreshToken, fun eventhub_auth:verify_admin_refresh_token/1,
|
||||
fun core_admin:get_by_id/1, admin, fun eventhub_auth:generate_admin_token/2,
|
||||
fun eventhub_auth:generate_admin_refresh_token/5, <<"admin">>).
|
||||
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% User (фаза 2)
|
||||
%%%-------------------------------------------------------------------
|
||||
|
||||
-spec issue_user_tokens(UserId :: binary(), Role :: binary()) ->
|
||||
{ok, AccessToken :: binary(), RefreshToken :: binary()}.
|
||||
issue_user_tokens(UserId, Role) ->
|
||||
issue_tokens(UserId, user, ?USER_CLIENT_WEB, Role, fun eventhub_auth:generate_user_token/2,
|
||||
fun eventhub_auth:generate_user_refresh_token/5).
|
||||
|
||||
-spec refresh_user(RefreshToken :: binary()) ->
|
||||
{ok, AccessToken :: binary(), NewRefreshToken :: binary()} |
|
||||
{error, atom()}.
|
||||
refresh_user(RefreshToken) ->
|
||||
refresh_subject(RefreshToken, fun eventhub_auth:verify_user_refresh_token/1,
|
||||
fun core_user:get_by_id/1, user, fun eventhub_auth:generate_user_token/2,
|
||||
fun eventhub_auth:generate_user_refresh_token/5, ?USER_CLIENT_WEB).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Internal
|
||||
%%%===================================================================
|
||||
|
||||
issue_tokens(SubjectId, SubjectType, ClientType, Role, GenAccess, GenRefresh) ->
|
||||
{ok, Session} = core_auth_session:create(SubjectId, SubjectType, ClientType),
|
||||
AccessToken = GenAccess(SubjectId, Role),
|
||||
{RefreshToken, _} = GenRefresh(
|
||||
Session#auth_session.session_id,
|
||||
Session#auth_session.family_id,
|
||||
Session#auth_session.current_jti,
|
||||
SubjectId,
|
||||
ClientType
|
||||
),
|
||||
{ok, AccessToken, RefreshToken}.
|
||||
|
||||
refresh_subject(RefreshToken, VerifyFn, GetSubjectFn, SubjectType, GenAccess, GenRefresh, ClientType) ->
|
||||
case VerifyFn(RefreshToken) of
|
||||
{ok, Claims} ->
|
||||
do_refresh(Claims, GetSubjectFn, SubjectType, GenAccess, GenRefresh, ClientType);
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
do_refresh(Claims, GetSubjectFn, SubjectType, GenAccess, GenRefresh, ClientType) ->
|
||||
SessionId = maps:get(<<"sid">>, Claims),
|
||||
Jti = maps:get(<<"jti">>, Claims),
|
||||
SubjectId = maps:get(<<"sub">>, Claims),
|
||||
FamilyId = maps:get(<<"fid">>, Claims),
|
||||
case core_auth_session:rotate(SessionId, Jti) of
|
||||
{ok, NewJti, _Session} ->
|
||||
{ok, Subject} = GetSubjectFn(SubjectId),
|
||||
Role = subject_role(Subject, SubjectType),
|
||||
AccessToken = GenAccess(SubjectId, Role),
|
||||
{NewRefresh, _} = GenRefresh(SessionId, FamilyId, NewJti, SubjectId, ClientType),
|
||||
{ok, AccessToken, NewRefresh};
|
||||
{error, reuse_detected} ->
|
||||
core_auth_session:revoke_family(FamilyId),
|
||||
{error, reuse_detected};
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
subject_role(#admin{role = Role}, admin) ->
|
||||
atom_to_binary(Role, utf8);
|
||||
subject_role(#user{role = Role}, user) ->
|
||||
atom_to_binary(Role, utf8).
|
||||
|
||||
Reference in New Issue
Block a user