Current section

Files

Jump to
maat_feather lib maat_feather_web controllers auth_controller.ex
Raw

lib/maat_feather_web/controllers/auth_controller.ex

defmodule MaatFeatherWeb.AuthController do
@moduledoc """
Controller for authentication.
"""
use MaatFeatherWeb, :controller
alias MaatFeather.Users
alias MaatFeather.Users.User
alias MaatFeather.Auth.{Token, TOTP}
alias MaatFeatherWeb.UserView
action_fallback MaatFeatherWeb.FallbackController
@doc """
Signin a user to the system.
## Request
```json
{
"credentials": {
"email": "elioenaiferrari@gmail.com",
"password": "123456",
"totp_token": "556475" // if user has MFA enabled
}
}
```
## Response
```json
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjE0MzgyNjAsImlzcyI6Im1hYXRfZmVhdGhlciIsImtpZCI6IkJBMTg5QzNGNjZDODJCNjIiLCJzdWIiOiIyRDJEMkQyRDJENDI0NTQ3NDk0RTIwNTA1NTQyNEM0OTQzMjA0QjQ1NTkyRDJEMkQyRDJEMEE0RDQ5NDc2NjRENDEzMDQ3NDM1MzcxNDc1MzQ5NjIzMzQ0NTE0NTQyNDE1MTU1NDE0MTM0NDc0RTQxNDQ0MzQyNjk1MTRCNDI2NzUxNDQ1MjU1NTc1MTQ4NjIzNDY0NzI2NjM3MzQ1MzMyNjk2NzY1NEY0RjYxNjM3NDRCNkM1ODBBMkI2NDUyNTA2OTRFMzQ1MzczNDIyRjU4MzA2RjcyNDQ2ODY3NzY1MTcxNTQ2NzQxMkI0MTdBN0E0Rjc0NEI0NzJCNzQzMjJGNEM3NzQ5NTg0RDUwNDc1MDU2NTI0NzM5NDY2QjU2Njk2NDZDNjI2MjU0MzY0NDM2Mzc3NjY5MkIwQTU2NkU1MzJGMzMzNzU2NkM2OTUxNTc2MTRDNEE1NDcxNkM0QjUzMzA1Nzc2NkU0MjZBNEY2RTRBNDQ2MTZBNTAzMjc3Mzc1MzMwNjY3MDMyN0E0MzU4NUE1MjY3NjE0RjM2MkY0RTRBMzM1QTYzNjMzNTRFNEQ3QTRBNjc0QTQyMEE2ODMxMzA0QjUxNjkyQjUzNkY3NzM4NkM0QTM3Njg2NTQ5NTE0OTQ0NDE1MTQxNDIwQTJEMkQyRDJEMkQ0NTRFNDQyMDUwNTU0MjRDNDk0MzIwNEI0NTU5MkQyRDJEMkQyRDBBMEEiLCJ0eXAiOiJhY2Nlc3MifQ.YMyQdeXdnvoeGhQR7auLwk4PHWD9oM2I_qQ5YGsbEl0G4Vf-pC7mhvl3JnLuvd_F7nMCVvJ-s_sfPbs-XzthknqAkfLZ3uf9yGCEQHxFQ56ex3rMKnTHsOTpyc5tLP2OP2i4JXmfbDr2D4DwynL6egLzxB41c1_WbrJ3Q3khKWOAxHSsyhN21QbepQrZmVOUZZXekslUUlJL9M79d4b1YWWnEb3daRinfHG6fDXp5dLmcSz8cBdVfNoewRqxpxJfFJesFsslox6p88ZWBTJo9-kmud5eWMgpor0yC4cdUoTfAfSn0cZWPkKME4DFk1FVvzXrs4-G_iMp2AoQsdHO8w"
}
```
"""
def signin(conn, %{"credentials" => credentials_params}) do
case Users.get_user_by!(email: credentials_params["email"]) do
%User{provider: "maat_feather", is_verified: true} = user ->
with true <- Argon2.verify_pass(credentials_params["password"], user.password_hash) do
unless user.has_mfa do
{:ok, access_token, _claims} = Token.encode(user)
conn
|> put_status(:ok)
|> json(%{access_token: access_token})
else
if TOTP.is_valid?(user.secret_key, credentials_params["totp_token"]) do
{:ok, access_token, _claims} = Token.encode(user)
conn
|> put_status(:ok)
|> json(%{access_token: access_token})
else
{:error, :unauthorized}
end
end
end
%User{provider: "maat_feather", is_verified: false} = _user ->
{:error, {:unauthorized, "User is not verified"}}
%User{provider: provider} ->
{:error, {:unauthorized, "user uses a oauth2 #{provider} provider"}}
_ ->
{:error, :not_found}
end
end
@doc """
Refresh an access token.
## Response
```json
{
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjE0MzgwODIsImlzcyI6Im1hYXRfZmVhdGhlciIsImtpZCI6IkNFNjI0Nzc0RjJFQjk2QjgiLCJzdWIiOiIyRDJEMkQyRDJENDI0NTQ3NDk0RTIwNTA1NTQyNEM0OTQzMjA0QjQ1NTkyRDJEMkQyRDJEMEE0RDQ5NDc2NjRENDEzMDQ3NDM1MzcxNDc1MzQ5NjIzMzQ0NTE0NTQyNDE1MTU1NDE0MTM0NDc0RTQxNDQ0MzQyNjk1MTRCNDI2NzUxNDQ1MjU1NTc1MTQ4NjIzNDY0NzI2NjM3MzQ1MzMyNjk2NzY1NEY0RjYxNjM3NDRCNkM1ODBBMkI2NDUyNTA2OTRFMzQ1MzczNDIyRjU4MzA2RjcyNDQ2ODY3NzY1MTcxNTQ2NzQxMkI0MTdBN0E0Rjc0NEI0NzJCNzQzMjJGNEM3NzQ5NTg0RDUwNDc1MDU2NTI0NzM5NDY2QjU2Njk2NDZDNjI2MjU0MzY0NDM2Mzc3NjY5MkIwQTU2NkU1MzJGMzMzNzU2NkM2OTUxNTc2MTRDNEE1NDcxNkM0QjUzMzA1Nzc2NkU0MjZBNEY2RTRBNDQ2MTZBNTAzMjc3Mzc1MzMwNjY3MDMyN0E0MzU4NUE1MjY3NjE0RjM2MkY0RTRBMzM1QTYzNjMzNTRFNEQ3QTRBNjc0QTQyMEE2ODMxMzA0QjUxNjkyQjUzNkY3NzM4NkM0QTM3Njg2NTQ5NTE0OTQ0NDE1MTQxNDIwQTJEMkQyRDJEMkQ0NTRFNDQyMDUwNTU0MjRDNDk0MzIwNEI0NTU5MkQyRDJEMkQyRDBBMEEiLCJ0eXAiOiJyZWZyZXNoIn0.fKxIwfti40QAiJsE9nN0_QjXhzsczvJoFoBaPZPikRNeaAXkjDrhNw2JRw71bB2WqBNlyojYEgjJvkqRe02MFenioaz_NOsewp3EGGBW3efPdTkJb9osHzTztfT8_E3fT8bsiFUxNrgsgc6ToQt2BX227ctJMjQZOK3BU6FqSQvUykulrjSAJi5C62bABxW9dMBGUiiMs1Jq7o-b9xLOEKN4Mor3o3C2brz9ifhMwFR1phWgpNosmunvnzNuKABgkdzqYSkgWz6KKQ_2NMUvNZcXa0Defs5_-Ait9Zda2yBXELXhAKmT9_vRSe_GMrJBVQZFurOFgTl64euqwu-OPQ"
}
```
"""
def refresh_token(%Plug.Conn{assigns: %{access_token: access_token}} = conn, _params) do
with {:ok, {_old_token, _old_claims}, {new_token, _new_claims}} <- Token.refresh(access_token) do
conn
|> put_status(:ok)
|> json(%{refresh_token: new_token})
else
{:error, message} ->
conn
|> put_status(:unauthorized)
|> json(%{errors: %{message: message}})
end
end
def refresh_token(_conn, _params), do: {:error, :unauthorized}
@doc """
Get totp qrcode.
## Response
```json
{
"qrcode": "<?xml version=\"1.0\" standalone=\"yes\"?>\n<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xmlns:ev=\"http://www.w3.org/2001/xml-events\"...",
"uri": "otpauth://totp/maat_feather:elioenaiferrari@gmail.com?secret=CRDJN4BBPYOHIWTY&issuer=maat_feather"
}
```
"""
def totp_qrcode(%Plug.Conn{assigns: %{user: %{"sub" => address}}} = conn, _params) do
case Users.get_user!(address) do
nil ->
{:error, :not_found}
user ->
{qrcode, uri} = TOTP.gen_qrcode!(user.email, user.secret_key)
conn
|> put_status(:ok)
|> json(%{qrcode: qrcode, uri: uri})
end
end
@doc """
Get user profile.
## Response
```json
{
"data": {
"address": "2D2D2D2D2D424547494E205055424C4943204B45592D2D2D2D2D0A4D4947664D413047435371475349623344514542415155414134474E4144434269514B4267514452555751486234647266373453326967654F4F6163744B6C580A2B645250694E345373422F58306F724468677651715467412B417A7A4F744B472B74322F4C7749584D50475056524739466B5669646C6262543644363776692B0A566E532F3337566C695157614C4A54716C4B533057766E426A4F6E4A44616A5032773753306670327A43585A5267614F362F4E4A335A6363354E4D7A4A674A420A6831304B51692B536F77386C4A37686549514944415141420A2D2D2D2D2D454E44205055424C4943204B45592D2D2D2D2D0A0A",
"email": "elioenaiferrari@gmail.com",
"role": "user",
"username": "Elix"
}
}
```
"""
def profile(
%Plug.Conn{assigns: %{user: %{"sub" => address}}} = conn,
_params
) do
case Users.get_user!(address) do
nil ->
{:error, :not_found}
user ->
conn
|> put_view(UserView)
|> put_status(:ok)
|> render("show.json", %{user: user})
end
end
def profile(_conn, _params), do: {:error, :unauthorized}
@doc """
Account verification.
## Response
```json
{
"message": "Account verified. You can now login."
}
```
"""
def account_verification(conn, %{"one_time_token" => one_time_token}) do
with %User{} = user <- Users.get_user_by!(one_time_token: one_time_token),
{:ok, _} <-
Users.update_user(user, %{
is_verified: true,
one_time_token: :crypto.strong_rand_bytes(16) |> Base.encode16()
}) do
conn
|> put_status(:ok)
|> json(%{
message: "Account verified. You can now login."
})
else
_ -> {:error, :internal_server_error}
end
end
end