-module(jchat_http_auth). -export([init/2]). -include("jchat.hrl"). init(Req0, State) -> Method = cowboy_req:method(Req0), Path = cowboy_req:path(Req0), handle_request(Method, Path, Req0, State). %% Handle registration endpoint handle_request(<<"POST">>, <<"/auth/register">>, Req0, State) -> case cowboy_req:read_body(Req0) of {ok, Body, Req1} -> process_registration(Body, Req1, State); {more, _Data, Req1} -> reply_error(413, <<"requestTooLarge">>, <<"Request body too large">>, Req1, State) end; %% Handle login endpoint handle_request(<<"POST">>, <<"/auth/login">>, Req0, State) -> case cowboy_req:read_body(Req0) of {ok, Body, Req1} -> process_login(Body, Req1, State); {more, _Data, Req1} -> reply_error(413, <<"requestTooLarge">>, <<"Request body too large">>, Req1, State) end; %% Handle logout endpoint handle_request(<<"POST">>, <<"/auth/logout">>, Req0, State) -> % For JWT-based auth, logout is mainly client-side % But we can implement token blacklisting here in the future Req1 = cowboy_req:reply(200, #{ <<"content-type">> => <<"application/json; charset=utf-8">>, <<"access-control-allow-origin">> => <<"*">> }, jchat_utils:json_encode(#{<<"success">> => true}), Req0), {ok, Req1, State}; %% Handle token validation endpoint handle_request(<<"GET">>, <<"/auth/me">>, Req0, State) -> case jchat_auth:authenticate_request(Req0) of {ok, AuthContext} -> User = maps:get(user, AuthContext), UserJson = #{ <<"id">> => User#user.id, <<"email">> => User#user.email, <<"displayName">> => User#user.display_name, <<"createdAt">> => User#user.created_at, <<"lastLoginAt">> => User#user.last_login_at, <<"isActive">> => User#user.is_active }, Req1 = cowboy_req:reply(200, #{ <<"content-type">> => <<"application/json; charset=utf-8">>, <<"access-control-allow-origin">> => <<"*">> }, jchat_utils:json_encode(#{<<"user">> => UserJson}), Req0), {ok, Req1, State}; {error, Error} -> Status = maps:get(status, Error, 401), Type = maps:get(type, Error, <<"unauthorized">>), Detail = maps:get(detail, Error, <<"Authentication required">>), reply_error(Status, Type, Detail, Req0, State) end; %% Handle OPTIONS requests for CORS handle_request(<<"OPTIONS">>, _Path, Req0, State) -> Req1 = cowboy_req:reply(200, #{ <<"access-control-allow-origin">> => <<"*">>, <<"access-control-allow-methods">> => <<"POST, GET, OPTIONS">>, <<"access-control-allow-headers">> => <<"content-type, authorization">>, <<"access-control-max-age">> => <<"86400">> }, <<>>, Req0), {ok, Req1, State}; %% Handle unsupported methods/paths handle_request(_Method, _Path, Req0, State) -> reply_error(404, <<"notFound">>, <<"Endpoint not found">>, Req0, State). %% Process user registration process_registration(Body, Req0, State) -> case jchat_utils:json_decode(Body) of {ok, Data} -> Email = maps:get(<<"email">>, Data, undefined), Password = maps:get(<<"password">>, Data, undefined), DisplayName = maps:get(<<"displayName">>, Data, undefined), case {Email, Password, DisplayName} of {undefined, _, _} -> reply_error(400, <<"invalidArguments">>, <<"Email is required">>, Req0, State); {_, undefined, _} -> reply_error(400, <<"invalidArguments">>, <<"Password is required">>, Req0, State); {_, _, undefined} -> reply_error(400, <<"invalidArguments">>, <<"Display name is required">>, Req0, State); {_, _, _} -> case jchat_auth:register_user(Email, Password, DisplayName) of {ok, Result} -> Req1 = cowboy_req:reply(201, #{ <<"content-type">> => <<"application/json; charset=utf-8">>, <<"access-control-allow-origin">> => <<"*">> }, jchat_utils:json_encode(Result), Req0), {ok, Req1, State}; {error, Error} -> Status = maps:get(status, Error, 400), Type = maps:get(type, Error, <<"registrationFailed">>), Detail = maps:get(detail, Error, <<"Registration failed">>), reply_error(Status, Type, Detail, Req0, State) end end; {error, invalid_json} -> reply_error(400, <<"invalidJSON">>, <<"Request body must be valid JSON">>, Req0, State) end. %% Process user login process_login(Body, Req0, State) -> case jchat_utils:json_decode(Body) of {ok, Data} -> Email = maps:get(<<"email">>, Data, undefined), Password = maps:get(<<"password">>, Data, undefined), case {Email, Password} of {undefined, _} -> reply_error(400, <<"invalidArguments">>, <<"Email is required">>, Req0, State); {_, undefined} -> reply_error(400, <<"invalidArguments">>, <<"Password is required">>, Req0, State); {_, _} -> case jchat_auth:login_user(Email, Password) of {ok, Result} -> Req1 = cowboy_req:reply(200, #{ <<"content-type">> => <<"application/json; charset=utf-8">>, <<"access-control-allow-origin">> => <<"*">> }, jchat_utils:json_encode(Result), Req0), {ok, Req1, State}; {error, Error} -> Status = maps:get(status, Error, 401), Type = maps:get(type, Error, <<"loginFailed">>), Detail = maps:get(detail, Error, <<"Login failed">>), reply_error(Status, Type, Detail, Req0, State) end end; {error, invalid_json} -> reply_error(400, <<"invalidJSON">>, <<"Request body must be valid JSON">>, Req0, State) end. %% Helper function to send error responses reply_error(Status, Type, Detail, Req0, State) -> ErrorResponse = #{ <<"type">> => Type, <<"detail">> => Detail, <<"status">> => Status }, Req1 = cowboy_req:reply(Status, #{ <<"content-type">> => <<"application/json; charset=utf-8">>, <<"access-control-allow-origin">> => <<"*">> }, jchat_utils:json_encode(ErrorResponse), Req0), {ok, Req1, State}.