88 lines
2.3 KiB
Erlang
88 lines
2.3 KiB
Erlang
-module(logic_auth).
|
||
|
||
-export([hash_password/1, verify_password/2]).
|
||
-export([generate_jwt/2, verify_jwt/1, extract_claims/1]).
|
||
-export([generate_refresh_token/1]).
|
||
|
||
%% ============ Argon2 хеширование ============
|
||
hash_password(Password) when is_binary(Password) ->
|
||
argon2:hash(Password).
|
||
|
||
verify_password(Password, Hash) when is_binary(Password), is_binary(Hash) ->
|
||
argon2:verify(Password, Hash).
|
||
|
||
%% ============ JWT с использованием jose ============
|
||
get_jwt_secret() ->
|
||
<<"my-super-secret-key-for-jwt-32-bytes!">>.
|
||
|
||
get_jwk() ->
|
||
jose_jwk:from_oct(get_jwt_secret()).
|
||
|
||
generate_jwt(UserId, Role) ->
|
||
JWK = get_jwk(),
|
||
|
||
ExpTime = os:system_time(seconds) + 86400, % 24 часа
|
||
Claims = #{
|
||
<<"user_id">> => UserId,
|
||
<<"role">> => Role,
|
||
<<"exp">> => ExpTime,
|
||
<<"iat">> => os:system_time(seconds)
|
||
},
|
||
|
||
JWT = jose_jwt:sign(JWK, #{<<"alg">> => <<"HS256">>}, Claims),
|
||
{_, Token} = jose_jws:compact(JWT),
|
||
Token.
|
||
|
||
verify_jwt(Token) when is_binary(Token) ->
|
||
try
|
||
JWK = get_jwk(),
|
||
case jose_jwt:verify(JWK, Token) of
|
||
{true, {jose_jwt, Claims}, _} ->
|
||
case check_expiry(Claims) of
|
||
true -> {ok, Claims};
|
||
false -> {error, expired}
|
||
end;
|
||
{true, Claims, _} when is_map(Claims) ->
|
||
case check_expiry(Claims) of
|
||
true -> {ok, Claims};
|
||
false -> {error, expired}
|
||
end;
|
||
{false, _, _} ->
|
||
{error, invalid_signature}
|
||
end
|
||
catch
|
||
_:_ -> {error, invalid_token}
|
||
end.
|
||
|
||
extract_claims(Token) when is_binary(Token) ->
|
||
try
|
||
JWK = get_jwk(),
|
||
case jose_jwt:verify(JWK, Token) of
|
||
{true, {jose_jwt, Claims}, _} ->
|
||
{ok, Claims};
|
||
{true, Claims, _} when is_map(Claims) ->
|
||
{ok, Claims};
|
||
_ ->
|
||
{error, invalid_token}
|
||
end
|
||
catch
|
||
_:_ -> {error, invalid_token}
|
||
end.
|
||
|
||
check_expiry(Claims) ->
|
||
case maps:find(<<"exp">>, Claims) of
|
||
{ok, Exp} when is_integer(Exp) ->
|
||
Exp > os:system_time(seconds);
|
||
_ ->
|
||
false
|
||
end.
|
||
|
||
%% ============ Refresh Token ============
|
||
generate_refresh_token(_UserId) ->
|
||
Token = base64:encode(crypto:strong_rand_bytes(32)),
|
||
ExpiresAt = calendar:universal_time_to_local_time(
|
||
calendar:gregorian_seconds_to_datetime(
|
||
calendar:datetime_to_gregorian_seconds(calendar:universal_time()) + 30 * 86400
|
||
)
|
||
),
|
||
{Token, ExpiresAt}. |