diff --git a/dudeswave_backend/Makefile b/dudeswave_backend/Makefile
new file mode 100644
index 0000000..160022d
--- /dev/null
+++ b/dudeswave_backend/Makefile
@@ -0,0 +1,7 @@
+.PHONY: all clean
+
+all:
+ ${MAKE} -C src
+
+clean:
+ ${MAKE} -C src clean
diff --git a/dudeswave_backend/ebin/dudeswave_backend.app b/dudeswave_backend/ebin/dudeswave_backend.app
new file mode 100644
index 0000000..8e59887
--- /dev/null
+++ b/dudeswave_backend/ebin/dudeswave_backend.app
@@ -0,0 +1,10 @@
+{application,dudeswave_backend,
+ [{description,"The dudeswave core"},
+ {vsn,"1.0.0"},
+ {modules,[dudeswave_backend,dudeswave_backend_app,
+ ,dudeswave_backend_supervisor,
+ dudeswave_backend_auth,dudeswave_backend_user]},
+ {registered,[]},
+ {applications,[kernel,stdlib,erts,cowboy,ranch]},
+ {mod,{dudeswave_backend_app,[]}},
+ {start_phases,[]}]}.
diff --git a/dudeswave_backend/include/defines.hrl b/dudeswave_backend/include/defines.hrl
new file mode 100644
index 0000000..48c12ba
--- /dev/null
+++ b/dudeswave_backend/include/defines.hrl
@@ -0,0 +1,23 @@
+%
+% Copyright (c) 2024 Andrea Biscuola
+%
+% Permission to use, copy, modify, and distribute this software for any
+% purpose with or without fee is hereby granted, provided that the above
+% copyright notice and this permission notice appear in all copies.
+%
+% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%
+
+-define(APPBUCK, dudeswave).
+-define(USERSBUCK, dudes).
+-define(COOKIESBUCK, cookies).
+-define(RANDBYTES, 32).
+-define(DEFVALIDITY, 365).
+-define(DUDENAME, "dudename").
+-define(DUDEAUTH, "dudeauth").
diff --git a/dudeswave_backend/src/Makefile b/dudeswave_backend/src/Makefile
new file mode 100644
index 0000000..8dab13c
--- /dev/null
+++ b/dudeswave_backend/src/Makefile
@@ -0,0 +1,19 @@
+.PHONY: all clean
+.SUFFIXES: .erl .beam
+
+ERLC?= erlc -server
+
+ERLFLAGS= -I ../../
+
+OBJS= dudeswave_backend.beam dudeswave_backend_app.beam
+OBJS+= dudeswave_backend_supervisor.beam
+OBJS+= dudeswave_backend_auth.beam dudeswave_backend_user.beam
+
+all: ${OBJS}
+
+.erl.beam:
+ ${ERLC} ${ERLOPTS} ${ERLFLAGS} $<
+
+clean:
+ rm -f *.beam
+
diff --git a/dudeswave_backend/src/dudeswave_backend.erl b/dudeswave_backend/src/dudeswave_backend.erl
new file mode 100644
index 0000000..399df0b
--- /dev/null
+++ b/dudeswave_backend/src/dudeswave_backend.erl
@@ -0,0 +1,46 @@
+%
+% Copyright (c) 2024 Andrea Biscuola
+%
+% Permission to use, copy, modify, and distribute this software for any
+% purpose with or without fee is hereby granted, provided that the above
+% copyright notice and this permission notice appear in all copies.
+%
+% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%
+-module(dudeswave_backend).
+
+-behaviour(gen_server).
+
+% Server start function
+-export([start_link/0]).
+
+% Module callbacks
+-export([init/1, handle_call/3, handle_cast/2, terminate/2]).
+
+%
+% Startup functions
+%
+
+start_link() ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+init([]) ->
+ process_flag(trap_exit, true),
+
+ {ok, 0}.
+
+%
+% Callbacks
+%
+
+handle_call(_Msg, _From, State) -> {noreply, State}.
+
+handle_cast(_Msg, State) -> {noreply, State}.
+
+terminate(_Reason, _N) -> ok.
diff --git a/dudeswave_backend/src/dudeswave_backend_app.erl b/dudeswave_backend/src/dudeswave_backend_app.erl
new file mode 100644
index 0000000..236581b
--- /dev/null
+++ b/dudeswave_backend/src/dudeswave_backend_app.erl
@@ -0,0 +1,31 @@
+%
+% Copyright (c) 2024 Andrea Biscuola
+%
+% Permission to use, copy, modify, and distribute this software for any
+% purpose with or without fee is hereby granted, provided that the above
+% copyright notice and this permission notice appear in all copies.
+%
+% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%
+-module(dudeswave_backend_app).
+-behaviour(application).
+
+-export([bootstrap/3, start/2, stop/1]).
+
+start(_Type, StartArgs) ->
+ crypto:rand_seed(),
+
+ dudeswave_backend_supervisor:start_link(StartArgs).
+
+stop(_State) -> ok.
+
+%
+% Bootstrap procedure to be completed
+%
+bootstrap(_Admin, _Password, _Nodes) -> ok.
diff --git a/dudeswave_backend/src/dudeswave_backend_auth.erl b/dudeswave_backend/src/dudeswave_backend_auth.erl
new file mode 100644
index 0000000..4164fec
--- /dev/null
+++ b/dudeswave_backend/src/dudeswave_backend_auth.erl
@@ -0,0 +1,131 @@
+%
+% Copyright (c) 2024 Andrea Biscuola
+%
+% Permission to use, copy, modify, and distribute this software for any
+% purpose with or without fee is hereby granted, provided that the above
+% copyright notice and this permission notice appear in all copies.
+%
+% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%
+-module(dudeswave_backend_auth).
+-moduledoc """
+Dudes authentication module
+
+Here lives all the functions for the APIs needed to handle users authentication.
+""".
+
+-include_lib("dudeswave/include/defines.hrl").
+-include_lib("storage/include/storage.hrl").
+
+-export([authenticate/2, logout/2]).
+
+-doc """
+Verify a session with an existing cookie.
+
+Spec:
+
+```
+-spec authenticate(User, Auth) -> true | false | {true, Cookie, Validity} | {error, Reason} when
+ User :: binary(),
+ Auth :: {cookie, binary()} | {password, binary()},
+ Reason :: term().
+```
+
+Authenticate a user with either an existing `Cookie` or by issuing a new one
+after authenticating with `Password`.
+
+If `Cookie` is valid, the function returns `true`. If the authentication is denied
+returns `false`
+""".
+-spec authenticate(User, Auth) -> true | false | {true, Cookie, Validity} | {error, Reason} when
+ User :: binary(),
+ Auth :: {cookie, binary()} | {password, binary()},
+ Cookie :: binary(),
+ Validity :: pos_integer(),
+ Reason :: term().
+
+authenticate(User, {cookie, Cookie}) ->
+ case storage:read(?COOKIESBUCK, Cookie) of
+ {ok, [R]} ->
+ CurTime = calendar:now_to_universal_time(erlang:timestamp()),
+ CookieTime = R#object.value,
+ {user, CookieUser} = proplists:lookup(user, R#object.metadata),
+
+ if
+ CookieTime >= CurTime ->
+ if
+ User =:= CookieUser -> true;
+ true -> false
+ end;
+ true -> false
+ end;
+ {ok, []} -> false;
+ {error, _} -> {error, service_unavailable}
+ end;
+
+authenticate(User, {password, Password}) ->
+ case storage:read(?USERSBUCK, User) of
+ {ok, [R]} ->
+ Validity = case application:get_env(cookie_validity) of
+ {ok, Value} ->
+ erlang:system_time(seconds) + Value * 86400;
+ undefined ->
+ erlang:system_time(seconds) + ?DEFVALIDITY * 86400
+ end,
+
+ {hash, Hash} = proplists:lookup(hash, R#object.metadata),
+ {salt, Salt} = proplists:lookup(salt, R#object.metadata),
+ {approved, Appr} = proplists:lookup(approved, R#object.metadata),
+
+ Auth = crypto:hash(sha256, <>),
+
+ if
+ Appr =/= true -> false;
+ Auth =:= Hash ->
+ Cookie = base64:encode(rand:bytes(64)),
+ case storage:write(?COOKIESBUCK, <>,
+ Validity, [{user, User}]) of
+ ok -> {true, Cookie, Validity};
+ {error, Reason} -> {error, Reason}
+ end;
+ true -> false
+ end;
+ {ok, []} -> false;
+ {error, Reason} -> {error, Reason}
+ end.
+
+-doc """
+Close an existing session
+
+Spec:
+
+```
+-spec logout(User, Cookie) -> ok | {error, Reason} when
+ User :: binary(),
+ Cookie :: binary(),
+ Reason :: term().
+```
+
+Invalidate and delete `Cookie` associated with `User` from the system.
+""".
+-spec logout(User, Cookie) -> ok | {error, Reason} when
+ User :: binary(),
+ Cookie :: binary(),
+ Reason :: term().
+
+logout(User, Cookie) ->
+ case storage:read(?COOKIESBUCK, Cookie) of
+ {ok, [R]} ->
+ {user, User} = proplists:lookup(user, R#object.metadata),
+ storage:delete(?COOKIESBUCK, Cookie);
+ {ok, []} ->
+ {error, not_found};
+ {error, Reason} ->
+ {error, Reason}
+ end.
diff --git a/dudeswave_backend/src/dudeswave_backend_supervisor.erl b/dudeswave_backend/src/dudeswave_backend_supervisor.erl
new file mode 100644
index 0000000..3229779
--- /dev/null
+++ b/dudeswave_backend/src/dudeswave_backend_supervisor.erl
@@ -0,0 +1,31 @@
+%
+% Copyright (c) 2024 Andrea Biscuola
+%
+% Permission to use, copy, modify, and distribute this software for any
+% purpose with or without fee is hereby granted, provided that the above
+% copyright notice and this permission notice appear in all copies.
+%
+% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%
+-module(dudeswave_backend_supervisor).
+-behaviour(supervisor).
+
+-export([start/0,
+ start_link/1,
+ init/1]).
+
+start() ->
+ spawn(fun() -> supervisor:start_link({local, ?MODULE}, ?MODULE, _Arg = []) end).
+
+start_link(Args) ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, Args).
+
+init([]) ->
+ {ok, {{one_for_one, 3, 10}, [{tag1, {dudeswave_backend, start_link, []}, permanent,
+ 10000, worker, [dudeswave_backend]}]}}.
\ No newline at end of file
diff --git a/dudeswave_backend/src/dudeswave_backend_user.erl b/dudeswave_backend/src/dudeswave_backend_user.erl
new file mode 100644
index 0000000..699f642
--- /dev/null
+++ b/dudeswave_backend/src/dudeswave_backend_user.erl
@@ -0,0 +1,134 @@
+%
+% Copyright (c) 2024 Andrea Biscuola
+%
+% Permission to use, copy, modify, and distribute this software for any
+% purpose with or without fee is hereby granted, provided that the above
+% copyright notice and this permission notice appear in all copies.
+%
+% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+%
+-module(dudeswave_backend_user).
+
+-include_lib("dudeswave/include/defines.hrl").
+-include_lib("storage/include/storage.hrl").
+
+-export([details/1, new/3, update/4, delete/1]).
+
+-doc """
+Return user details.
+
+Spec:
+
+```
+-spec details(User) -> Value | {error, Reason} when
+ User :: binary(),
+ Value :: term(),
+ Reason :: term().
+```
+""".
+-spec details(User) -> Value | {error, Reason} when
+ User :: binary(),
+ Value :: term(),
+ Reason :: term().
+
+details(User) ->
+ case storage:read(?USERSBUCK, User) of
+ {ok, [R]} -> R#object.value;
+ {error, Reason} -> {error, Reason};
+ {ok, []} -> {error, not_found}
+ end.
+
+-doc """
+Create a new user.
+
+Spec:
+
+```
+-spec new(User, Password, Email) -> ok | {error, Reason} when
+ User :: binary(),
+ Password :: binary(),
+ Email :: binary(),
+ Reason :: term().
+```
+
+The `User` is created, and stored in the application's users bucket
+`Password` is salted and hashed with SHA256 before being stored.
+
+The new user is saved with a metadata `approved` of `false`,
+""".
+-spec new(User, Password, Email) -> ok | {error, Reason} when
+ User :: binary(),
+ Password :: binary(),
+ Email :: binary(),
+ Reason :: term().
+
+new(User, Password, Email) ->
+ Salt = rand:bytes(?RANDBYTES),
+ Hash = crypto:hash(sha256, <>),
+
+ Data = #{<<"email">> => Email},
+ Metadata = [{salt, Salt}, {hash, Hash}, {approved, false}],
+
+ storage:write(?USERSBUCK, User, Data, Metadata).
+
+-doc """
+Update user's details
+
+Spec:
+
+```
+-spec update(User, Name, Email, Desc) -> ok | {error, Reason} when
+ User :: binary(),
+ Name :: binary(),
+ Email :: binary(),
+ Desc :: binary(),
+ Reason :: term().
+```
+
+The details apart from `User` are updated. The username itself is immutable
+and cannot be modified. All the other fields, excluding the e-mail, are the
+ones that can be seen in the public page.
+""".
+-spec update(User, Name, Email, Desc) -> ok | {error, Reason} when
+ User :: binary(),
+ Name :: binary(),
+ Email :: binary(),
+ Desc :: binary(),
+ Reason :: term().
+
+update(User, Name, Email, Desc) ->
+ {ok, CurData, Metadata} = case storage:read(?USERSBUCK, User) of
+ {ok, [R]} ->
+ {ok, R#object.value, R#object.metadata};
+ {error, Reason} -> {error, Reason}
+ end,
+
+ Data = CurData#{<<"email">> => Email, <<"name">> => Name,
+ <<"description">> => Desc},
+
+ storage:write(?USERSBUCK, User, Data, Metadata).
+
+-doc """
+Delete an existing user from the database.
+
+```
+-spec delete(User) -> ok | {error, Reason} when
+ User :: binary(),
+ Reason :: term().
+```
+""".
+-spec delete(User) -> ok | {error, Reason} when
+ User :: binary(),
+ Reason :: term().
+
+delete(User) ->
+ % We are missing the cleanup of the cookies
+ % here. For that, we need to add at least another
+ % API to the storage layer.
+ storage:delete(?USERSBUCK, User).