Packages

Firebase token authentication strategy for AshAuthentication.

Current section

Files

Jump to
ash_authentication_firebase lib strategy firebase strategy.ex
Raw

lib/strategy/firebase/strategy.ex

defimpl AshAuthentication.Strategy, for: AshAuthentication.Strategy.Firebase do
@moduledoc """
Authentication protocol implementation for firebase strategies.
"""
alias Ash.Resource
alias Ash.Changeset
alias AshAuthentication.Errors
alias AshAuthentication.Firebase.TokenVerifier
import AshAuthentication.Plug.Helpers, only: [store_authentication_result: 2]
require Ash.Query
def name(strategy), do: strategy.name
def phases(_), do: [:sign_in]
def actions(_), do: [:sign_in]
def routes(strategy) do
subject_name = AshAuthentication.Info.authentication_subject_name!(strategy.resource)
[
{"/#{subject_name}/#{strategy.name}", :sign_in}
]
end
def method_for_phase(_, :sign_in), do: :post
def plug(strategy, :sign_in, conn) do
params = Map.take(conn.params, [to_string(strategy.token_input)])
result = action(strategy, :sign_in, params, [])
store_authentication_result(conn, result)
end
def action(strategy, :sign_in, params, options) do
api = AshAuthentication.Info.authentication_api!(strategy.resource)
action = Resource.Info.action(strategy.resource, strategy.register_action_name, :create)
with {:ok, project_id} <- fetch_secret(strategy, :project_id),
{:ok, firebase_token} <-
get_firebase_token_from_params(params, strategy.token_input),
{:ok, _token, fields} <- verify_firebase_token(firebase_token, project_id),
user_info <- get_user_info(fields) do
strategy.resource
|> Changeset.new()
|> Changeset.set_context(%{
private: %{
ash_authentication?: true
}
})
|> Changeset.for_create(strategy.register_action_name, %{user_info: user_info},
upsert?: true,
upsert_identity: action.upsert_identity
)
|> api.create(options)
else
_ ->
{:error, Errors.InvalidToken.exception(type: :sign_in)}
end
end
defp verify_firebase_token(token, project_id) do
TokenVerifier.verify(token, project_id)
end
defp get_user_info(fields) do
Map.take(fields, ["user_id", "email"])
end
defp get_firebase_token_from_params(params, token_input) do
if Map.has_key?(params, token_input) do
Map.fetch(params, token_input)
else
Map.fetch(params, token_input |> Atom.to_string())
end
end
defp fetch_secret(strategy, secret_name) do
path = [:authentication, :strategies, strategy.name, secret_name]
with {:ok, {secret_module, secret_opts}} <- Map.fetch(strategy, secret_name),
{:ok, secret} when is_binary(secret) and byte_size(secret) > 0 <-
secret_module.secret_for(path, strategy.resource, secret_opts) do
{:ok, secret}
else
{:ok, secret} ->
{:ok, secret}
_ ->
{:error, Errors.MissingSecret.exception(path: path, resource: strategy.resource)}
end
end
end