%%%------------------------------------------------------------------- %%% @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).