From 43a578be42cfb49bb1048eff672a5a4d75d4099f Mon Sep 17 00:00:00 2001 From: Danielwhyte Date: Tue, 8 Jan 2019 15:01:31 +0000 Subject: [PATCH 001/189] adds setup module to dynamically define config at compile time --- lib/auth/setup.ex | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 lib/auth/setup.ex diff --git a/lib/auth/setup.ex b/lib/auth/setup.ex new file mode 100644 index 00000000..955d2d11 --- /dev/null +++ b/lib/auth/setup.ex @@ -0,0 +1,18 @@ +defmodule Auth.Setup do + Code.compile_quoted( + quote do + Application.put_env(:ueberauth, Ueberauth, + providers: [ + github: {Ueberauth.Strategy.Github, []}, + identity: + {Ueberauth.Strategy.Identity, + [ + callback_methods: ["POST"], + uid_field: :email, + nickname_field: :email + ]} + ] + ) + end + ) +end From 7936c116e382ee0b1e6ce068aff4af8dbcde3549 Mon Sep 17 00:00:00 2001 From: Danielwhyte Date: Fri, 18 Jan 2019 12:32:44 +0000 Subject: [PATCH 002/189] initial code for forwarding requests from external app --- lib/auth.ex | 46 +++++++++++++++++++++ lib/auth/setup.ex | 7 ++++ lib/auth/user/user.ex | 35 +++++++++++----- lib/auth_web/controllers/auth_controller.ex | 29 +++++-------- lib/auth_web/router.ex | 13 +----- mix.exs | 4 +- 6 files changed, 93 insertions(+), 41 deletions(-) create mode 100644 lib/auth.ex diff --git a/lib/auth.ex b/lib/auth.ex new file mode 100644 index 00000000..83e7c89a --- /dev/null +++ b/lib/auth.ex @@ -0,0 +1,46 @@ +defmodule Auth.Auth do + alias Auth.User + + def validate_user(uid, %{other: %{password: pw, password_confirmation: nil}}), + do: validate(uid, pw) + + def validate_user(uid, %{other: %{password: pw, password_confirmation: pw}}), + do: validate(uid, pw) + + def validate_user(uid, _), do: {:error, "passwords do not match"} + + def validate(uid, password) do + IO.inspect(:application.get_application(CsGuide.Repo)) + + get_by_uid = + case Application.get_env(:auth, Auth) do + nil -> :email_hash + config -> Keyword.get(config, :identity) |> Keyword.get(:uid_field) + end + |> (fn + nil -> Keyword.put([], :email_hash, uid) + uid_field -> Keyword.put([], uid_field, uid) + end).() + + with %User{} = user <- User.get_by(get_by_uid), + true <- Argon2.verify_pass(password, user.password) do + {:ok, user} + else + nil -> + with _ <- Argon2.no_user_verify() do + IO.inspect("no user") + {:error, "invalid credentials"} + end + + false -> + IO.inspect("bad pw") + {:error, "invalid credentials"} + end + end + + def login(conn, user) do + conn + |> Plug.Conn.put_session(:user_id, user.entry_id) + |> Plug.Conn.configure_session(renew: true) + end +end diff --git a/lib/auth/setup.ex b/lib/auth/setup.ex index 955d2d11..e38b10aa 100644 --- a/lib/auth/setup.ex +++ b/lib/auth/setup.ex @@ -1,4 +1,7 @@ defmodule Auth.Setup do + # Should run on code compilation - + # Reads config from app and + # sets up config for nested dependencies Code.compile_quoted( quote do Application.put_env(:ueberauth, Ueberauth, @@ -13,6 +16,10 @@ defmodule Auth.Setup do ]} ] ) + + Application.put_env(:alog, Alog, + repo: Application.get_env(:auth, Auth) |> Keyword.get(:repo) + ) end ) end diff --git a/lib/auth/user/user.ex b/lib/auth/user/user.ex index 178bd73c..4892ad00 100644 --- a/lib/auth/user/user.ex +++ b/lib/auth/user/user.ex @@ -1,23 +1,36 @@ defmodule Auth.User do use Ecto.Schema + use Alog import Ecto.Changeset schema "users" do - field(:email, :string) - field(:name, :string) - field(:username, :string) - field(:password, :string, virtual: true) - field(:password_hash, :string) + field(:email, Fields.EmailEncrypted) + field(:email_hash, Fields.EmailHash) + field(:email_plaintext, Fields.EmailPlaintext, virtual: true) + field(:password, Fields.Password) + field(:entry_id, :string) + field(:deleted, :boolean, default: false) + field(:admin, :boolean, default: false) + field(:name, :string, virtual: true) + field(:username, :string, virtual: true) + timestamps() end - def changeset(model, params \\ %{}) do - model - |> cast(params, ~w(email)) + @doc false + def changeset(user, attrs \\ %{}) do + user + |> cast(attrs, [:email, :password, :admin]) |> validate_required([:email]) - |> validate_format(:email, ~r/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/) - # save unique_constraint for last as DB call - |> unique_constraint(:email) + |> put_email_hash() + |> unique_constraint(:email_hash) + end + + def put_email_hash(user) do + case get_change(user, :email) do + nil -> user + email -> put_change(user, :email_hash, email) + end end def registration_changeset(model, params) do diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 5ab9117f..f50b5814 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -31,26 +31,19 @@ defmodule AuthWeb.AuthController do end def identity_callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do - IO.puts("auth:") - IO.inspect(auth) - opts = {} - repo = Keyword.fetch!(opts, :repo) - user = repo.get_by(User, username: auth.username) - IO.inspect(user) - # case validate_password(auth.credentials) do - case user && checkpw(auth.password, user.password_hash) do - :ok -> - user = %{id: auth.uid, name: auth.name, avatar: auth.info.image} - + case Auth.Auth.validate_user(auth.uid, auth.credentials) do + {:ok, user} -> conn - |> put_flash(:info, "Successfully authenticated.") - |> put_session(:current_user, user) - |> redirect(to: "/") + |> Auth.login(user) + |> (fn c -> + case user.admin do + true -> redirect(c, to: "/admin") + false -> redirect(c, to: "/") + end + end).() - {:error, reason} -> - conn - |> put_flash(:error, reason) - |> redirect(to: "/") + err -> + redirect(conn, to: "/auth/identity") end end diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index d7a8e417..0ac8dc0a 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -14,26 +14,17 @@ defmodule AuthWeb.Router do plug(:accepts, ["json"]) end - scope "/auth", AuthWeb do + scope "/", AuthWeb do pipe_through(:browser) get("/register", UserController, :register) post("/register", UserController, :register) get("/:provider", AuthController, :request) + post("/identity/callback", AuthController, :identity_callback) get("/:provider/callback", AuthController, :callback) post("/:provider/callback", AuthController, :callback) - post("/identity/callback", AuthController, :identity_callback) delete("/logout", AuthController, :delete) end - scope "/", AuthWeb do - # Use the default browser stack - pipe_through(:browser) - - get("/", PageController, :index) - resources("/email", EmailController, only: [:index, :create]) - resources("/user", UserController, only: [:index, :create, :new, :show]) - end - # Other scopes may use custom stacks. # scope "/api", Auth do # pipe_through :api diff --git a/mix.exs b/mix.exs index e1e418e4..9936d81d 100644 --- a/mix.exs +++ b/mix.exs @@ -48,8 +48,10 @@ defmodule Auth.Mixfile do # # Type `mix help deps` for examples and options. defp deps do - # Phoenix core: [ + {:fields, git: "https://github.com/dwyl/fields.git", tag: "0.1.4"}, + {:alog, git: "https://github.com/dwyl/alog.git", tag: "0.4.2"}, + # Phoenix core: {:phoenix, "~> 1.4.0"}, {:phoenix_pubsub, "~> 1.1"}, {:phoenix_ecto, "~> 4.0"}, From c9062481ca1122d8c596bd91e3aa0d18913e4b7c Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 6 Feb 2020 10:02:19 +0000 Subject: [PATCH 003/189] create .env_sample file --- .env_sample | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .env_sample diff --git a/.env_sample b/.env_sample new file mode 100644 index 00000000..ba216153 --- /dev/null +++ b/.env_sample @@ -0,0 +1,4 @@ +export GOOGLE_CLIENT_ID=YourAppsClientId.apps.googleusercontent.com +export GOOGLE_CLIENT_SECRET=SuperSecret +export SECRET_KEY_BASE=2PzB7PPnpuLsbWmWtXpGyI+kfSQSQ1zUW2Atz/+8PdZuSEJzHgzGnJWV35nTKRwx +export ENCRYPTION_KEYS='nMdayQpR0aoasLaq1g94FLba+A+wB44JLko47sVQXMg=,L+ZVX8iheoqgqb22mUpATmMDsvVGtafoAeb0KN5uWf0=' From d6be360a42ce5cdf6a991da444d770a2a6917755 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 6 Feb 2020 10:39:34 +0000 Subject: [PATCH 004/189] [WiP] revive project --- README.md | 35 +++++++++++++++++++++++------------ config/config.exs | 14 +++++++------- config/prod.exs | 2 +- lib/auth/setup.ex | 40 ++++++++++++++++++++-------------------- mix.exs | 16 ++++++++-------- mix.lock | 31 ++++++++++++++++++------------- 6 files changed, 77 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 8f6aacbb..454744de 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,22 @@ -# Elixir |> Phoenix |> Authentication +
-A ***Complete Authentication Solution*** for **Phoenix** Apps/APIs -to get you ***up and running*** in the next ***5 minutes***. +# `auth` -[![Build Status](https://travis-ci.org/dwyl/auth.svg)](https://travis-ci.org/dwyl/auth) -[![codecov.io](https://codecov.io/github/dwyl/auth/coverage.svg?branch=master)](https://codecov.io/github/dwyl/auth?branch=master) +A ***complete authentication solution*** for **Phoenix** Apps/APIs +you can setup in ***5 minutes***. +[![Build Status](https://img.shields.io/travis/dwyl/fields/master.svg?style=flat-square)](https://travis-ci.org/dwyl/fields) +[![codecov.io](https://img.shields.io/codecov/c/github/dwyl/fields/master.svg?style=flat-square)](http://codecov.io/github/dwyl/fields?branch=master) +[![Hex.pm](https://img.shields.io/hexpm/v/fields?color=brightgreen&style=flat-square)](https://hex.pm/packages/fields) +[![docs](https://img.shields.io/badge/docs-maintained-brightgreen?style=flat-square)](https://hexdocs.pm/fields/api-reference.html) +[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat-square)](https://github.com/dwyl/fields/issues) + + +
## Why? @@ -14,7 +25,7 @@ there is personalized content/functionality to display. We needed an *easy* way of doing Login/Authentication for our projects that we could drop into any project and be up-and-running in _minutes_ -and ***avoid*** "***re-inventing the wheel***". +without worrying about complexity or maintenance. After much research and investigation, we decided to use a few *existing* **Elixir** modules together to form a re-useable "starter pack". @@ -22,8 +33,8 @@ After much research and investigation, we decided to use a few *existing* ### What's In It For Me? As a developer, _using_ this module you can _rest assured_ that -+ **all code** for **authentication** in _your_ app is -**nicely contained & organized** in a ***single place**. ++ **all code** for **authentication** in _your_ app +is **nicely contained & organized** in a ***single place***. + all the auth-related code is ***well documented, tested & maintained***. + when ever there is an update in the underlying modules (_dependencies_) they will be **updated** and throughly tested in a ***timely manner***. @@ -37,9 +48,9 @@ and your app continues to work as expected. Login for Elixir/Phoenix Apps/APIs which gives you a set of routes and a predictable usage pattern. -### Auth "Strategies" +### What Can People Use to Authenticate? -+ "***Basic***" - Username/Email and Password (_enabled by default_) ++ **Email+Password** - Email and Password (_enabled by default_) + **GitHub** - Allow people to login with their GitHub Account using OAuth2 + **Google** - Let people authenticate with the most popular auth system! @@ -49,7 +60,7 @@ Our *objective* is to **_extensively_ test every aspect** of this package so that we can *rely* on it for our *high-traffic/security* projects. If you spot _any_ area for improvement, please create an issue: -https://github.com/dwyl/auth/issues so we can discuss. (_thanks!_) +https://github.com/dwyl/auth/issues (_thanks!_) ### Email Verification @@ -74,7 +85,7 @@ If you or *anyone* on your team are new to Phoenix, we have an **introductory tutorial**: [github.com/dwyl/**learn-phoenix-framework**](https://github.com/dwyl/learn-phoenix-framework) -### _One Minute_ Setup +### _5 Minute_ Setup diff --git a/config/config.exs b/config/config.exs index 4f186b11..bdb1909b 100644 --- a/config/config.exs +++ b/config/config.exs @@ -22,14 +22,14 @@ config :logger, :console, metadata: [:request_id] # Configure Ueberauth providers -config :ueberauth, Ueberauth, - providers: [ - github: {Ueberauth.Strategy.Github, []} - ] +# config :ueberauth, Ueberauth, +# providers: [ +# github: {Ueberauth.Strategy.Github, []} +# ] -config :ueberauth, Ueberauth.Strategy.Github.OAuth, - client_id: System.get_env("GITHUB_CLIENT_ID"), - client_secret: System.get_env("GITHUB_CLIENT_SECRET") +# config :ueberauth, Ueberauth.Strategy.Github.OAuth, +# client_id: System.get_env("GITHUB_CLIENT_ID"), +# client_secret: System.get_env("GITHUB_CLIENT_SECRET") # Configure email via AWS SES config :auth, Auth.Mailer, diff --git a/config/prod.exs b/config/prod.exs index 63cbd02f..6c369ff9 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -58,4 +58,4 @@ config :logger, level: :info # Finally import the config/prod.secret.exs # which should be versioned separately. -import_config "prod.secret.exs" +# import_config "prod.secret.exs" diff --git a/lib/auth/setup.ex b/lib/auth/setup.ex index e38b10aa..44cf6199 100644 --- a/lib/auth/setup.ex +++ b/lib/auth/setup.ex @@ -2,24 +2,24 @@ defmodule Auth.Setup do # Should run on code compilation - # Reads config from app and # sets up config for nested dependencies - Code.compile_quoted( - quote do - Application.put_env(:ueberauth, Ueberauth, - providers: [ - github: {Ueberauth.Strategy.Github, []}, - identity: - {Ueberauth.Strategy.Identity, - [ - callback_methods: ["POST"], - uid_field: :email, - nickname_field: :email - ]} - ] - ) - - Application.put_env(:alog, Alog, - repo: Application.get_env(:auth, Auth) |> Keyword.get(:repo) - ) - end - ) + # Code.compile_quoted( + # quote do + # Application.put_env(:ueberauth, Ueberauth, + # providers: [ + # github: {Ueberauth.Strategy.Github, []}, + # identity: + # {Ueberauth.Strategy.Identity, + # [ + # callback_methods: ["POST"], + # uid_field: :email, + # nickname_field: :email + # ]} + # ] + # ) + # + # Application.put_env(:alog, Alog, + # repo: Application.get_env(:auth, Auth) |> Keyword.get(:repo) + # ) + # end + # ) end diff --git a/mix.exs b/mix.exs index 9936d81d..d56ca671 100644 --- a/mix.exs +++ b/mix.exs @@ -49,14 +49,14 @@ defmodule Auth.Mixfile do # Type `mix help deps` for examples and options. defp deps do [ - {:fields, git: "https://github.com/dwyl/fields.git", tag: "0.1.4"}, - {:alog, git: "https://github.com/dwyl/alog.git", tag: "0.4.2"}, + {:fields, "~> 2.2.0"}, + # {:alog, git: "https://github.com/dwyl/alog.git", tag: "0.4.2"}, # Phoenix core: - {:phoenix, "~> 1.4.0"}, + {:phoenix, "~> 1.4.12"}, {:phoenix_pubsub, "~> 1.1"}, {:phoenix_ecto, "~> 4.0"}, - {:ecto_sql, "~> 3.0"}, - {:postgrex, ">= 0.0.0"}, + {:ecto_sql, "~> 3.3.3"}, + {:postgrex, ">= 0.15.0"}, {:phoenix_html, "~> 2.11"}, {:phoenix_live_reload, "~> 1.2", only: :dev}, {:gettext, "~> 0.11"}, @@ -65,9 +65,9 @@ defmodule Auth.Mixfile do # Auth: # github.com/ueberauth/ueberauth - {:ueberauth, "~> 0.4"}, + # {:ueberauth, "~> 0.4"}, # github.com/ueberauth/ueberauth_identity - {:ueberauth_identity, "~> 0.2"}, + # {:ueberauth_identity, "~> 0.2"}, # Email Sent by AWS SES see: https://git.io/vSuqc # github.com/thoughtbot/bamboo @@ -77,7 +77,7 @@ defmodule Auth.Mixfile do # Password Hashing # github.com/riverrun/comeonin (bcrypt) - {:comeonin, "~> 2.0"}, + # {:comeonin, "~> 2.0"}, # Dev/Test only: # for checking test coverage diff --git a/mix.lock b/mix.lock index d32b117c..3b0ead3a 100644 --- a/mix.lock +++ b/mix.lock @@ -1,20 +1,24 @@ %{ + "argon2_elixir": {:hex, :argon2_elixir, "2.1.2", "c276b960f0b550a7613a9bebf8e14645ca5eb71a34a1bf0f896fe3511966b051", [:make, :mix], [{:comeonin, "~> 5.1", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.5", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"}, "bamboo": {:hex, :bamboo, "1.1.0", "ecbdc851d0127d369957e5ca7bcfb4c7fe7dbfaf4fa0b2e5cc2493dd8a9a600e", [:mix], [{:hackney, ">= 1.13.0", [hex: :hackney, optional: false]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}, {:poison, ">= 1.5.0", [hex: :poison, optional: false]}]}, "bamboo_smtp": {:hex, :bamboo_smtp, "1.6.0", "0a3607b77f22554af58c547350c1c73ebba6f4fb2c4bd0b11713ab5b4081588f", [:mix], [{:bamboo, "~> 1.0", [hex: :bamboo, optional: false]}, {:gen_smtp, "~> 0.12.0", [hex: :gen_smtp, optional: false]}]}, "certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, optional: false]}]}, - "comeonin": {:hex, :comeonin, "2.6.0", "74c288338b33205f9ce97e2117bb9a2aaab103a1811d243443d76fdb62f904ac", [:make, :mix], []}, + "comeonin": {:hex, :comeonin, "5.2.0", "d0277415a4b887f7a5f418be78776e5dacf1b005105913f075d301c0da3b5311", [:mix], [], "hexpm"}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []}, - "cowboy": {:hex, :cowboy, "2.6.1", "f2e06f757c337b3b311f9437e6e072b678fcd71545a7b2865bdaa154d078593f", [:rebar3], [{:cowlib, "~> 2.7.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, optional: false]}]}, - "cowlib": {:hex, :cowlib, "2.7.0", "3ef16e77562f9855a2605900cedb15c1462d76fb1be6a32fc3ae91973ee543d2", [:rebar3], []}, - "db_connection": {:hex, :db_connection, "2.0.3", "b4e8aa43c100e16f122ccd6798cd51c48c79fd391c39d411f42b3cd765daccb0", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}]}, - "decimal": {:hex, :decimal, "1.6.0", "bfd84d90ff966e1f5d4370bdd3943432d8f65f07d3bab48001aebd7030590dcc", [:mix], []}, - "ecto": {:hex, :ecto, "3.0.6", "d33ab5b3f7553a41507d4b0ad5bf192d533119c4ad08f3a5d63d85aa12117dc9", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, optional: false]}, {:jason, "~> 1.0", [hex: :jason, optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, optional: true]}]}, - "ecto_sql": {:hex, :ecto_sql, "3.0.4", "e7a0feb0b2484b90981c56d5cd03c52122c1c31ded0b95ed213b7c5c07ae6737", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, optional: false]}, {:ecto, "~> 3.0.6", [hex: :ecto, optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, optional: true]}, {:postgrex, "~> 0.14.0", [hex: :postgrex, optional: true]}, {:telemetry, "~> 0.3.0", [hex: :telemetry, optional: false]}]}, + "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, + "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm"}, + "db_connection": {:hex, :db_connection, "2.2.0", "e923e88887cd60f9891fd324ac5e0290954511d090553c415fbf54be4c57ee63", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, + "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm"}, + "ecto": {:hex, :ecto, "3.3.2", "002aa428c752a8ee4bb65baa9d1041f0514d7435d2e21d58cb6aa69a1076721d", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, + "ecto_sql": {:hex, :ecto_sql, "3.3.3", "7d8962d39f16181c1df1bbd0f64aa392bd9ce0b9f8ff5ff21d43dca3d624eee7", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, + "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm"}, "excoveralls": {:hex, :excoveralls, "0.10.3", "b090a3fbcb3cfa136f0427d038c92a9051f840953ec11b40ee74d9d4eac04d1e", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, optional: false]}, {:jason, "~> 1.0", [hex: :jason, optional: false]}]}, + "fields": {:hex, :fields, "2.2.0", "6e52310411e244d9e2faaa399a78b829a6304634bba527ee45b18da2f557984e", [:mix], [{:argon2_elixir, "~> 2.1.2", [hex: :argon2_elixir, repo: "hexpm", optional: false]}, {:ecto, "~> 3.3.1", [hex: :ecto, repo: "hexpm", optional: false]}, {:html_sanitize_ex, "~> 1.4", [hex: :html_sanitize_ex, repo: "hexpm", optional: false]}], "hexpm"}, "file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [:mix], []}, "gen_smtp": {:hex, :gen_smtp, "0.12.0", "97d44903f5ca18ca85cb39aee7d9c77e98d79804bbdef56078adcf905cb2ef00", [:rebar3], []}, "gettext": {:hex, :gettext, "0.16.1", "e2130b25eebcbe02bb343b119a07ae2c7e28bd4b146c4a154da2ffb2b3507af2", [:mix], []}, "hackney": {:hex, :hackney, "1.15.0", "287a5d2304d516f63e56c469511c42b016423bcb167e61b611f6bad47e3ca60e", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, optional: false]}, {:idna, "6.0.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, optional: false]}]}, + "html_sanitize_ex": {:hex, :html_sanitize_ex, "1.4.0", "0310d27d7bafb662f30bff22ec732a72414799c83eaf44239781fd23b96216c0", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"}, "httpoison": {:hex, :httpoison, "0.13.0", "bfaf44d9f133a6599886720f3937a7699466d23bb0cd7a88b6ba011f53c6f562", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, optional: false]}]}, "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, optional: true]}]}, @@ -22,21 +26,22 @@ "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], []}, "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, + "mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"}, "mock": {:hex, :mock, "0.2.1", "bfdba786903e77f9c18772dee472d020ceb8ef000783e737725a4c8f54ad28ec", [:mix], [{:meck, "~> 0.8.2", [hex: :meck, optional: false]}]}, "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], []}, - "phoenix": {:hex, :phoenix, "1.4.0", "56fe9a809e0e735f3e3b9b31c1b749d4b436e466d8da627b8d82f90eaae714d2", [:mix], [{:jason, "~> 1.0", [hex: :jason, optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.7", [hex: :plug, optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, optional: true]}]}, + "phoenix": {:hex, :phoenix, "1.4.12", "b86fa85a2ba336f5de068549de5ccceec356fd413264a9637e7733395d6cc4ea", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, optional: true]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}]}, "phoenix_html": {:hex, :phoenix_html, "2.13.0", "3bad10de5efb6c590f7aa5b316ad0d3faa054715414c9b562c410de4ffb885c5", [:mix], [{:plug, "~> 1.5", [hex: :plug, optional: false]}]}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.0", "3bb31a9fbd40ffe8652e60c8660dffd72dd231efcdf49b744fb75b9ef7db5dd2", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, optional: false]}]}, - "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.1", "6668d787e602981f24f17a5fbb69cc98f8ab085114ebfac6cc36e10a90c8e93c", [:mix], []}, - "plug": {:hex, :plug, "1.7.1", "8516d565fb84a6a8b2ca722e74e2cd25ca0fc9d64f364ec9dbec09d33eb78ccd", [:mix], [{:mime, "~> 1.0", [hex: :mime, optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, optional: false]}]}, - "plug_cowboy": {:hex, :plug_cowboy, "2.0.1", "d798f8ee5acc86b7d42dbe4450b8b0dadf665ce588236eb0a751a132417a980e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, optional: false]}, {:plug, "~> 1.7", [hex: :plug, optional: false]}]}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"}, + "plug": {:hex, :plug, "1.8.3", "12d5f9796dc72e8ac9614e94bda5e51c4c028d0d428e9297650d09e15a684478", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.1.2", "8b0addb5908c5238fac38e442e81b6fcd32788eaa03246b4d55d147c47c5805e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], []}, "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []}, - "postgrex": {:hex, :postgrex, "0.14.1", "63247d4a5ad6b9de57a0bac5d807e1c32d41e39c04b8a4156a26c63bcd8a2e49", [:mix], [{:connection, "~> 1.0", [hex: :connection, optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, optional: false]}, {:jason, "~> 1.0", [hex: :jason, optional: true]}]}, + "postgrex": {:hex, :postgrex, "0.15.3", "5806baa8a19a68c4d07c7a624ccdb9b57e89cbc573f1b98099e3741214746ae4", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], []}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], []}, - "telemetry": {:hex, :telemetry, "0.3.0", "099a7f3ce31e4780f971b4630a3c22ec66d22208bc090fe33a2a3a6a67754a73", [:rebar3], []}, + "telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm"}, "ueberauth": {:hex, :ueberauth, "0.5.0", "4570ec94d7f784dc4c4aa94c83391dbd9b9bd7b66baa30e95a666c5ec1b168b1", [:mix], [{:plug, "~> 1.2", [hex: :plug, optional: false]}]}, "ueberauth_identity": {:hex, :ueberauth_identity, "0.2.3", "216f18ca24347f6b3dd0e5f4342b6fef7a4027fad0d133f1c454d51b7eaac55b", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}, {:ueberauth, "~> 0.2", [hex: :ueberauth, optional: false]}]}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], []}, From 20be37ec09d25d357b4940fe651e04c9c49f417c Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 13:48:28 +0100 Subject: [PATCH 005/189] mix phx.new auth --- .formatter.exs | 5 + .gitignore | 19 ++- .travis.yml | 4 +- README.md | 3 +- assets/.babelrc | 5 + assets/brunch-config.js | 65 --------- assets/css/app.css | 4 +- assets/css/phoenix.css | 134 ++++++++++++++++++ assets/js/app.js | 22 ++- assets/js/socket.js | 23 +-- assets/package.json | 20 +-- assets/webpack.config.js | 41 ++++++ config/config.exs | 32 +---- config/dev.exs | 59 ++++++-- config/prod.exs | 74 +++++----- config/test.exs | 18 +-- lib/auth.ex | 53 ++----- lib/auth/application.ex | 20 +-- lib/auth/mailer.ex | 3 - lib/auth/repo.ex | 4 +- lib/auth/setup.ex | 25 ---- lib/auth/user/user.ex | 66 --------- lib/auth/user/user_from_auth.ex | 48 ------- lib/auth_web.ex | 10 -- lib/auth_web/channels/user_socket.ex | 8 +- lib/auth_web/controllers/auth_controller.ex | 82 ----------- lib/auth_web/controllers/email_controller.ex | 18 --- lib/auth_web/controllers/page_controller.ex | 6 +- .../controllers/send_email_controller.ex | 14 -- lib/auth_web/controllers/user_controller.ex | 62 -------- lib/auth_web/endpoint.ex | 45 +++--- lib/auth_web/gettext.ex | 10 +- lib/auth_web/router.ex | 35 ++--- lib/auth_web/templates/auth/request.html.eex | 16 --- lib/auth_web/templates/email/index.html.eex | 16 --- .../templates/html_email/email.html.eex | 20 --- .../templates/html_email/index.html.eex | 11 -- lib/auth_web/templates/layout/app.html.eex | 64 ++++----- lib/auth_web/templates/page/index.html.eex | 78 +++++----- lib/auth_web/templates/user/register.html.eex | 17 --- lib/auth_web/views/auth_view.ex | 5 - lib/auth_web/views/email_view.ex | 3 - lib/auth_web/views/error_helpers.ex | 35 +++-- lib/auth_web/views/error_view.ex | 21 ++- lib/auth_web/views/html_email_view.ex | 3 - lib/auth_web/views/user_view.ex | 14 -- lib/email.ex | 22 --- lib/validate_email.ex | 10 -- mix.exs | 41 ++---- mix.lock | 48 ------- priv/gettext/en/LC_MESSAGES/errors.po | 8 +- priv/gettext/errors.pot | 12 +- priv/repo/migrations/.formatter.exs | 4 + .../migrations/20170207074532_create_user.exs | 17 --- priv/repo/seeds.exs | 2 +- .../controllers/page_controller_test.exs | 2 +- test/auth_web/views/error_view_test.exs | 10 +- test/auth_web/views/layout_view_test.exs | 5 + test/support/channel_case.ex | 10 +- test/support/conn_case.ex | 10 +- test/support/data_case.ex | 12 +- test/test_helper.exs | 4 +- test/validate_email_test.exs | 14 -- 63 files changed, 543 insertions(+), 1028 deletions(-) create mode 100644 .formatter.exs create mode 100644 assets/.babelrc delete mode 100644 assets/brunch-config.js create mode 100644 assets/css/phoenix.css create mode 100644 assets/webpack.config.js delete mode 100644 lib/auth/mailer.ex delete mode 100644 lib/auth/setup.ex delete mode 100644 lib/auth/user/user.ex delete mode 100644 lib/auth/user/user_from_auth.ex delete mode 100644 lib/auth_web/controllers/auth_controller.ex delete mode 100644 lib/auth_web/controllers/email_controller.ex delete mode 100644 lib/auth_web/controllers/send_email_controller.ex delete mode 100644 lib/auth_web/controllers/user_controller.ex delete mode 100644 lib/auth_web/templates/auth/request.html.eex delete mode 100644 lib/auth_web/templates/email/index.html.eex delete mode 100644 lib/auth_web/templates/html_email/email.html.eex delete mode 100644 lib/auth_web/templates/html_email/index.html.eex delete mode 100644 lib/auth_web/templates/user/register.html.eex delete mode 100644 lib/auth_web/views/auth_view.ex delete mode 100644 lib/auth_web/views/email_view.ex delete mode 100644 lib/auth_web/views/html_email_view.ex delete mode 100644 lib/auth_web/views/user_view.ex delete mode 100644 lib/email.ex delete mode 100644 lib/validate_email.ex delete mode 100644 mix.lock create mode 100644 priv/repo/migrations/.formatter.exs delete mode 100644 priv/repo/migrations/20170207074532_create_user.exs delete mode 100644 test/validate_email_test.exs diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 00000000..8a6391c6 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,5 @@ +[ + import_deps: [:ecto, :phoenix], + inputs: ["*.{ex,exs}", "priv/*/seeds.exs", "{config,lib,test}/**/*.{ex,exs}"], + subdirectories: ["priv/*/migrations"] +] diff --git a/.gitignore b/.gitignore index e2090cf6..e3b326f6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # App artifacts /_build /db -/deps /*.ez # Generated on crash by the VM @@ -20,6 +19,10 @@ npm-debug.log # this depending on your deployment strategy. /priv/static/ + +# Ignore package tarball (built via "mix hex.build"). +auth-*.tar + # The config/prod.secret.exs file by default contains sensitive # data and you should not commit it into version control. # @@ -29,4 +32,16 @@ npm-debug.log /config/prod.secret.exs .env -.elixir_ls \ No newline at end of file +.elixir_ls + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# Where 3rd-party dependencies like ExDoc output generated docs. +/doc/ + +# The directory Mix downloads your dependencies sources to. +/deps/ diff --git a/.travis.yml b/.travis.yml index 7d906134..778b15f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: elixir elixir: - 1.7 -addons: - postgresql: '9.4' +services: + - postgresql env: - MIX_ENV=test before_script: diff --git a/README.md b/README.md index 454744de..1a1abd48 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,8 @@ After much research and investigation, we decided to use a few *existing* ### What's In It For Me? -As a developer, _using_ this module you can _rest assured_ that +As a developer, _using_ this module you can _rest assured_ that: + + **all code** for **authentication** in _your_ app is **nicely contained & organized** in a ***single place***. + all the auth-related code is ***well documented, tested & maintained***. diff --git a/assets/.babelrc b/assets/.babelrc new file mode 100644 index 00000000..ce33b24d --- /dev/null +++ b/assets/.babelrc @@ -0,0 +1,5 @@ +{ + "presets": [ + "@babel/preset-env" + ] +} diff --git a/assets/brunch-config.js b/assets/brunch-config.js deleted file mode 100644 index c47e1ac5..00000000 --- a/assets/brunch-config.js +++ /dev/null @@ -1,65 +0,0 @@ -exports.config = { - // See http://brunch.io/#documentation for docs. - files: { - javascripts: { - joinTo: "js/app.js" - - // To use a separate vendor.js bundle, specify two files path - // http://brunch.io/docs/config#-files- - // joinTo: { - // "js/app.js": /^(web\/static\/js)/, - // "js/vendor.js": /^(web\/static\/vendor)|(deps)/ - // } - // - // To change the order of concatenation of files, explicitly mention here - // order: { - // before: [ - // "web/static/vendor/js/jquery-2.1.1.js", - // "web/static/vendor/js/bootstrap.min.js" - // ] - // } - }, - stylesheets: { - joinTo: "css/app.css", - order: { - after: ["css/app.css"] // concat app.css last - } - }, - templates: { - joinTo: "js/app.js" - } - }, - - conventions: { - // This option sets where we should place non-css and non-js assets in. - // By default, we set this to "/web/static/assets". Files in this directory - // will be copied to `paths.public`, which is "priv/static" by default. - assets: /^(web\/static\/assets)/ - }, - - // Phoenix paths configuration - paths: { - // Dependencies and current project directories to watch - watched: ["static", "css", "js"], - // Where to compile files to - public: "../priv/static" - }, - - // Configure your plugins - plugins: { - babel: { - // Do not use ES6 compiler in vendor code - ignore: [/vendor/] - } - }, - - modules: { - autoRequire: { - "js/app.js": ["js/app"] - } - }, - - npm: { - enabled: true - } -}; diff --git a/assets/css/app.css b/assets/css/app.css index 5314c34d..fec0b3fc 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -1 +1,3 @@ -/* This file is for your main application css. */ \ No newline at end of file +/* This file is for your main application css. */ + +@import "./phoenix.css"; diff --git a/assets/css/phoenix.css b/assets/css/phoenix.css new file mode 100644 index 00000000..e1ca940c --- /dev/null +++ b/assets/css/phoenix.css @@ -0,0 +1,134 @@ +/* Includes some default style for the starter application. + * This can be safely deleted to start fresh. + */ + +/* Milligram v1.3.0 https://milligram.github.io + * Copyright (c) 2017 CJ Patoilo Licensed under the MIT license + */ + +*,*:after,*:before{box-sizing:inherit}html{box-sizing:border-box;font-size:62.5%}body{color:#000000;font-family:'Helvetica', 'Arial', sans-serif;font-size:1.6em;font-weight:300;line-height:1.6}blockquote{border-left:0.3rem solid #d1d1d1;margin-left:0;margin-right:0;padding:1rem 1.5rem}blockquote *:last-child{margin-bottom:0}.button,button,input[type='button'],input[type='reset'],input[type='submit']{background-color:#0069d9;border:0.1rem solid #0069d9;border-radius:.4rem;color:#fff;cursor:pointer;display:inline-block;font-size:1.1rem;font-weight:700;height:3.8rem;letter-spacing:.1rem;line-height:3.8rem;padding:0 3.0rem;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap}.button:focus,.button:hover,button:focus,button:hover,input[type='button']:focus,input[type='button']:hover,input[type='reset']:focus,input[type='reset']:hover,input[type='submit']:focus,input[type='submit']:hover{background-color:#606c76;border-color:#606c76;color:#fff;outline:0}.button[disabled],button[disabled],input[type='button'][disabled],input[type='reset'][disabled],input[type='submit'][disabled]{cursor:default;opacity:.5}.button[disabled]:focus,.button[disabled]:hover,button[disabled]:focus,button[disabled]:hover,input[type='button'][disabled]:focus,input[type='button'][disabled]:hover,input[type='reset'][disabled]:focus,input[type='reset'][disabled]:hover,input[type='submit'][disabled]:focus,input[type='submit'][disabled]:hover{background-color:#0069d9;border-color:#0069d9}.button.button-outline,button.button-outline,input[type='button'].button-outline,input[type='reset'].button-outline,input[type='submit'].button-outline{background-color:transparent;color:#0069d9}.button.button-outline:focus,.button.button-outline:hover,button.button-outline:focus,button.button-outline:hover,input[type='button'].button-outline:focus,input[type='button'].button-outline:hover,input[type='reset'].button-outline:focus,input[type='reset'].button-outline:hover,input[type='submit'].button-outline:focus,input[type='submit'].button-outline:hover{background-color:transparent;border-color:#606c76;color:#606c76}.button.button-outline[disabled]:focus,.button.button-outline[disabled]:hover,button.button-outline[disabled]:focus,button.button-outline[disabled]:hover,input[type='button'].button-outline[disabled]:focus,input[type='button'].button-outline[disabled]:hover,input[type='reset'].button-outline[disabled]:focus,input[type='reset'].button-outline[disabled]:hover,input[type='submit'].button-outline[disabled]:focus,input[type='submit'].button-outline[disabled]:hover{border-color:inherit;color:#0069d9}.button.button-clear,button.button-clear,input[type='button'].button-clear,input[type='reset'].button-clear,input[type='submit'].button-clear{background-color:transparent;border-color:transparent;color:#0069d9}.button.button-clear:focus,.button.button-clear:hover,button.button-clear:focus,button.button-clear:hover,input[type='button'].button-clear:focus,input[type='button'].button-clear:hover,input[type='reset'].button-clear:focus,input[type='reset'].button-clear:hover,input[type='submit'].button-clear:focus,input[type='submit'].button-clear:hover{background-color:transparent;border-color:transparent;color:#606c76}.button.button-clear[disabled]:focus,.button.button-clear[disabled]:hover,button.button-clear[disabled]:focus,button.button-clear[disabled]:hover,input[type='button'].button-clear[disabled]:focus,input[type='button'].button-clear[disabled]:hover,input[type='reset'].button-clear[disabled]:focus,input[type='reset'].button-clear[disabled]:hover,input[type='submit'].button-clear[disabled]:focus,input[type='submit'].button-clear[disabled]:hover{color:#0069d9}code{background:#f4f5f6;border-radius:.4rem;font-size:86%;margin:0 .2rem;padding:.2rem .5rem;white-space:nowrap}pre{background:#f4f5f6;border-left:0.3rem solid #0069d9;overflow-y:hidden}pre>code{border-radius:0;display:block;padding:1rem 1.5rem;white-space:pre}hr{border:0;border-top:0.1rem solid #f4f5f6;margin:3.0rem 0}input[type='email'],input[type='number'],input[type='password'],input[type='search'],input[type='tel'],input[type='text'],input[type='url'],textarea,select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:transparent;border:0.1rem solid #d1d1d1;border-radius:.4rem;box-shadow:none;box-sizing:inherit;height:3.8rem;padding:.6rem 1.0rem;width:100%}input[type='email']:focus,input[type='number']:focus,input[type='password']:focus,input[type='search']:focus,input[type='tel']:focus,input[type='text']:focus,input[type='url']:focus,textarea:focus,select:focus{border-color:#0069d9;outline:0}select{background:url('data:image/svg+xml;utf8,') center right no-repeat;padding-right:3.0rem}select:focus{background-image:url('data:image/svg+xml;utf8,')}textarea{min-height:6.5rem}label,legend{display:block;font-size:1.6rem;font-weight:700;margin-bottom:.5rem}fieldset{border-width:0;padding:0}input[type='checkbox'],input[type='radio']{display:inline}.label-inline{display:inline-block;font-weight:normal;margin-left:.5rem}.row{display:flex;flex-direction:column;padding:0;width:100%}.row.row-no-padding{padding:0}.row.row-no-padding>.column{padding:0}.row.row-wrap{flex-wrap:wrap}.row.row-top{align-items:flex-start}.row.row-bottom{align-items:flex-end}.row.row-center{align-items:center}.row.row-stretch{align-items:stretch}.row.row-baseline{align-items:baseline}.row .column{display:block;flex:1 1 auto;margin-left:0;max-width:100%;width:100%}.row .column.column-offset-10{margin-left:10%}.row .column.column-offset-20{margin-left:20%}.row .column.column-offset-25{margin-left:25%}.row .column.column-offset-33,.row .column.column-offset-34{margin-left:33.3333%}.row .column.column-offset-50{margin-left:50%}.row .column.column-offset-66,.row .column.column-offset-67{margin-left:66.6666%}.row .column.column-offset-75{margin-left:75%}.row .column.column-offset-80{margin-left:80%}.row .column.column-offset-90{margin-left:90%}.row .column.column-10{flex:0 0 10%;max-width:10%}.row .column.column-20{flex:0 0 20%;max-width:20%}.row .column.column-25{flex:0 0 25%;max-width:25%}.row .column.column-33,.row .column.column-34{flex:0 0 33.3333%;max-width:33.3333%}.row .column.column-40{flex:0 0 40%;max-width:40%}.row .column.column-50{flex:0 0 50%;max-width:50%}.row .column.column-60{flex:0 0 60%;max-width:60%}.row .column.column-66,.row .column.column-67{flex:0 0 66.6666%;max-width:66.6666%}.row .column.column-75{flex:0 0 75%;max-width:75%}.row .column.column-80{flex:0 0 80%;max-width:80%}.row .column.column-90{flex:0 0 90%;max-width:90%}.row .column .column-top{align-self:flex-start}.row .column .column-bottom{align-self:flex-end}.row .column .column-center{-ms-grid-row-align:center;align-self:center}@media (min-width: 40rem){.row{flex-direction:row;margin-left:-1.0rem;width:calc(100% + 2.0rem)}.row .column{margin-bottom:inherit;padding:0 1.0rem}}a{color:#0069d9;text-decoration:none}a:focus,a:hover{color:#606c76}dl,ol,ul{list-style:none;margin-top:0;padding-left:0}dl dl,dl ol,dl ul,ol dl,ol ol,ol ul,ul dl,ul ol,ul ul{font-size:90%;margin:1.5rem 0 1.5rem 3.0rem}ol{list-style:decimal inside}ul{list-style:circle inside}.button,button,dd,dt,li{margin-bottom:1.0rem}fieldset,input,select,textarea{margin-bottom:1.5rem}blockquote,dl,figure,form,ol,p,pre,table,ul{margin-bottom:2.5rem}table{border-spacing:0;width:100%}td,th{border-bottom:0.1rem solid #e1e1e1;padding:1.2rem 1.5rem;text-align:left}td:first-child,th:first-child{padding-left:0}td:last-child,th:last-child{padding-right:0}b,strong{font-weight:bold}p{margin-top:0}h1,h2,h3,h4,h5,h6{font-weight:300;letter-spacing:-.1rem;margin-bottom:2.0rem;margin-top:0}h1{font-size:4.6rem;line-height:1.2}h2{font-size:3.6rem;line-height:1.25}h3{font-size:2.8rem;line-height:1.3}h4{font-size:2.2rem;letter-spacing:-.08rem;line-height:1.35}h5{font-size:1.8rem;letter-spacing:-.05rem;line-height:1.5}h6{font-size:1.6rem;letter-spacing:0;line-height:1.4}img{max-width:100%}.clearfix:after{clear:both;content:' ';display:table}.float-left{float:left}.float-right{float:right} + +/* General style */ +h1{font-size: 3.6rem; line-height: 1.25} +h2{font-size: 2.8rem; line-height: 1.3} +h3{font-size: 2.2rem; letter-spacing: -.08rem; line-height: 1.35} +h4{font-size: 1.8rem; letter-spacing: -.05rem; line-height: 1.5} +h5{font-size: 1.6rem; letter-spacing: 0; line-height: 1.4} +h6{font-size: 1.4rem; letter-spacing: 0; line-height: 1.2} + +.container{ + margin: 0 auto; + max-width: 80.0rem; + padding: 0 2.0rem; + position: relative; + width: 100% +} +select { + width: auto; +} + +/* Alerts and form errors */ +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert p { + margin-bottom: 0; +} +.alert:empty { + display: none; +} +.help-block { + color: #a94442; + display: block; + margin: -1rem 0 2rem; +} + +/* Phoenix promo and logo */ +.phx-hero { + text-align: center; + border-bottom: 1px solid #e3e3e3; + background: #eee; + border-radius: 6px; + padding: 3em; + margin-bottom: 3rem; + font-weight: 200; + font-size: 120%; +} +.phx-hero p { + margin: 0; +} +.phx-logo { + min-width: 300px; + margin: 1rem; + display: block; +} +.phx-logo img { + width: auto; + display: block; +} + +/* Headers */ +header { + width: 100%; + background: #fdfdfd; + border-bottom: 1px solid #eaeaea; + margin-bottom: 2rem; +} +header section { + align-items: center; + display: flex; + flex-direction: column; + justify-content: space-between; +} +header section :first-child { + order: 2; +} +header section :last-child { + order: 1; +} +header nav ul, +header nav li { + margin: 0; + padding: 0; + display: block; + text-align: right; + white-space: nowrap; +} +header nav ul { + margin: 1rem; + margin-top: 0; +} +header nav a { + display: block; +} + +@media (min-width: 40.0rem) { /* Small devices (landscape phones, 576px and up) */ + header section { + flex-direction: row; + } + header nav ul { + margin: 1rem; + } + .phx-logo { + flex-basis: 527px; + margin: 2rem 1rem; + } +} diff --git a/assets/js/app.js b/assets/js/app.js index e7549b9d..8a5d386f 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -1,21 +1,17 @@ -// Brunch automatically concatenates all files in your -// watched paths. Those paths can be configured at -// config.paths.watched in "brunch-config.js". -// -// However, those files will only be executed if -// explicitly imported. The only exception are files -// in vendor, which are never wrapped in imports and -// therefore are always executed. +// We need to import the CSS so that webpack will load it. +// The MiniCssExtractPlugin is used to separate it out into +// its own CSS file. +import css from "../css/app.css" +// webpack automatically bundles all modules in your +// entry points. Those entry points can be configured +// in "webpack.config.js". +// // Import dependencies // -// If you no longer want to use a dependency, remember -// to also remove its path from "config.paths.watched". import "phoenix_html" // Import local files // -// Local files can be imported directly using relative -// paths "./socket" or full ones "web/static/js/socket". - +// Local files can be imported directly using relative paths, for example: // import socket from "./socket" diff --git a/assets/js/socket.js b/assets/js/socket.js index 0f8d461f..09929abc 100644 --- a/assets/js/socket.js +++ b/assets/js/socket.js @@ -1,8 +1,11 @@ // NOTE: The contents of this file will only be executed if -// you uncomment its entry in "web/static/js/app.js". +// you uncomment its entry in "assets/js/app.js". -// To use Phoenix channels, the first step is to import Socket -// and connect at the socket path in "lib/my_app/endpoint.ex": +// To use Phoenix channels, the first step is to import Socket, +// and connect at the socket path in "lib/web/endpoint.ex". +// +// Pass the token on params as below. Or remove it +// from the params if you are not using authentication. import {Socket} from "phoenix" let socket = new Socket("/socket", {params: {token: window.userToken}}) @@ -13,7 +16,7 @@ let socket = new Socket("/socket", {params: {token: window.userToken}}) // If the current user exists you can assign the user's token in // the connection for use in the layout. // -// In your "web/router.ex": +// In your "lib/web/router.ex": // // pipeline :browser do // ... @@ -31,14 +34,14 @@ let socket = new Socket("/socket", {params: {token: window.userToken}}) // end // // Now you need to pass this token to JavaScript. You can do so -// inside a script tag in "web/templates/layout/app.html.eex": +// inside a script tag in "lib/web/templates/layout/app.html.eex": // // // -// You will need to verify the user token in the "connect/2" function -// in "web/channels/user_socket.ex": +// You will need to verify the user token in the "connect/3" function +// in "lib/web/channels/user_socket.ex": // -// def connect(%{"token" => token}, socket) do +// def connect(%{"token" => token}, socket, _connect_info) do // # max_age: 1209600 is equivalent to two weeks in seconds // case Phoenix.Token.verify(socket, "user socket", token, max_age: 1209600) do // {:ok, user_id} -> @@ -48,9 +51,7 @@ let socket = new Socket("/socket", {params: {token: window.userToken}}) // end // end // -// Finally, pass the token on connect as below. Or remove it -// from connect if you don't care about authentication. - +// Finally, connect to the socket: socket.connect() // Now that you are connected, you can join channels with a topic: diff --git a/assets/package.json b/assets/package.json index 8cb3dc7c..cd2bb9a7 100644 --- a/assets/package.json +++ b/assets/package.json @@ -2,19 +2,23 @@ "repository": {}, "license": "MIT", "scripts": { - "deploy": "brunch build --production", - "watch": "brunch watch --stdin" + "deploy": "webpack --mode production", + "watch": "webpack --mode development --watch" }, "dependencies": { "phoenix": "file:../deps/phoenix", "phoenix_html": "file:../deps/phoenix_html" }, "devDependencies": { - "babel-brunch": "~6.0.0", - "brunch": "2.7.4", - "clean-css-brunch": "~2.0.0", - "css-brunch": "~2.0.0", - "javascript-brunch": "~2.0.0", - "uglify-js-brunch": "~2.0.1" + "@babel/core": "^7.0.0", + "@babel/preset-env": "^7.0.0", + "babel-loader": "^8.0.0", + "copy-webpack-plugin": "^5.1.1", + "css-loader": "^3.4.2", + "mini-css-extract-plugin": "^0.9.0", + "optimize-css-assets-webpack-plugin": "^5.0.1", + "terser-webpack-plugin": "^2.3.2", + "webpack": "4.41.5", + "webpack-cli": "^3.3.2" } } diff --git a/assets/webpack.config.js b/assets/webpack.config.js new file mode 100644 index 00000000..4569a84f --- /dev/null +++ b/assets/webpack.config.js @@ -0,0 +1,41 @@ +const path = require('path'); +const glob = require('glob'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const TerserPlugin = require('terser-webpack-plugin'); +const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); + +module.exports = (env, options) => ({ + optimization: { + minimizer: [ + new TerserPlugin({ cache: true, parallel: true, sourceMap: false }), + new OptimizeCSSAssetsPlugin({}) + ] + }, + entry: { + './js/app.js': glob.sync('./vendor/**/*.js').concat(['./js/app.js']) + }, + output: { + filename: 'app.js', + path: path.resolve(__dirname, '../priv/static/js') + }, + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + use: { + loader: 'babel-loader' + } + }, + { + test: /\.css$/, + use: [MiniCssExtractPlugin.loader, 'css-loader'] + } + ] + }, + plugins: [ + new MiniCssExtractPlugin({ filename: '../css/app.css' }), + new CopyWebpackPlugin([{ from: 'static/', to: '../' }]) + ] +}); diff --git a/config/config.exs b/config/config.exs index bdb1909b..56e2e137 100644 --- a/config/config.exs +++ b/config/config.exs @@ -3,46 +3,28 @@ # # This configuration file is loaded before any dependency and # is restricted to this project. -use Mix.Config # General application configuration +use Mix.Config + config :auth, ecto_repos: [Auth.Repo] # Configures the endpoint config :auth, AuthWeb.Endpoint, url: [host: "localhost"], - secret_key_base: "jUgd/S8tmMjbhzawyKI8ns2C3RbS04n0ClDVrLa7KjHLgDfcFJ6FS60BhvWBw/cg", + secret_key_base: "dGzC3qel+EMmLNQQPhQphV5vdNjODKh9IGCbL8j2lipAwuoAWeysUTtfLDsYVlq7", render_errors: [view: AuthWeb.ErrorView, accepts: ~w(html json)], - pubsub: [name: Auth.PubSub, adapter: Phoenix.PubSub.PG2] + pubsub: [name: Auth.PubSub, adapter: Phoenix.PubSub.PG2], + live_view: [signing_salt: "G+UI6RIv"] # Configures Elixir's Logger config :logger, :console, format: "$time $metadata[$level] $message\n", metadata: [:request_id] -# Configure Ueberauth providers -# config :ueberauth, Ueberauth, -# providers: [ -# github: {Ueberauth.Strategy.Github, []} -# ] - -# config :ueberauth, Ueberauth.Strategy.Github.OAuth, -# client_id: System.get_env("GITHUB_CLIENT_ID"), -# client_secret: System.get_env("GITHUB_CLIENT_SECRET") - -# Configure email via AWS SES -config :auth, Auth.Mailer, - adapter: Bamboo.SMTPAdapter, - server: System.get_env("SES_SERVER"), - port: System.get_env("SES_PORT"), - username: System.get_env("SMTP_USERNAME"), - password: System.get_env("SMTP_PASSWORD"), - # can be `:always` or `:never` - tls: :always, - # can be `true` - ssl: false, - retries: 1 +# Use Jason for JSON parsing in Phoenix +config :phoenix, :json_library, Jason # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. diff --git a/config/dev.exs b/config/dev.exs index 54609aa1..e45f2aa3 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -1,11 +1,20 @@ use Mix.Config +# Configure your database +config :auth, Auth.Repo, + username: "postgres", + password: "postgres", + database: "auth_dev", + hostname: "localhost", + show_sensitive_data_on_connection_error: true, + pool_size: 10 + # For development, we disable any cache and enable # debugging and code reloading. # # The watchers configuration can be used to run external # watchers to your application. For example, we use it -# with brunch.io to recompile .js and .css sources. +# with webpack to recompile .js and .css sources. config :auth, AuthWeb.Endpoint, http: [port: 4000], debug_errors: true, @@ -13,21 +22,46 @@ config :auth, AuthWeb.Endpoint, check_origin: false, watchers: [ node: [ - "node_modules/brunch/bin/brunch", - "watch", - "--stdin", + "node_modules/webpack/bin/webpack.js", + "--mode", + "development", + "--watch-stdin", cd: Path.expand("../assets", __DIR__) ] ] +# ## SSL Support +# +# In order to use HTTPS in development, a self-signed +# certificate can be generated by running the following +# Mix task: +# +# mix phx.gen.cert +# +# Note that this task requires Erlang/OTP 20 or later. +# Run `mix help phx.gen.cert` for more information. +# +# The `http:` config above can be replaced with: +# +# https: [ +# port: 4001, +# cipher_suite: :strong, +# keyfile: "priv/cert/selfsigned_key.pem", +# certfile: "priv/cert/selfsigned.pem" +# ], +# +# If desired, both `http:` and `https:` keys can be +# configured to run both http and https servers on +# different ports. + # Watch static and templates for browser reloading. config :auth, AuthWeb.Endpoint, live_reload: [ patterns: [ - ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$}, - ~r{priv/gettext/.*(po)$}, - ~r{web/views/.*(ex)$}, - ~r{web/templates/.*(eex)$} + ~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$", + ~r"priv/gettext/.*(po)$", + ~r"lib/auth_web/(live|views)/.*(ex)$", + ~r"lib/auth_web/templates/.*(eex)$" ] ] @@ -38,10 +72,5 @@ config :logger, :console, format: "[$level] $message\n" # in production as building large stacktraces may be expensive. config :phoenix, :stacktrace_depth, 20 -# Configure your database -config :auth, Auth.Repo, - username: "postgres", - password: "postgres", - database: "auth_dev", - hostname: "localhost", - pool_size: 10 +# Initialize plugs at runtime for faster development compilation +config :phoenix, :plug_init_mode, :runtime diff --git a/config/prod.exs b/config/prod.exs index 6c369ff9..a0d98b03 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -1,20 +1,17 @@ use Mix.Config -# For production, we configure the host to read the PORT -# from the system environment. Therefore, you will need -# to set PORT=80 before running your server. +# For production, don't forget to configure the url host +# to something meaningful, Phoenix uses this information +# when generating URLs. # -# You should also configure the url host to something -# meaningful, we use this information when generating URLs. -# -# Finally, we also include the path to a manifest +# Note we also include the path to a cache manifest # containing the digested version of static files. This -# manifest is generated by the mix phoenix.digest task -# which you typically run after static files are built. +# manifest is generated by the `mix phx.digest` task, +# which you should run after static files are built and +# before starting your production server. config :auth, AuthWeb.Endpoint, - http: [port: {:system, "PORT"}], url: [host: "example.com", port: 80], - cache_static_manifest: "priv/static/manifest.json" + cache_static_manifest: "priv/static/cache_manifest.json" # Do not print debug messages in production config :logger, level: :info @@ -24,38 +21,35 @@ config :logger, level: :info # To get SSL working, you will need to add the `https` key # to the previous section and set your `:url` port to 443: # -# config :auth, Auth.Endpoint, +# config :auth, AuthWeb.Endpoint, # ... # url: [host: "example.com", port: 443], -# https: [port: 443, -# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), -# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")] -# -# Where those two env variables return an absolute path to -# the key and cert in disk or a relative path inside priv, -# for example "priv/ssl/server.key". -# -# We also recommend setting `force_ssl`, ensuring no data is -# ever sent via http, always redirecting to https: -# -# config :auth, Auth.Endpoint, +# https: [ +# port: 443, +# cipher_suite: :strong, +# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), +# certfile: System.get_env("SOME_APP_SSL_CERT_PATH"), +# transport_options: [socket_opts: [:inet6]] +# ] +# +# The `cipher_suite` is set to `:strong` to support only the +# latest and more secure SSL ciphers. This means old browsers +# and clients may not be supported. You can set it to +# `:compatible` for wider support. +# +# `:keyfile` and `:certfile` expect an absolute path to the key +# and cert in disk or a relative path inside priv, for example +# "priv/ssl/server.key". For all supported SSL configuration +# options, see https://hexdocs.pm/plug/Plug.SSL.html#configure/1 +# +# We also recommend setting `force_ssl` in your endpoint, ensuring +# no data is ever sent via http, always redirecting to https: +# +# config :auth, AuthWeb.Endpoint, # force_ssl: [hsts: true] # # Check `Plug.SSL` for all available options in `force_ssl`. -# ## Using releases -# -# If you are doing OTP releases, you need to instruct Phoenix -# to start the server for all endpoints: -# -# config :phoenix, :serve_endpoints, true -# -# Alternatively, you can configure exactly which server to -# start per endpoint: -# -# config :auth, Auth.Endpoint, server: true -# - -# Finally import the config/prod.secret.exs -# which should be versioned separately. -# import_config "prod.secret.exs" +# Finally import the config/prod.secret.exs which loads secrets +# and configuration from environment variables. +import_config "prod.secret.exs" diff --git a/config/test.exs b/config/test.exs index 77bac8e8..e601d5ef 100644 --- a/config/test.exs +++ b/config/test.exs @@ -1,14 +1,5 @@ use Mix.Config -# We don't run a server during test. If one is required, -# you can enable the server option below. -config :auth, AuthWeb.Endpoint, - http: [port: 4001], - server: false - -# Print only warnings and errors during test -config :logger, level: :warn - # Configure your database config :auth, Auth.Repo, username: "postgres", @@ -16,3 +7,12 @@ config :auth, Auth.Repo, database: "auth_test", hostname: "localhost", pool: Ecto.Adapters.SQL.Sandbox + +# We don't run a server during test. If one is required, +# you can enable the server option below. +config :auth, AuthWeb.Endpoint, + http: [port: 4002], + server: false + +# Print only warnings and errors during test +config :logger, level: :warn diff --git a/lib/auth.ex b/lib/auth.ex index 83e7c89a..b24e2051 100644 --- a/lib/auth.ex +++ b/lib/auth.ex @@ -1,46 +1,9 @@ -defmodule Auth.Auth do - alias Auth.User - - def validate_user(uid, %{other: %{password: pw, password_confirmation: nil}}), - do: validate(uid, pw) - - def validate_user(uid, %{other: %{password: pw, password_confirmation: pw}}), - do: validate(uid, pw) - - def validate_user(uid, _), do: {:error, "passwords do not match"} - - def validate(uid, password) do - IO.inspect(:application.get_application(CsGuide.Repo)) - - get_by_uid = - case Application.get_env(:auth, Auth) do - nil -> :email_hash - config -> Keyword.get(config, :identity) |> Keyword.get(:uid_field) - end - |> (fn - nil -> Keyword.put([], :email_hash, uid) - uid_field -> Keyword.put([], uid_field, uid) - end).() - - with %User{} = user <- User.get_by(get_by_uid), - true <- Argon2.verify_pass(password, user.password) do - {:ok, user} - else - nil -> - with _ <- Argon2.no_user_verify() do - IO.inspect("no user") - {:error, "invalid credentials"} - end - - false -> - IO.inspect("bad pw") - {:error, "invalid credentials"} - end - end - - def login(conn, user) do - conn - |> Plug.Conn.put_session(:user_id, user.entry_id) - |> Plug.Conn.configure_session(renew: true) - end +defmodule Auth do + @moduledoc """ + Auth keeps the contexts that define your domain + and business logic. + + Contexts are also responsible for managing your data, regardless + if it comes from the database, an external API or others. + """ end diff --git a/lib/auth/application.ex b/lib/auth/application.ex index 6f1b3523..01f6b037 100644 --- a/lib/auth/application.ex +++ b/lib/auth/application.ex @@ -1,22 +1,22 @@ defmodule Auth.Application do + # See https://hexdocs.pm/elixir/Application.html + # for more information on OTP Applications + @moduledoc false + use Application - # See http://elixir-lang.org/docs/stable/elixir/Application.html - # for more information on OTP Applications def start(_type, _args) do - import Supervisor.Spec - - # Define workers and child supervisors to be supervised + # List all child processes to be supervised children = [ # Start the Ecto repository - supervisor(Auth.Repo, []), + Auth.Repo, # Start the endpoint when the application starts - supervisor(AuthWeb.Endpoint, []) - # Start your own worker by calling: Auth.Worker.start_link(arg1, arg2, arg3) - # worker(Auth.Worker, [arg1, arg2, arg3]), + AuthWeb.Endpoint + # Starts a worker by calling: Auth.Worker.start_link(arg) + # {Auth.Worker, arg}, ] - # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html + # See https://hexdocs.pm/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: Auth.Supervisor] Supervisor.start_link(children, opts) diff --git a/lib/auth/mailer.ex b/lib/auth/mailer.ex deleted file mode 100644 index 5839f4fe..00000000 --- a/lib/auth/mailer.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule Auth.Mailer do - use Bamboo.Mailer, otp_app: :auth -end diff --git a/lib/auth/repo.ex b/lib/auth/repo.ex index 9a95fd29..d2c26ac4 100644 --- a/lib/auth/repo.ex +++ b/lib/auth/repo.ex @@ -1,3 +1,5 @@ defmodule Auth.Repo do - use Ecto.Repo, otp_app: :auth, adapter: Ecto.Adapters.Postgres + use Ecto.Repo, + otp_app: :auth, + adapter: Ecto.Adapters.Postgres end diff --git a/lib/auth/setup.ex b/lib/auth/setup.ex deleted file mode 100644 index 44cf6199..00000000 --- a/lib/auth/setup.ex +++ /dev/null @@ -1,25 +0,0 @@ -defmodule Auth.Setup do - # Should run on code compilation - - # Reads config from app and - # sets up config for nested dependencies - # Code.compile_quoted( - # quote do - # Application.put_env(:ueberauth, Ueberauth, - # providers: [ - # github: {Ueberauth.Strategy.Github, []}, - # identity: - # {Ueberauth.Strategy.Identity, - # [ - # callback_methods: ["POST"], - # uid_field: :email, - # nickname_field: :email - # ]} - # ] - # ) - # - # Application.put_env(:alog, Alog, - # repo: Application.get_env(:auth, Auth) |> Keyword.get(:repo) - # ) - # end - # ) -end diff --git a/lib/auth/user/user.ex b/lib/auth/user/user.ex deleted file mode 100644 index 4892ad00..00000000 --- a/lib/auth/user/user.ex +++ /dev/null @@ -1,66 +0,0 @@ -defmodule Auth.User do - use Ecto.Schema - use Alog - import Ecto.Changeset - - schema "users" do - field(:email, Fields.EmailEncrypted) - field(:email_hash, Fields.EmailHash) - field(:email_plaintext, Fields.EmailPlaintext, virtual: true) - field(:password, Fields.Password) - field(:entry_id, :string) - field(:deleted, :boolean, default: false) - field(:admin, :boolean, default: false) - field(:name, :string, virtual: true) - field(:username, :string, virtual: true) - - timestamps() - end - - @doc false - def changeset(user, attrs \\ %{}) do - user - |> cast(attrs, [:email, :password, :admin]) - |> validate_required([:email]) - |> put_email_hash() - |> unique_constraint(:email_hash) - end - - def put_email_hash(user) do - case get_change(user, :email) do - nil -> user - email -> put_change(user, :email_hash, email) - end - end - - def registration_changeset(model, params) do - model - |> changeset(params) - |> cast(params, ~w(password)) - |> validate_length(:password, min: 6, max: 100) - |> put_pass_hash() - end - - # register with only email address - def register_changeset(model, params \\ :empty) do - IO.puts("register_changeset called") - IO.inspect(params) - - model - |> IO.inspect() - |> cast(params, ~w(username name)) - # |> validate_required([:username]) - |> changeset(params) - end - - defp put_pass_hash(changeset) do - case changeset do - %Ecto.Changeset{valid?: true, changes: %{password: pass}} -> - put_change(changeset, :password_hash, Comeonin.Bcrypt.hashpwsalt(pass)) - - # that underscore matches the error case - _ -> - changeset - end - end -end diff --git a/lib/auth/user/user_from_auth.ex b/lib/auth/user/user_from_auth.ex deleted file mode 100644 index 8e4523f0..00000000 --- a/lib/auth/user/user_from_auth.ex +++ /dev/null @@ -1,48 +0,0 @@ -defmodule UserFromAuth do - @moduledoc """ - Retrieve the user information from an auth request - """ - - alias Ueberauth.Auth - - def find_or_create(%Auth{provider: :identity} = auth) do - case validate_pass(auth.credentials) do - :ok -> - {:ok, basic_info(auth)} - {:error, reason} -> {:error, reason} - end - end - - def find_or_create(%Auth{} = auth) do - {:ok, basic_info(auth)} - end - - defp basic_info(auth) do - %{id: auth.uid, name: name_from_auth(auth), avatar: auth.info.image} - end - - defp name_from_auth(auth) do - if auth.info.name do - auth.info.name - else - name = [auth.info.first_name, auth.info.last_name] - |> Enum.filter(&(&1 != nil and &1 != "")) - - cond do - length(name) == 0 -> auth.info.nickname - true -> Enum.join(name, " ") - end - end - end - - defp validate_pass(%{other: %{password: ""}}) do - {:error, "Password required"} - end - defp validate_pass(%{other: %{password: pw, password_confirmation: pw}}) do - :ok - end - defp validate_pass(%{other: %{password: _}}) do - {:error, "Passwords do not match"} - end - defp validate_pass(_), do: {:error, "Password Required"} -end diff --git a/lib/auth_web.ex b/lib/auth_web.ex index 9dc178c3..5c0cb570 100644 --- a/lib/auth_web.ex +++ b/lib/auth_web.ex @@ -17,16 +17,6 @@ defmodule AuthWeb do and import those modules here. """ - def model do - quote do - use Ecto.Schema - - import Ecto - import Ecto.Changeset - import Ecto.Query - end - end - def controller do quote do use Phoenix.Controller, namespace: AuthWeb diff --git a/lib/auth_web/channels/user_socket.ex b/lib/auth_web/channels/user_socket.ex index 4fb25219..eaf704c7 100644 --- a/lib/auth_web/channels/user_socket.ex +++ b/lib/auth_web/channels/user_socket.ex @@ -2,7 +2,7 @@ defmodule AuthWeb.UserSocket do use Phoenix.Socket ## Channels - # channel "room:*", Auth.RoomChannel + # channel "room:*", AuthWeb.RoomChannel # Socket params are passed from the client and can # be used to verify and authenticate a user. After @@ -15,18 +15,18 @@ defmodule AuthWeb.UserSocket do # # See `Phoenix.Token` documentation for examples in # performing token verification on connect. - def connect(_params, socket) do + def connect(_params, socket, _connect_info) do {:ok, socket} end # Socket id's are topics that allow you to identify all sockets for a given user: # - # def id(socket), do: "users_socket:#{socket.assigns.user_id}" + # def id(socket), do: "user_socket:#{socket.assigns.user_id}" # # Would allow you to broadcast a "disconnect" event and terminate # all active sockets and channels for a given user: # - # Auth.Endpoint.broadcast("users_socket:#{user.id}", "disconnect", %{}) + # AuthWeb.Endpoint.broadcast("user_socket:#{user.id}", "disconnect", %{}) # # Returning `nil` makes this socket anonymous. def id(_socket), do: nil diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex deleted file mode 100644 index f50b5814..00000000 --- a/lib/auth_web/controllers/auth_controller.ex +++ /dev/null @@ -1,82 +0,0 @@ -defmodule AuthWeb.AuthController do - use AuthWeb, :controller - import Plug.Conn - import Comeonin.Bcrypt, only: [checkpw: 2] - plug(Ueberauth) - # plug :authenticate_user when action in [:index, :show] - alias Auth.User - alias Ueberauth.Strategy.Helpers - - def callback(%{assigns: %{ueberauth_failure: _fails}} = conn, _params) do - conn - |> IO.inspect() - |> put_flash(:info, "Successfully authenticated.") - |> redirect(to: "/") - end - - def callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do - case UserFromAuth.find_or_create(auth) do - {:ok, user} -> - conn - |> IO.inspect() - |> put_flash(:info, "sucessfully authenticated.") - |> put_session(:current_user, user) - |> redirect(to: "/") - - {:error, reason} -> - conn - |> put_flash(:error, reason) - |> redirect(to: "/") - end - end - - def identity_callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do - case Auth.Auth.validate_user(auth.uid, auth.credentials) do - {:ok, user} -> - conn - |> Auth.login(user) - |> (fn c -> - case user.admin do - true -> redirect(c, to: "/admin") - false -> redirect(c, to: "/") - end - end).() - - err -> - redirect(conn, to: "/auth/identity") - end - end - - def login(conn, user) do - conn - |> assign(:current_user, user) - |> put_session(:user_id, user.id) - |> configure_session(renew: true) - end - - def delete(conn, _params) do - conn - |> put_flash(:info, "You have been logged out!") - |> configure_session(drop: true) - |> redirect(to: "/") - end - - def request(conn, _params) do - IO.inspect(conn) - IO.inspect(Helpers.callback_url(conn)) - render(conn, "request.html", callback_url: Helpers.callback_url(conn)) - end - - import Phoenix.Controller - - def authenticate_user(conn, _opts) do - if conn.assigns.current_user do - conn - else - conn - |> put_flash(:error, "You must be logged in to access that page") - |> redirect(to: Routes.page_path(conn, :index)) - |> halt() - end - end -end diff --git a/lib/auth_web/controllers/email_controller.ex b/lib/auth_web/controllers/email_controller.ex deleted file mode 100644 index 6b2a3185..00000000 --- a/lib/auth_web/controllers/email_controller.ex +++ /dev/null @@ -1,18 +0,0 @@ -defmodule AuthWeb.EmailController do - use AuthWeb, :controller - - def index(conn, _params) do - render(conn, "index.html") - end - - def create(conn, %{ - "email" => %{"email_from" => email_from, "subject" => subject, "message" => message} - }) do - Auth.Email.send_test_email(email_from, subject, message) - |> Auth.Mailer.deliver_now() - - conn - |> put_flash(:info, "Email Sent") - |> redirect(to: Routes.email_path(conn, :index)) - end -end diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index 70f956ff..b745074f 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -1,11 +1,7 @@ defmodule AuthWeb.PageController do - @moduledoc """ - Static page controller - """ - use AuthWeb, :controller def index(conn, _params) do - render(conn, "index.html", current_user: get_session(conn, :current_user)) + render(conn, "index.html") end end diff --git a/lib/auth_web/controllers/send_email_controller.ex b/lib/auth_web/controllers/send_email_controller.ex deleted file mode 100644 index 19246158..00000000 --- a/lib/auth_web/controllers/send_email_controller.ex +++ /dev/null @@ -1,14 +0,0 @@ -defmodule AuthWeb.HtmlEmailController do - use AuthWeb, :controller - - def index(conn, _params) do - render(conn, "index.html") - end - - def html_email(conn, %{"email" => email}) do - Auth.Email.send_test_html_email(email, "Test email subject", "www.dwyl.com") - |> Auth.Mailer.deliver_now() - - render(conn, "index.html") - end -end diff --git a/lib/auth_web/controllers/user_controller.ex b/lib/auth_web/controllers/user_controller.ex deleted file mode 100644 index a4358142..00000000 --- a/lib/auth_web/controllers/user_controller.ex +++ /dev/null @@ -1,62 +0,0 @@ -defmodule AuthWeb.UserController do - use AuthWeb, :controller - # plug :authenticate_user when action in [:index, :show] - alias Auth.{User, Repo} - - def index(conn, _params) do - users = Repo.all(User) - render(conn, "index.html", users: users) - end - - def show(conn, %{"id" => id}) do - user = Repo.get(User, id) - render(conn, "show.html", user: user) - end - - def new(conn, _params) do - changeset = User.changeset(%User{}) - render(conn, "new.html", changeset: changeset) - end - - def create(conn, %{"user" => user_params}) do - IO.inspect(user_params) - changeset = User.registration_changeset(%User{}, user_params) - - case Repo.insert(changeset) do - {:ok, user} -> - conn - |> AuthWeb.AuthController.login(user) - |> put_flash(:info, "#{user.name} created!") - |> redirect(to: Routes.user_path(conn, :index)) - - {:error, changeset} -> - render(conn, "new.html", changeset: changeset) - end - end - - def register(conn, %{"user" => user_params}) do - IO.puts(">>>> UserController.register called") - email = user_params["email"] - IO.inspect(">>>>> username: " <> email) - user_params = Map.put(user_params, "name", Enum.at(String.split(email, "@"), 0)) - changeset = User.register_changeset(%User{}, user_params) - # render(conn, "register.html", changeset: changeset) - case Repo.insert(changeset) do - {:ok, user} -> - conn - |> AuthWeb.AuthController.login(user) - |> put_flash(:info, "#{user.name} created!") - |> redirect(to: Routes.page_path(conn, :index)) - - {:error, changeset} -> - IO.inspect(changeset) - render(conn, "register.html", changeset: changeset) - end - end - - # render the registration template/partial without any data - def register(conn, _params) do - changeset = User.changeset(%User{}) - render(conn, "register.html", changeset: changeset) - end -end diff --git a/lib/auth_web/endpoint.ex b/lib/auth_web/endpoint.ex index 140ff525..4e5ca7d8 100644 --- a/lib/auth_web/endpoint.ex +++ b/lib/auth_web/endpoint.ex @@ -1,50 +1,47 @@ defmodule AuthWeb.Endpoint do use Phoenix.Endpoint, otp_app: :auth - socket("/socket", AuthWeb.UserSocket, + # The session will be stored in the cookie and signed, + # this means its contents can be read but not tampered with. + # Set :encryption_salt if you would also like to encrypt it. + @session_options [ + store: :cookie, + key: "_auth_key", + signing_salt: "aDYyYPIr" + ] + + socket "/socket", AuthWeb.UserSocket, websocket: true, longpoll: false - ) # Serve at "/" the static files from "priv/static" directory. # # You should set gzip to true if you are running phx.digest # when deploying your static files in production. - plug(Plug.Static, + plug Plug.Static, at: "/", from: :auth, gzip: false, only: ~w(css fonts images js favicon.ico robots.txt) - ) # Code reloading can be explicitly enabled under the # :code_reloader configuration of your endpoint. if code_reloading? do - socket("/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket) - plug(Phoenix.LiveReloader) - plug(Phoenix.CodeReloader) + socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket + plug Phoenix.LiveReloader + plug Phoenix.CodeReloader end - plug(Plug.RequestId) - plug(Plug.Logger) + plug Plug.RequestId + plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] - plug(Plug.Parsers, + plug Plug.Parsers, parsers: [:urlencoded, :multipart, :json], pass: ["*/*"], json_decoder: Phoenix.json_library() - ) - - plug(Plug.MethodOverride) - plug(Plug.Head) - - # The session will be stored in the cookie and signed, - # this means its contents can be read but not tampered with. - # Set :encryption_salt if you would also like to encrypt it. - plug(Plug.Session, - store: :cookie, - key: "_auth_key", - signing_salt: "BEgfYz/m" - ) - plug(AuthWeb.Router) + plug Plug.MethodOverride + plug Plug.Head + plug Plug.Session, @session_options + plug AuthWeb.Router end diff --git a/lib/auth_web/gettext.ex b/lib/auth_web/gettext.ex index 29623297..5b80a92c 100644 --- a/lib/auth_web/gettext.ex +++ b/lib/auth_web/gettext.ex @@ -5,18 +5,18 @@ defmodule AuthWeb.Gettext do By using [Gettext](https://hexdocs.pm/gettext), your module gains a set of macros for translations, for example: - import Auth.Gettext + import AuthWeb.Gettext # Simple translation - gettext "Here is the string to translate" + gettext("Here is the string to translate") # Plural translation - ngettext "Here is the string to translate", + ngettext("Here is the string to translate", "Here are the strings to translate", - 3 + 3) # Domain-based translation - dgettext "errors", "Here is the error message to translate" + dgettext("errors", "Here is the error message to translate") See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage. """ diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index d263fe80..c75601ab 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -1,43 +1,26 @@ defmodule AuthWeb.Router do use AuthWeb, :router - require Ueberauth pipeline :browser do - plug(:accepts, ["html"]) - plug(:fetch_session) - plug(:fetch_flash) - plug(:protect_from_forgery) - plug(:put_secure_browser_headers) + plug :accepts, ["html"] + plug :fetch_session + plug :fetch_flash + plug :protect_from_forgery + plug :put_secure_browser_headers end pipeline :api do - plug(:accepts, ["json"]) + plug :accepts, ["json"] end scope "/", AuthWeb do - pipe_through(:browser) - get("/register", UserController, :register) - post("/register", UserController, :register) - get("/:provider", AuthController, :request) - post("/identity/callback", AuthController, :identity_callback) - get("/:provider/callback", AuthController, :callback) - post("/:provider/callback", AuthController, :callback) - delete("/logout", AuthController, :delete) - end - - scope "/", AuthWeb do - # Use the default browser stack - pipe_through(:browser) + pipe_through :browser - get("/", PageController, :index) - get("/html-email", HtmlEmailController, :index) - post("/html-email", HtmlEmailController, :html_email) - resources("/email", EmailController, only: [:index, :create]) - resources("/user", UserController, only: [:index, :create, :new, :show]) + get "/", PageController, :index end # Other scopes may use custom stacks. - # scope "/api", Auth do + # scope "/api", AuthWeb do # pipe_through :api # end end diff --git a/lib/auth_web/templates/auth/request.html.eex b/lib/auth_web/templates/auth/request.html.eex deleted file mode 100644 index 1b5ffa45..00000000 --- a/lib/auth_web/templates/auth/request.html.eex +++ /dev/null @@ -1,16 +0,0 @@ -

Login

-<%= form_tag @callback_url, method: "post" do %> -
- - -
- -
- - -
- -
- -
-<% end %> diff --git a/lib/auth_web/templates/email/index.html.eex b/lib/auth_web/templates/email/index.html.eex deleted file mode 100644 index 6079cc37..00000000 --- a/lib/auth_web/templates/email/index.html.eex +++ /dev/null @@ -1,16 +0,0 @@ -<%= form_for @conn, Routes.email_path(@conn, :create), [as: :email], fn f -> %> -
Fill in the fields to send an email
-
- <%= label f, "Email address", class: "control-label" %> - <%= text_input f, :email_from, placeholder: "email@example.com", class: "form-control" %> -
-
- <%= label f, :subject, class: "control-label" %> - <%= text_input f, :subject, class: "form-control" %> -
-
- <%= label f, :message, class: "control-label" %> - <%= text_input f, :message, class: "form-control" %> -
- <%= submit "Send", class: "btn btn-primary" %> -<% end %> diff --git a/lib/auth_web/templates/html_email/email.html.eex b/lib/auth_web/templates/html_email/email.html.eex deleted file mode 100644 index 07437a2f..00000000 --- a/lib/auth_web/templates/html_email/email.html.eex +++ /dev/null @@ -1,20 +0,0 @@ -
-
- dwyl logo -
-

- Please confirm your email address -

-

- As an extra security measure, please verify this is the correct email - address linked to your account by clicking the button below. -

- -
diff --git a/lib/auth_web/templates/html_email/index.html.eex b/lib/auth_web/templates/html_email/index.html.eex deleted file mode 100644 index 6ff045dc..00000000 --- a/lib/auth_web/templates/html_email/index.html.eex +++ /dev/null @@ -1,11 +0,0 @@ -
-

Send an email which contains a html template:

- - <%= form_for @conn, Routes.html_email_path(@conn, :html_email), fn f -> %> -
- <%= label f, "Email address", class: "control-label" %> - <%= text_input f, :email %> -
- <%= submit "Send Html Email", class: "btn btn-primary" %> - <% end %> -
diff --git a/lib/auth_web/templates/layout/app.html.eex b/lib/auth_web/templates/layout/app.html.eex index ee88b20f..4968fd3f 100644 --- a/lib/auth_web/templates/layout/app.html.eex +++ b/lib/auth_web/templates/layout/app.html.eex @@ -1,47 +1,31 @@ - - - - - - - Authentication Example - - - "> + + + + <%= assigns[:page_title] || "Auth · Phoenix Framework" %> + "/> + <%= csrf_meta_tag() %> - -
- -<% # Alerts for Info/Errors using Tachyons see: https://git.io/vSMY7 - info = get_flash(@conn, :info) - error = get_flash(@conn, :error) -%> -<%= if info do %> - -<% end %> -<%= if error do %> - -<% end %> - - - <%= render @view_module, @view_template, assigns %> - -
- - - - Fork me on GitHub - +
+
+ + +
+
+
+ + + <%= render @view_module, @view_template, assigns %> +
+ diff --git a/lib/auth_web/templates/page/index.html.eex b/lib/auth_web/templates/page/index.html.eex index 5800a412..8cbd9d83 100644 --- a/lib/auth_web/templates/page/index.html.eex +++ b/lib/auth_web/templates/page/index.html.eex @@ -1,45 +1,35 @@ +
+

<%= gettext "Welcome to %{name}!", name: "Phoenix" %>

+

A productive web framework that
does not compromise speed or maintainability.

+
-

Phoenix
Authentication
Example

-

-This is an application to show an example of how to wire up -Authentication with -Phoenix. -

- -<%= if @current_user do %> -

Welcome, <%= @current_user.name %>!

- - <%= link "Logout", to: Routes.auth_path(@conn, :delete), method: :delete, class: "btn btn-danger" %> -
-<% else %> -
    - -
- - - Sign in with GitHub - - - - Sign in with Google - - - - Login with Email Address - - - -<% end %> +
+ + +
diff --git a/lib/auth_web/templates/user/register.html.eex b/lib/auth_web/templates/user/register.html.eex deleted file mode 100644 index ae5f4df4..00000000 --- a/lib/auth_web/templates/user/register.html.eex +++ /dev/null @@ -1,17 +0,0 @@ -

Register Your Email Address

- -<%= form_for @changeset, Routes.user_path(@conn, :register), fn f -> %> - <%= if @changeset.action do %> -
-

Opps, something went wrong! Please check the errors below.

-
- <% end %> -
- <%= text_input f, :email, placeholder: "Your Email Address", - class: "pa3 ma2 mr1 f3 ba bg-transparent w-100" %> - <%= error_tag f, :email %> -
- <%= submit "Save Email Address", - class: "b pa3 input-reset ba b--dark-green bw2 br3 bg-transparent - grow pointer f4 lh-solid white bg-green fr mr1" %> -<% end %> diff --git a/lib/auth_web/views/auth_view.ex b/lib/auth_web/views/auth_view.ex deleted file mode 100644 index 88802262..00000000 --- a/lib/auth_web/views/auth_view.ex +++ /dev/null @@ -1,5 +0,0 @@ -defmodule AuthWeb.AuthView do - @moduledoc false - - use AuthWeb, :view -end diff --git a/lib/auth_web/views/email_view.ex b/lib/auth_web/views/email_view.ex deleted file mode 100644 index 432ce0ff..00000000 --- a/lib/auth_web/views/email_view.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule AuthWeb.EmailView do - use AuthWeb, :view -end diff --git a/lib/auth_web/views/error_helpers.ex b/lib/auth_web/views/error_helpers.ex index 1ea02023..5a18c258 100644 --- a/lib/auth_web/views/error_helpers.ex +++ b/lib/auth_web/views/error_helpers.ex @@ -9,28 +9,35 @@ defmodule AuthWeb.ErrorHelpers do Generates tag for inlined form input errors. """ def error_tag(form, field) do - if error = form.errors[field] do - content_tag(:span, translate_error(error), class: "help-block") - end + Enum.map(Keyword.get_values(form.errors, field), fn error -> + content_tag(:span, translate_error(error), + class: "help-block", + data: [phx_error_for: input_id(form, field)] + ) + end) end @doc """ Translates an error message using gettext. """ def translate_error({msg, opts}) do - # Because error messages were defined within Ecto, we must - # call the Gettext module passing our Gettext backend. We - # also use the "errors" domain as translations are placed - # in the errors.po file. - # Ecto will pass the :count keyword if the error message is - # meant to be pluralized. - # On your own code and templates, depending on whether you - # need the message to be pluralized or not, this could be - # written simply as: + # When using gettext, we typically pass the strings we want + # to translate as a static argument: + # + # # Translate "is invalid" in the "errors" domain + # dgettext("errors", "is invalid") + # + # # Translate the number of files with plural rules + # dngettext("errors", "1 file", "%{count} files", count) # - # dngettext "errors", "1 file", "%{count} files", count - # dgettext "errors", "is invalid" + # Because the error messages we show in our forms and APIs + # are defined inside Ecto, we need to translate them dynamically. + # This requires us to call the Gettext module passing our gettext + # backend as first argument. # + # Note we use the "errors" domain, which means translations + # should be written to the errors.po file. The :count option is + # set by Ecto and indicates we should also apply plural rules. if count = opts[:count] do Gettext.dngettext(AuthWeb.Gettext, "errors", msg, msg, count, opts) else diff --git a/lib/auth_web/views/error_view.ex b/lib/auth_web/views/error_view.ex index 4175dada..c0311c2d 100644 --- a/lib/auth_web/views/error_view.ex +++ b/lib/auth_web/views/error_view.ex @@ -1,17 +1,16 @@ defmodule AuthWeb.ErrorView do use AuthWeb, :view - def render("404.html", _assigns) do - "Page not found" - end - - def render("500.html", _assigns) do - "Internal server error" - end + # If you want to customize a particular status code + # for a certain format, you may uncomment below. + # def render("500.html", _assigns) do + # "Internal Server Error" + # end - # In case no render clause matches or no - # template is found, let's render it as 500 - def template_not_found(_template, assigns) do - render("500.html", assigns) + # By default, Phoenix returns the status message from + # the template name. For example, "404.html" becomes + # "Not Found". + def template_not_found(template, _assigns) do + Phoenix.Controller.status_message_from_template(template) end end diff --git a/lib/auth_web/views/html_email_view.ex b/lib/auth_web/views/html_email_view.ex deleted file mode 100644 index 62c92a0d..00000000 --- a/lib/auth_web/views/html_email_view.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule AuthWeb.HtmlEmailView do - use AuthWeb, :view -end diff --git a/lib/auth_web/views/user_view.ex b/lib/auth_web/views/user_view.ex deleted file mode 100644 index 41ef8b1b..00000000 --- a/lib/auth_web/views/user_view.ex +++ /dev/null @@ -1,14 +0,0 @@ -defmodule AuthWeb.UserView do - use AuthWeb, :view - alias Auth.User - - def first_name(%User{name: name}) do - name - |> String.split(" ") - |> Enum.at(0) - end - - def render("user.json", %{user: user}) do - %{id: user.id, username: user.username} - end -end diff --git a/lib/email.ex b/lib/email.ex deleted file mode 100644 index c8b9157b..00000000 --- a/lib/email.ex +++ /dev/null @@ -1,22 +0,0 @@ -defmodule Auth.Email do - use Bamboo.Phoenix, view: AuthWeb.HtmlEmailView - - def send_test_email(to_email_address, subject, message) do - new_email() - # also needs to be a validated email - |> from("nelson@dwyl.io") - |> to(to_email_address) - |> subject(subject) - |> text_body(message) - end - - def send_test_html_email(to_email_address, subject, link) do - new_email() - # also needs to be a validated email - |> from("cleo@dwyl.com") - |> to(to_email_address) - |> subject(subject) - |> assign(:link, link) - |> render("email.html") - end -end diff --git a/lib/validate_email.ex b/lib/validate_email.ex deleted file mode 100644 index ddf4e75c..00000000 --- a/lib/validate_email.ex +++ /dev/null @@ -1,10 +0,0 @@ -defmodule ValidateEmail do - def validate(email) do - case Regex.run(~r/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/, email) do - nil -> - {:error, "Invalid email"} - [email] -> - {:ok, Regex.run(~r/(\w+)@([\w.]+)/, email)} - end - end -end diff --git a/mix.exs b/mix.exs index d56ca671..5d217f98 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Auth.Mixfile do def project do [ app: :auth, - version: "0.0.1", + version: "1.0.0", elixir: "~> 1.5", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix, :gettext] ++ Mix.compilers(), @@ -28,15 +28,7 @@ defmodule Auth.Mixfile do def application do [ mod: {Auth.Application, []}, - extra_applications: [ - # auth specific: - :ueberauth, - :ueberauth_identity, - # email - :bamboo, - # password encryption/checking for :ueberauth_identity - :comeonin - ] + extra_applications: [:logger, :runtime_tools] ] end @@ -49,41 +41,26 @@ defmodule Auth.Mixfile do # Type `mix help deps` for examples and options. defp deps do [ - {:fields, "~> 2.2.0"}, - # {:alog, git: "https://github.com/dwyl/alog.git", tag: "0.4.2"}, # Phoenix core: - {:phoenix, "~> 1.4.12"}, + {:phoenix, "~> 1.4.16"}, {:phoenix_pubsub, "~> 1.1"}, {:phoenix_ecto, "~> 4.0"}, - {:ecto_sql, "~> 3.3.3"}, - {:postgrex, ">= 0.15.0"}, + {:ecto_sql, "~> 3.1"}, + {:postgrex, ">= 0.0.0"}, {:phoenix_html, "~> 2.11"}, {:phoenix_live_reload, "~> 1.2", only: :dev}, {:gettext, "~> 0.11"}, {:jason, "~> 1.0"}, {:plug_cowboy, "~> 2.0"}, - # Auth: - # github.com/ueberauth/ueberauth - # {:ueberauth, "~> 0.4"}, - # github.com/ueberauth/ueberauth_identity - # {:ueberauth_identity, "~> 0.2"}, + # Field Validation and Encryption: + {:fields, "~> 2.3.1"}, - # Email Sent by AWS SES see: https://git.io/vSuqc - # github.com/thoughtbot/bamboo - {:bamboo, "~> 1.1"}, - # github.com/fewlinesco/bamboo_smtp - {:bamboo_smtp, "~> 1.6.0"}, + # Auth: - # Password Hashing - # github.com/riverrun/comeonin (bcrypt) - # {:comeonin, "~> 2.0"}, - # Dev/Test only: - # for checking test coverage + # check test coverage {:excoveralls, "~> 0.6", only: :test}, - # for testing email without sending any - {:mock, "~> 0.2.0", only: :test} ] end diff --git a/mix.lock b/mix.lock deleted file mode 100644 index 3b0ead3a..00000000 --- a/mix.lock +++ /dev/null @@ -1,48 +0,0 @@ -%{ - "argon2_elixir": {:hex, :argon2_elixir, "2.1.2", "c276b960f0b550a7613a9bebf8e14645ca5eb71a34a1bf0f896fe3511966b051", [:make, :mix], [{:comeonin, "~> 5.1", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.5", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"}, - "bamboo": {:hex, :bamboo, "1.1.0", "ecbdc851d0127d369957e5ca7bcfb4c7fe7dbfaf4fa0b2e5cc2493dd8a9a600e", [:mix], [{:hackney, ">= 1.13.0", [hex: :hackney, optional: false]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}, {:poison, ">= 1.5.0", [hex: :poison, optional: false]}]}, - "bamboo_smtp": {:hex, :bamboo_smtp, "1.6.0", "0a3607b77f22554af58c547350c1c73ebba6f4fb2c4bd0b11713ab5b4081588f", [:mix], [{:bamboo, "~> 1.0", [hex: :bamboo, optional: false]}, {:gen_smtp, "~> 0.12.0", [hex: :gen_smtp, optional: false]}]}, - "certifi": {:hex, :certifi, "2.4.2", "75424ff0f3baaccfd34b1214184b6ef616d89e420b258bb0a5ea7d7bc628f7f0", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, optional: false]}]}, - "comeonin": {:hex, :comeonin, "5.2.0", "d0277415a4b887f7a5f418be78776e5dacf1b005105913f075d301c0da3b5311", [:mix], [], "hexpm"}, - "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []}, - "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"}, - "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm"}, - "db_connection": {:hex, :db_connection, "2.2.0", "e923e88887cd60f9891fd324ac5e0290954511d090553c415fbf54be4c57ee63", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, - "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm"}, - "ecto": {:hex, :ecto, "3.3.2", "002aa428c752a8ee4bb65baa9d1041f0514d7435d2e21d58cb6aa69a1076721d", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, - "ecto_sql": {:hex, :ecto_sql, "3.3.3", "7d8962d39f16181c1df1bbd0f64aa392bd9ce0b9f8ff5ff21d43dca3d624eee7", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4 or ~> 3.3.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, - "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm"}, - "excoveralls": {:hex, :excoveralls, "0.10.3", "b090a3fbcb3cfa136f0427d038c92a9051f840953ec11b40ee74d9d4eac04d1e", [:mix], [{:hackney, "~> 1.13", [hex: :hackney, optional: false]}, {:jason, "~> 1.0", [hex: :jason, optional: false]}]}, - "fields": {:hex, :fields, "2.2.0", "6e52310411e244d9e2faaa399a78b829a6304634bba527ee45b18da2f557984e", [:mix], [{:argon2_elixir, "~> 2.1.2", [hex: :argon2_elixir, repo: "hexpm", optional: false]}, {:ecto, "~> 3.3.1", [hex: :ecto, repo: "hexpm", optional: false]}, {:html_sanitize_ex, "~> 1.4", [hex: :html_sanitize_ex, repo: "hexpm", optional: false]}], "hexpm"}, - "file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [:mix], []}, - "gen_smtp": {:hex, :gen_smtp, "0.12.0", "97d44903f5ca18ca85cb39aee7d9c77e98d79804bbdef56078adcf905cb2ef00", [:rebar3], []}, - "gettext": {:hex, :gettext, "0.16.1", "e2130b25eebcbe02bb343b119a07ae2c7e28bd4b146c4a154da2ffb2b3507af2", [:mix], []}, - "hackney": {:hex, :hackney, "1.15.0", "287a5d2304d516f63e56c469511c42b016423bcb167e61b611f6bad47e3ca60e", [:rebar3], [{:certifi, "2.4.2", [hex: :certifi, optional: false]}, {:idna, "6.0.0", [hex: :idna, optional: false]}, {:metrics, "1.0.1", [hex: :metrics, optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, optional: false]}]}, - "html_sanitize_ex": {:hex, :html_sanitize_ex, "1.4.0", "0310d27d7bafb662f30bff22ec732a72414799c83eaf44239781fd23b96216c0", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm"}, - "httpoison": {:hex, :httpoison, "0.13.0", "bfaf44d9f133a6599886720f3937a7699466d23bb0cd7a88b6ba011f53c6f562", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, - "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, optional: false]}]}, - "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, optional: true]}]}, - "meck": {:hex, :meck, "0.8.12", "1f7b1a9f5d12c511848fec26bbefd09a21e1432eadb8982d9a8aceb9891a3cf2", [:rebar3], []}, - "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], []}, - "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], []}, - "mimerl": {:hex, :mimerl, "1.0.2", "993f9b0e084083405ed8252b99460c4f0563e41729ab42d9074fd5e52439be88", [:rebar3], []}, - "mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"}, - "mock": {:hex, :mock, "0.2.1", "bfdba786903e77f9c18772dee472d020ceb8ef000783e737725a4c8f54ad28ec", [:mix], [{:meck, "~> 0.8.2", [hex: :meck, optional: false]}]}, - "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], []}, - "phoenix": {:hex, :phoenix, "1.4.12", "b86fa85a2ba336f5de068549de5ccceec356fd413264a9637e7733395d6cc4ea", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, - "phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, optional: true]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}]}, - "phoenix_html": {:hex, :phoenix_html, "2.13.0", "3bad10de5efb6c590f7aa5b316ad0d3faa054715414c9b562c410de4ffb885c5", [:mix], [{:plug, "~> 1.5", [hex: :plug, optional: false]}]}, - "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.0", "3bb31a9fbd40ffe8652e60c8660dffd72dd231efcdf49b744fb75b9ef7db5dd2", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, optional: false]}]}, - "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"}, - "plug": {:hex, :plug, "1.8.3", "12d5f9796dc72e8ac9614e94bda5e51c4c028d0d428e9297650d09e15a684478", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.1.2", "8b0addb5908c5238fac38e442e81b6fcd32788eaa03246b4d55d147c47c5805e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, - "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], []}, - "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], []}, - "postgrex": {:hex, :postgrex, "0.15.3", "5806baa8a19a68c4d07c7a624ccdb9b57e89cbc573f1b98099e3741214746ae4", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, - "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], []}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], []}, - "telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm"}, - "ueberauth": {:hex, :ueberauth, "0.5.0", "4570ec94d7f784dc4c4aa94c83391dbd9b9bd7b66baa30e95a666c5ec1b168b1", [:mix], [{:plug, "~> 1.2", [hex: :plug, optional: false]}]}, - "ueberauth_identity": {:hex, :ueberauth_identity, "0.2.3", "216f18ca24347f6b3dd0e5f4342b6fef7a4027fad0d133f1c454d51b7eaac55b", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}, {:ueberauth, "~> 0.2", [hex: :ueberauth, optional: false]}]}, - "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], []}, -} diff --git a/priv/gettext/en/LC_MESSAGES/errors.po b/priv/gettext/en/LC_MESSAGES/errors.po index 087d374e..a589998c 100644 --- a/priv/gettext/en/LC_MESSAGES/errors.po +++ b/priv/gettext/en/LC_MESSAGES/errors.po @@ -22,6 +22,10 @@ msgstr "" msgid "is invalid" msgstr "" +## From Ecto.Changeset.validate_acceptance/3 +msgid "must be accepted" +msgstr "" + ## From Ecto.Changeset.validate_format/3 msgid "has invalid format" msgstr "" @@ -39,10 +43,10 @@ msgid "does not match confirmation" msgstr "" ## From Ecto.Changeset.no_assoc_constraint/3 -msgid "is still associated to this entry" +msgid "is still associated with this entry" msgstr "" -msgid "are still associated to this entry" +msgid "are still associated with this entry" msgstr "" ## From Ecto.Changeset.validate_length/3 diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot index a2289579..39a220be 100644 --- a/priv/gettext/errors.pot +++ b/priv/gettext/errors.pot @@ -1,11 +1,11 @@ -## This file is a PO Template file. +## This is a PO Template file. ## ## `msgid`s here are often extracted from source code. ## Add new translations manually only if they're dynamic ## translations that can't be statically extracted. ## ## Run `mix gettext.extract` to bring this file up to -## date. Leave `msgstr`s empty as changing them here as no +## date. Leave `msgstr`s empty as changing them here has no ## effect: edit them in PO (`.po`) files instead. ## From Ecto.Changeset.cast/4 @@ -20,6 +20,10 @@ msgstr "" msgid "is invalid" msgstr "" +## From Ecto.Changeset.validate_acceptance/3 +msgid "must be accepted" +msgstr "" + ## From Ecto.Changeset.validate_format/3 msgid "has invalid format" msgstr "" @@ -37,10 +41,10 @@ msgid "does not match confirmation" msgstr "" ## From Ecto.Changeset.no_assoc_constraint/3 -msgid "is still associated to this entry" +msgid "is still associated with this entry" msgstr "" -msgid "are still associated to this entry" +msgid "are still associated with this entry" msgstr "" ## From Ecto.Changeset.validate_length/3 diff --git a/priv/repo/migrations/.formatter.exs b/priv/repo/migrations/.formatter.exs new file mode 100644 index 00000000..49f9151e --- /dev/null +++ b/priv/repo/migrations/.formatter.exs @@ -0,0 +1,4 @@ +[ + import_deps: [:ecto_sql], + inputs: ["*.exs"] +] diff --git a/priv/repo/migrations/20170207074532_create_user.exs b/priv/repo/migrations/20170207074532_create_user.exs deleted file mode 100644 index 40c4c9ea..00000000 --- a/priv/repo/migrations/20170207074532_create_user.exs +++ /dev/null @@ -1,17 +0,0 @@ -defmodule Auth.Repo.Migrations.CreateUser do - use Ecto.Migration - - def change do - create table(:users) do - add :email, :string, null: false - add :name, :string, null: true - add :username, :string, null: true - add :password_hash, :string - - timestamps() - end - - create unique_index(:users, [:email]) - create unique_index(:users, [:username]) - end -end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index e89787f7..e2497a21 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -5,7 +5,7 @@ # Inside the script, you can read and write to any of your # repositories directly: # -# Auth.Repo.insert!(%Auth.SomeModel{}) +# Auth.Repo.insert!(%Auth.SomeSchema{}) # # We recommend using the bang functions (`insert!`, `update!` # and so on) as they will fail if something goes wrong. diff --git a/test/auth_web/controllers/page_controller_test.exs b/test/auth_web/controllers/page_controller_test.exs index 957ad260..a7d4dcbe 100644 --- a/test/auth_web/controllers/page_controller_test.exs +++ b/test/auth_web/controllers/page_controller_test.exs @@ -3,6 +3,6 @@ defmodule AuthWeb.PageControllerTest do test "GET /", %{conn: conn} do conn = get(conn, "/") - assert html_response(conn, 200) =~ "Authentication" + assert html_response(conn, 200) =~ "Welcome to Phoenix!" end end diff --git a/test/auth_web/views/error_view_test.exs b/test/auth_web/views/error_view_test.exs index 13983f06..da2d4a6a 100644 --- a/test/auth_web/views/error_view_test.exs +++ b/test/auth_web/views/error_view_test.exs @@ -5,14 +5,10 @@ defmodule AuthWeb.ErrorViewTest do import Phoenix.View test "renders 404.html" do - assert render_to_string(AuthWeb.ErrorView, "404.html", []) == "Page not found" + assert render_to_string(AuthWeb.ErrorView, "404.html", []) == "Not Found" end - test "render 500.html" do - assert render_to_string(AuthWeb.ErrorView, "500.html", []) == "Internal server error" - end - - test "render any other" do - assert render_to_string(AuthWeb.ErrorView, "505.html", []) == "Internal server error" + test "renders 500.html" do + assert render_to_string(AuthWeb.ErrorView, "500.html", []) == "Internal Server Error" end end diff --git a/test/auth_web/views/layout_view_test.exs b/test/auth_web/views/layout_view_test.exs index 934624a7..1b8a790d 100644 --- a/test/auth_web/views/layout_view_test.exs +++ b/test/auth_web/views/layout_view_test.exs @@ -1,3 +1,8 @@ defmodule AuthWeb.LayoutViewTest do use AuthWeb.ConnCase, async: true + + # When testing helpers, you may want to import Phoenix.HTML and + # use functions such as safe_to_string() to convert the helper + # result into an HTML string. + # import Phoenix.HTML end diff --git a/test/support/channel_case.ex b/test/support/channel_case.ex index 1154d9b6..4b3e7f6b 100644 --- a/test/support/channel_case.ex +++ b/test/support/channel_case.ex @@ -5,12 +5,14 @@ defmodule AuthWeb.ChannelCase do Such tests rely on `Phoenix.ChannelTest` and also import other functionality to make it easier - to build and query models. + to build common data structures and query the data layer. Finally, if the test case interacts with the database, - it cannot be async. For this reason, every test runs - inside a transaction which is reset at the beginning - of the test unless the test case is marked as async. + we enable the SQL sandbox, so changes done to the database + are reverted at the end of every test. If you are using + PostgreSQL, you can even run database tests asynchronously + by setting `use AuthWeb.ChannelCase, async: true`, although + this option is not recommended for other databases. """ use ExUnit.CaseTemplate diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index 6b8705b1..02e5cac3 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -5,12 +5,14 @@ defmodule AuthWeb.ConnCase do Such tests rely on `Phoenix.ConnTest` and also import other functionality to make it easier - to build and query models. + to build common data structures and query the data layer. Finally, if the test case interacts with the database, - it cannot be async. For this reason, every test runs - inside a transaction which is reset at the beginning - of the test unless the test case is marked as async. + we enable the SQL sandbox, so changes done to the database + are reverted at the end of every test. If you are using + PostgreSQL, you can even run database tests asynchronously + by setting `use AuthWeb.ConnCase, async: true`, although + this option is not recommended for other databases. """ use ExUnit.CaseTemplate diff --git a/test/support/data_case.ex b/test/support/data_case.ex index 04186466..f4ac7359 100644 --- a/test/support/data_case.ex +++ b/test/support/data_case.ex @@ -7,9 +7,11 @@ defmodule Auth.DataCase do your tests. Finally, if the test case interacts with the database, - it cannot be async. For this reason, every test runs - inside a transaction which is reset at the beginning - of the test unless the test case is marked as async. + we enable the SQL sandbox, so changes done to the database + are reverted at the end of every test. If you are using + PostgreSQL, you can even run database tests asynchronously + by setting `use Auth.DataCase, async: true`, although + this option is not recommended for other databases. """ use ExUnit.CaseTemplate @@ -45,8 +47,8 @@ defmodule Auth.DataCase do """ def errors_on(changeset) do Ecto.Changeset.traverse_errors(changeset, fn {message, opts} -> - Enum.reduce(opts, message, fn {key, value}, acc -> - String.replace(acc, "%{#{key}}", to_string(value)) + Regex.replace(~r"%{(\w+)}", message, fn _, key -> + opts |> Keyword.get(String.to_existing_atom(key), key) |> to_string() end) end) end diff --git a/test/test_helper.exs b/test/test_helper.exs index 7642646c..26689cc8 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,4 +1,2 @@ -ExUnit.start - +ExUnit.start() Ecto.Adapters.SQL.Sandbox.mode(Auth.Repo, :manual) - diff --git a/test/validate_email_test.exs b/test/validate_email_test.exs deleted file mode 100644 index 520baeaa..00000000 --- a/test/validate_email_test.exs +++ /dev/null @@ -1,14 +0,0 @@ -defmodule ValidateEmailTest do - use AuthWeb.ConnCase, async: true - require ValidateEmail - - test "invalid email address returns {:error, 'Invalid email'}" do - assert ValidateEmail.validate("invalid") == {:error, "Invalid email"} - end - - test "Valid email address e.g: username@domain.net returns - {:ok, ['username@domain.net', 'username', 'domain.net']}" do - assert ValidateEmail.validate("username@domain.net") == - {:ok, ["username@domain.net", "username", "domain.net"]} - end -end From d8c4d06b95892111f53b180635b5ac794cbdd354 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 13:53:18 +0100 Subject: [PATCH 006/189] update version of Phoenix and Fields --- mix.lock | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 mix.lock diff --git a/mix.lock b/mix.lock new file mode 100644 index 00000000..0fe66eb6 --- /dev/null +++ b/mix.lock @@ -0,0 +1,39 @@ +%{ + "argon2_elixir": {:hex, :argon2_elixir, "2.3.0", "e251bdafd69308e8c1263e111600e6d68bd44f23d2cccbe43fcb1a417a76bc8e", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "28ccb63bff213aecec1f7f3dde9648418b031f822499973281d8f494b9d5a3b3"}, + "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"}, + "comeonin": {:hex, :comeonin, "5.3.1", "7fe612b739c78c9c1a75186ef2d322ce4d25032d119823269d0aa1e2f1e20025", [:mix], [], "hexpm", "d6222483060c17f0977fad1b7401ef0c5863c985a64352755f366aee3799c245"}, + "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, + "cowboy": {:hex, :cowboy, "2.7.0", "91ed100138a764355f43316b1d23d7ff6bdb0de4ea618cb5d8677c93a7a2f115", [:rebar3], [{:cowlib, "~> 2.8.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "04fd8c6a39edc6aaa9c26123009200fc61f92a3a94f3178c527b70b767c6e605"}, + "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"}, + "db_connection": {:hex, :db_connection, "2.2.1", "caee17725495f5129cb7faebde001dc4406796f12a62b8949f4ac69315080566", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "2b02ece62d9f983fcd40954e443b7d9e6589664380e5546b2b9b523cd0fb59e1"}, + "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, + "ecto": {:hex, :ecto, "3.4.0", "a7a83ab8359bf816ce729e5e65981ce25b9fc5adfc89c2ea3980f4fed0bfd7c1", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "5eed18252f5b5bbadec56a24112b531343507dbe046273133176b12190ce19cc"}, + "ecto_sql": {:hex, :ecto_sql, "3.4.1", "3c9136ba138f9b74d31286c73c61232a92bd19385f7c5607bdeb3a4587ef91f5", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b4be0bffe7b0bdf5393defcae52712f248e70cc2bc0e8ab6ddb03be66371516"}, + "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, + "excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"}, + "fields": {:hex, :fields, "2.3.1", "30102c844f79a9024cf4e07a1595427c8007bd301f1c6ddd689a31f0f0cbd622", [:mix], [{:argon2_elixir, "~> 2.3.0", [hex: :argon2_elixir, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:html_sanitize_ex, "~> 1.4", [hex: :html_sanitize_ex, repo: "hexpm", optional: false]}], "hexpm", "fc3067bf3226c43aeaf3e7eab2524785da28507fd064d858b61ddac4d971cec8"}, + "file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm", "97a3b6f8d63ef53bd0113070102db2ce05352ecf0d25390eb8d747c2bde98bca"}, + "gettext": {:hex, :gettext, "0.17.4", "f13088e1ec10ce01665cf25f5ff779e7df3f2dc71b37084976cf89d1aa124d5c", [:mix], [], "hexpm", "3c75b5ea8288e2ee7ea503ff9e30dfe4d07ad3c054576a6e60040e79a801e14d"}, + "hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "e0100f8ef7d1124222c11ad362c857d3df7cb5f4204054f9f0f4a728666591fc"}, + "html_sanitize_ex": {:hex, :html_sanitize_ex, "1.4.0", "0310d27d7bafb662f30bff22ec732a72414799c83eaf44239781fd23b96216c0", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm", "c5d79626be0b6e50c19ecdfb783ee26e85bd3a77436b488379ce6dc104ec4593"}, + "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, + "jason": {:hex, :jason, "1.2.0", "10043418c42d2493d0ee212d3fddd25d7ffe484380afad769a0a38795938e448", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "116747dbe057794c3a3e4e143b7c8390b29f634e16c78a7f59ba75bfa6852e7f"}, + "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, + "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"}, + "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, + "mochiweb": {:hex, :mochiweb, "2.20.1", "e4dbd0ed716f076366ecf62ada5755a844e1d95c781e8c77df1d4114be868cdf", [:rebar3], [], "hexpm", "d1aeee7870470d2fa9eae0b3d5ab6c33801aa2d82b10e9dade885c5c921b36aa"}, + "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, + "phoenix": {:hex, :phoenix, "1.4.16", "2cbbe0c81e6601567c44cc380c33aa42a1372ac1426e3de3d93ac448a7ec4308", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "856cc1a032fa53822737413cf51aa60e750525d7ece7d1c0576d90d7c0f05c24"}, + "phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "c5e666a341ff104d0399d8f0e4ff094559b2fde13a5985d4cb5023b2c2ac558b"}, + "phoenix_html": {:hex, :phoenix_html, "2.14.1", "7dabafadedb552db142aacbd1f11de1c0bbaa247f90c449ca549d5e30bbc66b4", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "536d5200ad37fecfe55b3241d90b7a8c3a2ca60cd012fc065f776324fa9ab0a9"}, + "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.1", "274a4b07c4adbdd7785d45a8b0bb57634d0b4f45b18d2c508b26c0344bd59b8f", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "41b4103a2fa282cfd747d377233baf213c648fdcc7928f432937676532490eee"}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm", "1f13f9f0f3e769a667a6b6828d29dec37497a082d195cc52dbef401a9b69bf38"}, + "plug": {:hex, :plug, "1.10.0", "6508295cbeb4c654860845fb95260737e4a8838d34d115ad76cd487584e2fc4d", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "422a9727e667be1bf5ab1de03be6fa0ad67b775b2d84ed908f3264415ef29d4a"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.1.2", "8b0addb5908c5238fac38e442e81b6fcd32788eaa03246b4d55d147c47c5805e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "7d722581ce865a237e14da6d946f92704101740a256bd13ec91e63c0b122fc70"}, + "plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"}, + "postgrex": {:hex, :postgrex, "0.15.3", "5806baa8a19a68c4d07c7a624ccdb9b57e89cbc573f1b98099e3741214746ae4", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "4737ce62a31747b4c63c12b20c62307e51bb4fcd730ca0c32c280991e0606c90"}, + "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"}, + "telemetry": {:hex, :telemetry, "0.4.1", "ae2718484892448a24470e6aa341bc847c3277bfb8d4e9289f7474d752c09c7f", [:rebar3], [], "hexpm", "4738382e36a0a9a2b6e25d67c960e40e1a2c95560b9f936d8e29de8cd858480f"}, + "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, +} From 7b3cbe81b5a3029852e245cc820b51fbe5f21163 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 13:53:50 +0100 Subject: [PATCH 007/189] update version of Elixir in .travis.yml --- .travis.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 778b15f3..a09673f2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,8 @@ language: elixir elixir: - - 1.7 + - 1.10.2 +otp_release: + - 22.1.8 services: - postgresql env: @@ -8,10 +10,10 @@ env: before_script: - mix do ecto.create, ecto.migrate script: - - mix do deps.get, compile --warnings-as-errors, coveralls.json -# after_success: -# - bash <(curl -s https://codecov.io/bash) + - mix do deps.get, coveralls.json # See: github.com/dwyl/repo-badges#documentation after_script: - MIX_ENV=docs mix deps.get - MIX_ENV=docs mix inch.report +after_success: + - bash <(curl -s https://codecov.io/bash) From fc90cf354ee406d0a153f0040ab8fa18b33f86d9 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 17:24:00 +0100 Subject: [PATCH 008/189] add untestable files to coveralls.json --- coveralls.json | 10 ++++------ lib/auth/application.ex | 3 +++ lib/auth_web/router.ex | 6 +++--- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/coveralls.json b/coveralls.json index a8436de9..aa06e6c8 100644 --- a/coveralls.json +++ b/coveralls.json @@ -3,10 +3,8 @@ "minimum_coverage": 100 }, "skip_files": [ - "web/channels", - "lib/dwylbot.ex", - "test/support/model_case.ex", - "test/support/channel_case.ex", - "web/web.ex" + "test/", + "lib/auth_web.ex", + "lib/auth_web/views/error_helpers.ex" ] -} \ No newline at end of file +} diff --git a/lib/auth/application.ex b/lib/auth/application.ex index 01f6b037..d04e495e 100644 --- a/lib/auth/application.ex +++ b/lib/auth/application.ex @@ -24,8 +24,11 @@ defmodule Auth.Application do # Tell Phoenix to update the endpoint configuration # whenever the application is updated. + # Sadly this is untestable hence ignoring it. + # coveralls-ignore-start def config_change(changed, _new, removed) do AuthWeb.Endpoint.config_change(changed, removed) :ok end + # coveralls-ignore-stop end diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index c75601ab..1cf1602d 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -9,9 +9,9 @@ defmodule AuthWeb.Router do plug :put_secure_browser_headers end - pipeline :api do - plug :accepts, ["json"] - end + # pipeline :api do + # plug :accepts, ["json"] + # end scope "/", AuthWeb do pipe_through :browser From a75b4e36e3aad9c6c457837a623dce495214f4a7 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 20:48:49 +0100 Subject: [PATCH 009/189] add migrations from MVP #45 --- .../migrations/20191113100513_create_tags.exs | 11 +++++++++ .../20191113100912_create_status.exs | 11 +++++++++ .../20191113100920_create_people.exs | 23 +++++++++++++++++++ .../20191113114340_add_person_id_to_tag.exs | 9 ++++++++ ...20191113141229_add_person_id_to_status.exs | 9 ++++++++ .../20191126144556_create_session_table.exs | 15 ++++++++++++ ...130210036_add_picture_locale_to_people.exs | 10 ++++++++ 7 files changed, 88 insertions(+) create mode 100644 priv/repo/migrations/20191113100513_create_tags.exs create mode 100644 priv/repo/migrations/20191113100912_create_status.exs create mode 100644 priv/repo/migrations/20191113100920_create_people.exs create mode 100644 priv/repo/migrations/20191113114340_add_person_id_to_tag.exs create mode 100644 priv/repo/migrations/20191113141229_add_person_id_to_status.exs create mode 100644 priv/repo/migrations/20191126144556_create_session_table.exs create mode 100644 priv/repo/migrations/20191130210036_add_picture_locale_to_people.exs diff --git a/priv/repo/migrations/20191113100513_create_tags.exs b/priv/repo/migrations/20191113100513_create_tags.exs new file mode 100644 index 00000000..2166ca7b --- /dev/null +++ b/priv/repo/migrations/20191113100513_create_tags.exs @@ -0,0 +1,11 @@ +defmodule App.Repo.Migrations.CreateTags do + use Ecto.Migration + + def change do + create table(:tags) do + add :text, :string + + timestamps() + end + end +end diff --git a/priv/repo/migrations/20191113100912_create_status.exs b/priv/repo/migrations/20191113100912_create_status.exs new file mode 100644 index 00000000..8502bbcc --- /dev/null +++ b/priv/repo/migrations/20191113100912_create_status.exs @@ -0,0 +1,11 @@ +defmodule App.Repo.Migrations.CreateStatus do + use Ecto.Migration + + def change do + create table(:status) do + add :text, :string + + timestamps() + end + end +end diff --git a/priv/repo/migrations/20191113100920_create_people.exs b/priv/repo/migrations/20191113100920_create_people.exs new file mode 100644 index 00000000..1992fc6c --- /dev/null +++ b/priv/repo/migrations/20191113100920_create_people.exs @@ -0,0 +1,23 @@ +defmodule App.Repo.Migrations.CreatePeople do + use Ecto.Migration + + def change do + create table(:people) do + add :username, :binary + add :username_hash, :binary + add :email, :binary + add :email_hash, :binary + add :givenName, :binary + add :familyName, :binary + add :password_hash, :binary + add :key_id, :integer + add :status, references(:status, on_delete: :nothing) + add :tag, references(:tags, on_delete: :nothing) + + timestamps() + end + + create index(:people, [:status]) + create index(:people, [:tag]) + end +end diff --git a/priv/repo/migrations/20191113114340_add_person_id_to_tag.exs b/priv/repo/migrations/20191113114340_add_person_id_to_tag.exs new file mode 100644 index 00000000..27c7290c --- /dev/null +++ b/priv/repo/migrations/20191113114340_add_person_id_to_tag.exs @@ -0,0 +1,9 @@ +defmodule App.Repo.Migrations.AddPersonIdToTag do + use Ecto.Migration + + def change do + alter table(:tags) do + add :person_id, references(:people, on_delete: :nothing) + end + end +end diff --git a/priv/repo/migrations/20191113141229_add_person_id_to_status.exs b/priv/repo/migrations/20191113141229_add_person_id_to_status.exs new file mode 100644 index 00000000..31427a9c --- /dev/null +++ b/priv/repo/migrations/20191113141229_add_person_id_to_status.exs @@ -0,0 +1,9 @@ +defmodule App.Repo.Migrations.AddPersonIdToStatus do + use Ecto.Migration + + def change do + alter table(:status) do + add :person_id, references(:people, on_delete: :nothing) + end + end +end diff --git a/priv/repo/migrations/20191126144556_create_session_table.exs b/priv/repo/migrations/20191126144556_create_session_table.exs new file mode 100644 index 00000000..7705c9d3 --- /dev/null +++ b/priv/repo/migrations/20191126144556_create_session_table.exs @@ -0,0 +1,15 @@ +defmodule App.Repo.Migrations.CreateSessionTable do + use Ecto.Migration + + def change do + create table(:sessions) do + add :auth_token, :binary + add :refresh_token, :binary + add :key_id, :integer + + # keep sessions when user is deleted + add :person_id, references(:people, on_delete: :nothing) + timestamps() + end + end +end diff --git a/priv/repo/migrations/20191130210036_add_picture_locale_to_people.exs b/priv/repo/migrations/20191130210036_add_picture_locale_to_people.exs new file mode 100644 index 00000000..68a00f74 --- /dev/null +++ b/priv/repo/migrations/20191130210036_add_picture_locale_to_people.exs @@ -0,0 +1,10 @@ +defmodule App.Repo.Migrations.AddPictureLocaleToPeople do + use Ecto.Migration + + def change do + alter table(:people) do + add :picture, :binary + add :locale, :string, default: "en" + end + end +end From 71f14d2eb49cd62287091980a2c2054c33cf6e94 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 20:50:40 +0100 Subject: [PATCH 010/189] replace App.Repo with Auth.Repo #45 --- priv/repo/migrations/20191113100513_create_tags.exs | 2 +- priv/repo/migrations/20191113100912_create_status.exs | 2 +- priv/repo/migrations/20191113100920_create_people.exs | 2 +- priv/repo/migrations/20191113114340_add_person_id_to_tag.exs | 2 +- priv/repo/migrations/20191113141229_add_person_id_to_status.exs | 2 +- priv/repo/migrations/20191126144556_create_session_table.exs | 2 +- .../migrations/20191130210036_add_picture_locale_to_people.exs | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/priv/repo/migrations/20191113100513_create_tags.exs b/priv/repo/migrations/20191113100513_create_tags.exs index 2166ca7b..2136ef52 100644 --- a/priv/repo/migrations/20191113100513_create_tags.exs +++ b/priv/repo/migrations/20191113100513_create_tags.exs @@ -1,4 +1,4 @@ -defmodule App.Repo.Migrations.CreateTags do +defmodule Auth.Repo.Migrations.CreateTags do use Ecto.Migration def change do diff --git a/priv/repo/migrations/20191113100912_create_status.exs b/priv/repo/migrations/20191113100912_create_status.exs index 8502bbcc..6d19f2c7 100644 --- a/priv/repo/migrations/20191113100912_create_status.exs +++ b/priv/repo/migrations/20191113100912_create_status.exs @@ -1,4 +1,4 @@ -defmodule App.Repo.Migrations.CreateStatus do +defmodule Auth.Repo.Migrations.CreateStatus do use Ecto.Migration def change do diff --git a/priv/repo/migrations/20191113100920_create_people.exs b/priv/repo/migrations/20191113100920_create_people.exs index 1992fc6c..716e3629 100644 --- a/priv/repo/migrations/20191113100920_create_people.exs +++ b/priv/repo/migrations/20191113100920_create_people.exs @@ -1,4 +1,4 @@ -defmodule App.Repo.Migrations.CreatePeople do +defmodule Auth.Repo.Migrations.CreatePeople do use Ecto.Migration def change do diff --git a/priv/repo/migrations/20191113114340_add_person_id_to_tag.exs b/priv/repo/migrations/20191113114340_add_person_id_to_tag.exs index 27c7290c..27c7e9a5 100644 --- a/priv/repo/migrations/20191113114340_add_person_id_to_tag.exs +++ b/priv/repo/migrations/20191113114340_add_person_id_to_tag.exs @@ -1,4 +1,4 @@ -defmodule App.Repo.Migrations.AddPersonIdToTag do +defmodule Auth.Repo.Migrations.AddPersonIdToTag do use Ecto.Migration def change do diff --git a/priv/repo/migrations/20191113141229_add_person_id_to_status.exs b/priv/repo/migrations/20191113141229_add_person_id_to_status.exs index 31427a9c..2b948e65 100644 --- a/priv/repo/migrations/20191113141229_add_person_id_to_status.exs +++ b/priv/repo/migrations/20191113141229_add_person_id_to_status.exs @@ -1,4 +1,4 @@ -defmodule App.Repo.Migrations.AddPersonIdToStatus do +defmodule Auth.Repo.Migrations.AddPersonIdToStatus do use Ecto.Migration def change do diff --git a/priv/repo/migrations/20191126144556_create_session_table.exs b/priv/repo/migrations/20191126144556_create_session_table.exs index 7705c9d3..5401d22e 100644 --- a/priv/repo/migrations/20191126144556_create_session_table.exs +++ b/priv/repo/migrations/20191126144556_create_session_table.exs @@ -1,4 +1,4 @@ -defmodule App.Repo.Migrations.CreateSessionTable do +defmodule Auth.Repo.Migrations.CreateSessionTable do use Ecto.Migration def change do diff --git a/priv/repo/migrations/20191130210036_add_picture_locale_to_people.exs b/priv/repo/migrations/20191130210036_add_picture_locale_to_people.exs index 68a00f74..c0d89848 100644 --- a/priv/repo/migrations/20191130210036_add_picture_locale_to_people.exs +++ b/priv/repo/migrations/20191130210036_add_picture_locale_to_people.exs @@ -1,4 +1,4 @@ -defmodule App.Repo.Migrations.AddPictureLocaleToPeople do +defmodule Auth.Repo.Migrations.AddPictureLocaleToPeople do use Ecto.Migration def change do From 7f8eec2c26c8a3b9f71025080e78d28cd2bb5406 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 20:52:11 +0100 Subject: [PATCH 011/189] add *.DS_Store to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index e3b326f6..95776370 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,5 @@ auth-*.tar # The directory Mix downloads your dependencies sources to. /deps/ + +*.DS_Store From dbc44d590ce1ffda163b9df2b2c7da946a87f484 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 20:58:06 +0100 Subject: [PATCH 012/189] add person schema #45 --- lib/auth/person.ex | 146 ++++++++++++++++++++++++++++++++++++++++++++ lib/auth/session.ex | 23 +++++++ lib/auth/status.ex | 18 ++++++ lib/auth/tag.ex | 17 ++++++ 4 files changed, 204 insertions(+) create mode 100644 lib/auth/person.ex create mode 100644 lib/auth/session.ex create mode 100644 lib/auth/status.ex create mode 100644 lib/auth/tag.ex diff --git a/lib/auth/person.ex b/lib/auth/person.ex new file mode 100644 index 00000000..10b7eefa --- /dev/null +++ b/lib/auth/person.ex @@ -0,0 +1,146 @@ +defmodule Auth.Person do + use Ecto.Schema + import Ecto.Changeset + + schema "people" do + field :email, Fields.EmailEncrypted + field :email_hash, Fields.EmailHash + field :familyName, Fields.Encrypted + field :givenName, Fields.Encrypted + field :locale, :string + field :password, :string, virtual: true + field :password_hash, Fields.Password + field :picture, :binary + field :username, :binary + field :username_hash, Fields.Hash + field :status, :id + field :tag, :id + field :key_id, :integer + + has_many :sessions, App.Ctx.Session, on_delete: :delete_all + timestamps() + end + + @doc """ + Default attributes validation for Person + """ + def changeset(person, attrs) do + person + |> cast(attrs, [ + :username, + :email, + :givenName, + :familyName, + :password_hash, + :key_id, + :locale, + :picture + ]) + |> validate_required([ + :username, + :email, + :givenName, + :familyName, + :password_hash, + :key_id + ]) + |> put_email_hash() + end + + @doc """ + Changeset used for Google OAuth authentication + Add email hash and set status verified + """ + def google_changeset(profile, attrs) do + profile + |> cast(attrs, [:email, :givenName, :familyName, :picture, :locale]) + |> validate_required([:email]) + |> put_email_hash() + |> put_email_status_verified() + end + + defp put_email_hash(changeset) do + case changeset do + %{valid?: true, changes: %{email: email}} -> + put_change(changeset, :email_hash, email) + + _ -> + changeset + end + end + + defp put_email_status_verified(changeset) do + status_verified = App.Ctx.get_status_verified() + + case changeset do + %{valid?: true} -> + put_change(changeset, :status, status_verified.id) + + _ -> + changeset + end + end + + @doc """ + `transform_profile_data_to_person/1` transforms the profile data + received from invoking `ElixirAuthGoogle.get_user_profile/1` + into a `person` record that can be inserted into the people table. + + ## Example + + iex> transform_profile_data_to_person(%{ + "email" => "nelson@gmail.com", + "email_verified" => true, + "family_name" => "Correia", + "given_name" => "Nelson", + "locale" => "en", + "name" => "Nelson Correia", + "picture" => "https://lh3.googleusercontent.com/a-/AAuE7mApnYb260YC1JY7a", + "sub" => "940732358705212133793" + }) + %{ + "email" => "nelson@gmail.com", + "email_verified" => true, + "family_name" => "Correia", + "given_name" => "Nelson", + "locale" => "en", + "name" => "Nelson Correia", + "picture" => "https://lh3.googleusercontent.com/a-/AAuE7mApnYb260YC1JY7a", + "sub" => "940732358705212133793" + "status" => 1, + "familyName" => "Correia", + "givenName" => "Nelson" + } + """ + def transform_profile_data_to_person(profile) do + profile + |> Map.put(:familyName, profile.family_name) + |> Map.put(:givenName, profile.given_name) + |> Map.put(:locale, profile.locale) + |> Map.put(:picture, profile.picture) + end + + @doc """ + Changeset function used for email/password registration + Define email hash and password hash + """ + def changeset_registration(profile, attrs) do + profile + |> cast(attrs, [:email, :password]) + |> validate_required([:email, :password]) + |> validate_length(:password, min: 6, max: 100) + |> unique_constraint(:email) + |> put_email_hash() + |> put_pass_hash() + end + + defp put_pass_hash(changeset) do + case changeset do + %Ecto.Changeset{valid?: true, changes: %{password: pass}} -> + put_change(changeset, :password_hash, pass) + + _ -> + changeset + end + end +end diff --git a/lib/auth/session.ex b/lib/auth/session.ex new file mode 100644 index 00000000..1feff11a --- /dev/null +++ b/lib/auth/session.ex @@ -0,0 +1,23 @@ +defmodule Auth.Session do + use Ecto.Schema + import Ecto.Changeset + + schema "sessions" do + field :auth_token, Fields.Encrypted + field :refresh_token, Fields.Encrypted + field :key_id, :integer + + belongs_to :person, App.Ctx.Person + timestamps() + end + + def changeset(people, attrs) do + Ecto.build_assoc(people, :sessions) + |> cast(attrs, [:auth_token, :refresh_token]) + |> validate_required([:auth_token, :refresh_token]) + end + + def basic_changeset(people, _attrs) do + Ecto.build_assoc(people, :sessions) + end +end diff --git a/lib/auth/status.ex b/lib/auth/status.ex new file mode 100644 index 00000000..eb391426 --- /dev/null +++ b/lib/auth/status.ex @@ -0,0 +1,18 @@ +defmodule Auth.Status do + use Ecto.Schema + import Ecto.Changeset + + schema "status" do + field :text, :string + belongs_to :person, App.Ctx.Person + + timestamps() + end + + @doc false + def changeset(status, attrs) do + status + |> cast(attrs, [:text]) + |> validate_required([:text]) + end +end diff --git a/lib/auth/tag.ex b/lib/auth/tag.ex new file mode 100644 index 00000000..0bc5f945 --- /dev/null +++ b/lib/auth/tag.ex @@ -0,0 +1,17 @@ +defmodule Auth.Tag do + use Ecto.Schema + import Ecto.Changeset + + schema "tags" do + field :text, :string + belongs_to :person, App.Ctx.Person + timestamps() + end + + @doc false + def changeset(tag, attrs) do + tag + |> cast(attrs, [:text]) + |> validate_required([:text]) + end +end From 034af35150fd7bcea9c3e8d45168128c836c480e Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 22:16:47 +0100 Subject: [PATCH 013/189] comment out required fields in person (only require email) #45 --- lib/auth/person.ex | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 10b7eefa..885f8131 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -37,12 +37,12 @@ defmodule Auth.Person do :picture ]) |> validate_required([ - :username, + # :username, :email, - :givenName, - :familyName, - :password_hash, - :key_id + # :givenName, + # :familyName, + # :password_hash, + # :key_id ]) |> put_email_hash() end From 6ba651e3eeb83e5797fab028588662143c75a843 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 22:40:25 +0100 Subject: [PATCH 014/189] add elixir_auth_github 1.1.0 and elixir_auth_google 1.1.0 to mix.exs --- mix.exs | 3 ++- mix.lock | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 5d217f98..f7ddb785 100644 --- a/mix.exs +++ b/mix.exs @@ -57,7 +57,8 @@ defmodule Auth.Mixfile do {:fields, "~> 2.3.1"}, # Auth: - + {:elixir_auth_github, "~> 1.1"}, + {:elixir_auth_google, "~> 1.1"}, # check test coverage {:excoveralls, "~> 0.6", only: :test}, diff --git a/mix.lock b/mix.lock index 0fe66eb6..dbb79e2b 100644 --- a/mix.lock +++ b/mix.lock @@ -9,6 +9,8 @@ "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, "ecto": {:hex, :ecto, "3.4.0", "a7a83ab8359bf816ce729e5e65981ce25b9fc5adfc89c2ea3980f4fed0bfd7c1", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "5eed18252f5b5bbadec56a24112b531343507dbe046273133176b12190ce19cc"}, "ecto_sql": {:hex, :ecto_sql, "3.4.1", "3c9136ba138f9b74d31286c73c61232a92bd19385f7c5607bdeb3a4587ef91f5", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b4be0bffe7b0bdf5393defcae52712f248e70cc2bc0e8ab6ddb03be66371516"}, + "elixir_auth_github": {:hex, :elixir_auth_github, "1.1.0", "7cc0e88395d41e3062ba06acfab6990172e66017ac0d367b9130ac20bed255b3", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "b0a72250186d46ce59bf947addfb087904a1ad14fece59126af83f497c0aec2b"}, + "elixir_auth_google": {:hex, :elixir_auth_google, "1.1.0", "f7b4df39e6c9d8864992de9dc1d7b10566927d6e678eecec6790485021b128b5", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "2b99212d14bb992f126b866fb4674deedd2c6f9d270aec383e82c8065371ac60"}, "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, "excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"}, "fields": {:hex, :fields, "2.3.1", "30102c844f79a9024cf4e07a1595427c8007bd301f1c6ddd689a31f0f0cbd622", [:mix], [{:argon2_elixir, "~> 2.3.0", [hex: :argon2_elixir, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:html_sanitize_ex, "~> 1.4", [hex: :html_sanitize_ex, repo: "hexpm", optional: false]}], "hexpm", "fc3067bf3226c43aeaf3e7eab2524785da28507fd064d858b61ddac4d971cec8"}, @@ -16,6 +18,7 @@ "gettext": {:hex, :gettext, "0.17.4", "f13088e1ec10ce01665cf25f5ff779e7df3f2dc71b37084976cf89d1aa124d5c", [:mix], [], "hexpm", "3c75b5ea8288e2ee7ea503ff9e30dfe4d07ad3c054576a6e60040e79a801e14d"}, "hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "e0100f8ef7d1124222c11ad362c857d3df7cb5f4204054f9f0f4a728666591fc"}, "html_sanitize_ex": {:hex, :html_sanitize_ex, "1.4.0", "0310d27d7bafb662f30bff22ec732a72414799c83eaf44239781fd23b96216c0", [:mix], [{:mochiweb, "~> 2.15", [hex: :mochiweb, repo: "hexpm", optional: false]}], "hexpm", "c5d79626be0b6e50c19ecdfb783ee26e85bd3a77436b488379ce6dc104ec4593"}, + "httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, "jason": {:hex, :jason, "1.2.0", "10043418c42d2493d0ee212d3fddd25d7ffe484380afad769a0a38795938e448", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "116747dbe057794c3a3e4e143b7c8390b29f634e16c78a7f59ba75bfa6852e7f"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, @@ -31,6 +34,7 @@ "plug": {:hex, :plug, "1.10.0", "6508295cbeb4c654860845fb95260737e4a8838d34d115ad76cd487584e2fc4d", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "422a9727e667be1bf5ab1de03be6fa0ad67b775b2d84ed908f3264415ef29d4a"}, "plug_cowboy": {:hex, :plug_cowboy, "2.1.2", "8b0addb5908c5238fac38e442e81b6fcd32788eaa03246b4d55d147c47c5805e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "7d722581ce865a237e14da6d946f92704101740a256bd13ec91e63c0b122fc70"}, "plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"}, + "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, "postgrex": {:hex, :postgrex, "0.15.3", "5806baa8a19a68c4d07c7a624ccdb9b57e89cbc573f1b98099e3741214746ae4", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "4737ce62a31747b4c63c12b20c62307e51bb4fcd730ca0c32c280991e0606c90"}, "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm", "451d8527787df716d99dc36162fca05934915db0b6141bbdac2ea8d3c7afc7d7"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.5", "6eaf7ad16cb568bb01753dbbd7a95ff8b91c7979482b95f38443fe2c8852a79b", [:make, :mix, :rebar3], [], "hexpm", "13104d7897e38ed7f044c4de953a6c28597d1c952075eb2e328bc6d6f2bfc496"}, From 8d28824b75d5ad44c5d4af96e64e21b73f56768a Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 22:44:17 +0100 Subject: [PATCH 015/189] replace App.Ctx with Auth. #45 --- lib/auth/person.ex | 4 ++-- lib/auth/session.ex | 2 +- lib/auth/status.ex | 6 +++++- lib/auth/tag.ex | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 885f8131..5247478c 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -17,7 +17,7 @@ defmodule Auth.Person do field :tag, :id field :key_id, :integer - has_many :sessions, App.Ctx.Session, on_delete: :delete_all + has_many :sessions, Auth.Session, on_delete: :delete_all timestamps() end @@ -70,7 +70,7 @@ defmodule Auth.Person do end defp put_email_status_verified(changeset) do - status_verified = App.Ctx.get_status_verified() + status_verified = Auth.Status.get_status_verified() case changeset do %{valid?: true} -> diff --git a/lib/auth/session.ex b/lib/auth/session.ex index 1feff11a..afb22cc9 100644 --- a/lib/auth/session.ex +++ b/lib/auth/session.ex @@ -7,7 +7,7 @@ defmodule Auth.Session do field :refresh_token, Fields.Encrypted field :key_id, :integer - belongs_to :person, App.Ctx.Person + belongs_to :person, Auth.Person timestamps() end diff --git a/lib/auth/status.ex b/lib/auth/status.ex index eb391426..da1b8061 100644 --- a/lib/auth/status.ex +++ b/lib/auth/status.ex @@ -4,7 +4,7 @@ defmodule Auth.Status do schema "status" do field :text, :string - belongs_to :person, App.Ctx.Person + belongs_to :person, Auth.Person timestamps() end @@ -15,4 +15,8 @@ defmodule Auth.Status do |> cast(attrs, [:text]) |> validate_required([:text]) end + + def get_status_verified() do + Auth.Repo.get_by(Status, text: "verified") + end end diff --git a/lib/auth/tag.ex b/lib/auth/tag.ex index 0bc5f945..ad5d0709 100644 --- a/lib/auth/tag.ex +++ b/lib/auth/tag.ex @@ -4,7 +4,7 @@ defmodule Auth.Tag do schema "tags" do field :text, :string - belongs_to :person, App.Ctx.Person + belongs_to :person, Auth.Person timestamps() end From 96a038158047eaa385fa815a01d704cec9c8c44c Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 23:26:55 +0100 Subject: [PATCH 016/189] enable GitHub OAuth #45 --- .../controllers/github_auth_controller.ex | 13 +++++++ lib/auth_web/controllers/page_controller.ex | 3 +- lib/auth_web/router.ex | 1 + lib/auth_web/templates/page/index.html.eex | 38 +++---------------- .../templates/page/welcome_github.html.eex | 9 +++++ 5 files changed, 30 insertions(+), 34 deletions(-) create mode 100644 lib/auth_web/controllers/github_auth_controller.ex create mode 100644 lib/auth_web/templates/page/welcome_github.html.eex diff --git a/lib/auth_web/controllers/github_auth_controller.ex b/lib/auth_web/controllers/github_auth_controller.ex new file mode 100644 index 00000000..3694064b --- /dev/null +++ b/lib/auth_web/controllers/github_auth_controller.ex @@ -0,0 +1,13 @@ +defmodule AuthWeb.GithubAuthController do + use AuthWeb, :controller + + @doc """ + `index/2` handles the callback from GitHub Auth API redirect. + """ + def index(conn, %{"code" => code}) do + {:ok, profile} = ElixirAuthGithub.github_auth(code) + conn + |> put_view(AuthWeb.PageView) + |> render(:welcome_github, profile: profile) + end +end diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index b745074f..b4bb3aec 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -2,6 +2,7 @@ defmodule AuthWeb.PageController do use AuthWeb, :controller def index(conn, _params) do - render(conn, "index.html") + oauth_github_url = ElixirAuthGithub.login_url(%{scopes: ["user:email"]}) + render(conn, "index.html", [oauth_github_url: oauth_github_url]) end end diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index 1cf1602d..f9a4d9a3 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -17,6 +17,7 @@ defmodule AuthWeb.Router do pipe_through :browser get "/", PageController, :index + get "/auth/github/callback", GithubAuthController, :index end # Other scopes may use custom stacks. diff --git a/lib/auth_web/templates/page/index.html.eex b/lib/auth_web/templates/page/index.html.eex index 8cbd9d83..88cd1675 100644 --- a/lib/auth_web/templates/page/index.html.eex +++ b/lib/auth_web/templates/page/index.html.eex @@ -1,35 +1,7 @@
-

<%= gettext "Welcome to %{name}!", name: "Phoenix" %>

-

A productive web framework that
does not compromise speed or maintainability.

-
- -
- - +

Welcome to Awesome App!

+

To get started, login to your GitHub Account:

+ + Sign in with GitHub +

diff --git a/lib/auth_web/templates/page/welcome_github.html.eex b/lib/auth_web/templates/page/welcome_github.html.eex new file mode 100644 index 00000000..ac95b82c --- /dev/null +++ b/lib/auth_web/templates/page/welcome_github.html.eex @@ -0,0 +1,9 @@ +
+

Welcome <%= @profile.name %>! + +

+

You are signed in + with your GitHub Account
+ <%= @profile.email %> +

+

From d4905b16a6ea2dba3ddc925a4d7b6ef1598ed35d Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 23:37:13 +0100 Subject: [PATCH 017/189] enable Google OAuth login #45 --- lib/auth_web/controllers/google_auth_controller.ex | 14 ++++++++++++++ lib/auth_web/controllers/page_controller.ex | 6 +++++- lib/auth_web/router.ex | 1 + lib/auth_web/templates/page/index.html.eex | 6 +++++- .../templates/page/welcome_google.html.eex | 9 +++++++++ 5 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 lib/auth_web/controllers/google_auth_controller.ex create mode 100644 lib/auth_web/templates/page/welcome_google.html.eex diff --git a/lib/auth_web/controllers/google_auth_controller.ex b/lib/auth_web/controllers/google_auth_controller.ex new file mode 100644 index 00000000..707d373d --- /dev/null +++ b/lib/auth_web/controllers/google_auth_controller.ex @@ -0,0 +1,14 @@ +defmodule AuthWeb.GoogleAuthController do + use AuthWeb, :controller + + @doc """ + `index/2` handles the callback from Google Auth API redirect. + """ + def index(conn, %{"code" => code}) do + {:ok, token} = ElixirAuthGoogle.get_token(code, conn) + {:ok, profile} = ElixirAuthGoogle.get_user_profile(token.access_token) + conn + |> put_view(AuthWeb.PageView) + |> render(:welcome_google, profile: profile) + end +end diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index b4bb3aec..584d9362 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -3,6 +3,10 @@ defmodule AuthWeb.PageController do def index(conn, _params) do oauth_github_url = ElixirAuthGithub.login_url(%{scopes: ["user:email"]}) - render(conn, "index.html", [oauth_github_url: oauth_github_url]) + oauth_google_url = ElixirAuthGoogle.generate_oauth_url(conn) + render(conn, "index.html", [ + oauth_github_url: oauth_github_url, + oauth_google_url: oauth_google_url + ]) end end diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index f9a4d9a3..a0a6d475 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -18,6 +18,7 @@ defmodule AuthWeb.Router do get "/", PageController, :index get "/auth/github/callback", GithubAuthController, :index + get "/auth/google/callback", GoogleAuthController, :index end # Other scopes may use custom stacks. diff --git a/lib/auth_web/templates/page/index.html.eex b/lib/auth_web/templates/page/index.html.eex index 88cd1675..6e634985 100644 --- a/lib/auth_web/templates/page/index.html.eex +++ b/lib/auth_web/templates/page/index.html.eex @@ -1,7 +1,11 @@

Welcome to Awesome App!

-

To get started, login to your GitHub Account:

+

To get started, login to your GitHub or Google Account:

Sign in with GitHub + + + Sign in with Google +

diff --git a/lib/auth_web/templates/page/welcome_google.html.eex b/lib/auth_web/templates/page/welcome_google.html.eex new file mode 100644 index 00000000..72c3b063 --- /dev/null +++ b/lib/auth_web/templates/page/welcome_google.html.eex @@ -0,0 +1,9 @@ +
+

Welcome <%= @profile.given_name %>! + +

+

You are signed in + with your Google Account
+ <%= @profile.email %> +

+

From c36c32a75af39b86980c1d38fd0a9d6e8c58bf64 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 23:39:06 +0100 Subject: [PATCH 018/189] fix failing test --- test/auth_web/controllers/page_controller_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/auth_web/controllers/page_controller_test.exs b/test/auth_web/controllers/page_controller_test.exs index a7d4dcbe..68c7bd12 100644 --- a/test/auth_web/controllers/page_controller_test.exs +++ b/test/auth_web/controllers/page_controller_test.exs @@ -3,6 +3,6 @@ defmodule AuthWeb.PageControllerTest do test "GET /", %{conn: conn} do conn = get(conn, "/") - assert html_response(conn, 200) =~ "Welcome to Phoenix!" + assert html_response(conn, 200) =~ "login to" end end From a3956cb7a83a10b74b802e2c6f3e45e28bc4605b Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 1 Apr 2020 23:52:30 +0100 Subject: [PATCH 019/189] use svg buttons #44 --- lib/auth_web/templates/page/index.html.eex | 43 ++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/lib/auth_web/templates/page/index.html.eex b/lib/auth_web/templates/page/index.html.eex index 6e634985..cde00aef 100644 --- a/lib/auth_web/templates/page/index.html.eex +++ b/lib/auth_web/templates/page/index.html.eex @@ -9,3 +9,46 @@ Sign in with Google + + +
+ + +
+ + + +
+
+ Sign in with GitHub +
+
+ + +
+ + + + + + +
+
+ Sign in with Google +
+
+
From 1e55b0bda036894fe689886f3c27c8bfc756bd10 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 2 Apr 2020 00:10:51 +0100 Subject: [PATCH 020/189] scale up SVG buttons #44 --- lib/auth_web/templates/page/index.html.eex | 89 ++++++++++------------ 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/lib/auth_web/templates/page/index.html.eex b/lib/auth_web/templates/page/index.html.eex index cde00aef..efa82b88 100644 --- a/lib/auth_web/templates/page/index.html.eex +++ b/lib/auth_web/templates/page/index.html.eex @@ -1,54 +1,45 @@

Welcome to Awesome App!

To get started, login to your GitHub or Google Account:

- - Sign in with GitHub - +

- - -
- - -
- - - -
-
- Sign in with GitHub -
-
- - -
- - - - - - -
-
- Sign in with Google -
-
-
From 686934db6f5cc0025948d9171193a2f6e871ec14 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 2 Apr 2020 00:20:52 +0100 Subject: [PATCH 021/189] tidy up layout template --- lib/auth_web/templates/layout/app.html.eex | 12 ------------ lib/auth_web/templates/page/index.html.eex | 2 +- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/lib/auth_web/templates/layout/app.html.eex b/lib/auth_web/templates/layout/app.html.eex index 4968fd3f..a4b98c61 100644 --- a/lib/auth_web/templates/layout/app.html.eex +++ b/lib/auth_web/templates/layout/app.html.eex @@ -9,18 +9,6 @@ <%= csrf_meta_tag() %> -
-
- - -
-
diff --git a/lib/auth_web/templates/page/index.html.eex b/lib/auth_web/templates/page/index.html.eex index efa82b88..91e0aa9f 100644 --- a/lib/auth_web/templates/page/index.html.eex +++ b/lib/auth_web/templates/page/index.html.eex @@ -1,6 +1,6 @@

Welcome to Awesome App!

-

To get started, login to your GitHub or Google Account:

+

To get started, login to your GitHub or Google Account:

Date: Thu, 2 Apr 2020 23:23:06 +0100 Subject: [PATCH 022/189] use latest version of elixir_auth_google with dependency injection https://github.com/dwyl/elixir-auth-google/issues/35 --- .../controllers/google_auth_controller.ex | 4 + lib/auth_web/controllers/page_controller.ex | 3 + lib/auth_web/templates/page/index.html.eex | 81 ++++++++++--------- mix.exs | 2 +- mix.lock | 2 +- .../google_auth_controller_test.exs | 15 ++++ 6 files changed, 65 insertions(+), 42 deletions(-) create mode 100644 test/auth_web/controllers/google_auth_controller_test.exs diff --git a/lib/auth_web/controllers/google_auth_controller.ex b/lib/auth_web/controllers/google_auth_controller.ex index 707d373d..1282049e 100644 --- a/lib/auth_web/controllers/google_auth_controller.ex +++ b/lib/auth_web/controllers/google_auth_controller.ex @@ -5,7 +5,11 @@ defmodule AuthWeb.GoogleAuthController do `index/2` handles the callback from Google Auth API redirect. """ def index(conn, %{"code" => code}) do + env = Mix.env() + IO.inspect(env, label: "auth env") + IO.inspect(System.get_env("MIX_ENV"), label: "MIX_ENV:10") {:ok, token} = ElixirAuthGoogle.get_token(code, conn) + IO.inspect(token, label: "token") {:ok, profile} = ElixirAuthGoogle.get_user_profile(token.access_token) conn |> put_view(AuthWeb.PageView) diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index 584d9362..194f562d 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -1,6 +1,9 @@ defmodule AuthWeb.PageController do use AuthWeb, :controller + @elixir_auth_google Application.get_env(:auth_mvp, :elixir_auth_google) || ElixirAuthGoogle + @elixir_auth_github Application.get_env(:auth_mvp, :elixir_auth_github) || ElixirAuthGithub + def index(conn, _params) do oauth_github_url = ElixirAuthGithub.login_url(%{scopes: ["user:email"]}) oauth_google_url = ElixirAuthGoogle.generate_oauth_url(conn) diff --git a/lib/auth_web/templates/page/index.html.eex b/lib/auth_web/templates/page/index.html.eex index 91e0aa9f..ada3b280 100644 --- a/lib/auth_web/templates/page/index.html.eex +++ b/lib/auth_web/templates/page/index.html.eex @@ -1,45 +1,46 @@

Welcome to Awesome App!

To get started, login to your GitHub or Google Account:

-
diff --git a/mix.exs b/mix.exs index f7ddb785..260f31c5 100644 --- a/mix.exs +++ b/mix.exs @@ -58,7 +58,7 @@ defmodule Auth.Mixfile do # Auth: {:elixir_auth_github, "~> 1.1"}, - {:elixir_auth_google, "~> 1.1"}, + {:elixir_auth_google, "~> 1.1.5"}, # check test coverage {:excoveralls, "~> 0.6", only: :test}, diff --git a/mix.lock b/mix.lock index dbb79e2b..c6d2274a 100644 --- a/mix.lock +++ b/mix.lock @@ -10,7 +10,7 @@ "ecto": {:hex, :ecto, "3.4.0", "a7a83ab8359bf816ce729e5e65981ce25b9fc5adfc89c2ea3980f4fed0bfd7c1", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "5eed18252f5b5bbadec56a24112b531343507dbe046273133176b12190ce19cc"}, "ecto_sql": {:hex, :ecto_sql, "3.4.1", "3c9136ba138f9b74d31286c73c61232a92bd19385f7c5607bdeb3a4587ef91f5", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b4be0bffe7b0bdf5393defcae52712f248e70cc2bc0e8ab6ddb03be66371516"}, "elixir_auth_github": {:hex, :elixir_auth_github, "1.1.0", "7cc0e88395d41e3062ba06acfab6990172e66017ac0d367b9130ac20bed255b3", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "b0a72250186d46ce59bf947addfb087904a1ad14fece59126af83f497c0aec2b"}, - "elixir_auth_google": {:hex, :elixir_auth_google, "1.1.0", "f7b4df39e6c9d8864992de9dc1d7b10566927d6e678eecec6790485021b128b5", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "2b99212d14bb992f126b866fb4674deedd2c6f9d270aec383e82c8065371ac60"}, + "elixir_auth_google": {:hex, :elixir_auth_google, "1.1.4", "656a693b5f11729fcc14d23d93289883db0dd3149c80f8fd8b0500e53f86a5eb", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "231d009be276b64b35cd775904c7c2ee3ecbbf04582acc4cd56f2878ce835738"}, "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, "excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"}, "fields": {:hex, :fields, "2.3.1", "30102c844f79a9024cf4e07a1595427c8007bd301f1c6ddd689a31f0f0cbd622", [:mix], [{:argon2_elixir, "~> 2.3.0", [hex: :argon2_elixir, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:html_sanitize_ex, "~> 1.4", [hex: :html_sanitize_ex, repo: "hexpm", optional: false]}], "hexpm", "fc3067bf3226c43aeaf3e7eab2524785da28507fd064d858b61ddac4d971cec8"}, diff --git a/test/auth_web/controllers/google_auth_controller_test.exs b/test/auth_web/controllers/google_auth_controller_test.exs new file mode 100644 index 00000000..62e67e58 --- /dev/null +++ b/test/auth_web/controllers/google_auth_controller_test.exs @@ -0,0 +1,15 @@ +defmodule AuthWeb.GoogleAuthControllerTest do + use AuthWeb.ConnCase + + test "index/2 handler for google auth callback", %{conn: conn} do + + conn = get(conn, Routes.google_auth_path(conn, :index, %{code: "234", state: "http://localhost/"})) + # IO.inspect(conn, label: "conn") + + assert html_response(conn, 200) =~ "nelson@gmail.com" + + # # same again to exercise to the branch where person already exists: + # conn = get(conn, Routes.google_auth_path(conn, :index, %{code: "234", state: "http://localhost/"})) + # assert html_response(conn, 302) + end +end From 91b0e8f6bd86c65179acac806513058200092ade Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 2 Apr 2020 23:38:20 +0100 Subject: [PATCH 023/189] use v1.2.0 of elixir_auth_google with TestDouble! https://github.com/dwyl/elixir-auth-google/issues/35 --- mix.exs | 6 +++--- mix.lock | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index 260f31c5..8f800b15 100644 --- a/mix.exs +++ b/mix.exs @@ -4,8 +4,8 @@ defmodule Auth.Mixfile do def project do [ app: :auth, - version: "1.0.0", - elixir: "~> 1.5", + version: "1.2.0", + elixir: "~> 1.9", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix, :gettext] ++ Mix.compilers(), test_coverage: [tool: ExCoveralls], @@ -58,7 +58,7 @@ defmodule Auth.Mixfile do # Auth: {:elixir_auth_github, "~> 1.1"}, - {:elixir_auth_google, "~> 1.1.5"}, + {:elixir_auth_google, "~> 1.2.0"}, # check test coverage {:excoveralls, "~> 0.6", only: :test}, diff --git a/mix.lock b/mix.lock index c6d2274a..5f28c905 100644 --- a/mix.lock +++ b/mix.lock @@ -10,7 +10,7 @@ "ecto": {:hex, :ecto, "3.4.0", "a7a83ab8359bf816ce729e5e65981ce25b9fc5adfc89c2ea3980f4fed0bfd7c1", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "5eed18252f5b5bbadec56a24112b531343507dbe046273133176b12190ce19cc"}, "ecto_sql": {:hex, :ecto_sql, "3.4.1", "3c9136ba138f9b74d31286c73c61232a92bd19385f7c5607bdeb3a4587ef91f5", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b4be0bffe7b0bdf5393defcae52712f248e70cc2bc0e8ab6ddb03be66371516"}, "elixir_auth_github": {:hex, :elixir_auth_github, "1.1.0", "7cc0e88395d41e3062ba06acfab6990172e66017ac0d367b9130ac20bed255b3", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "b0a72250186d46ce59bf947addfb087904a1ad14fece59126af83f497c0aec2b"}, - "elixir_auth_google": {:hex, :elixir_auth_google, "1.1.4", "656a693b5f11729fcc14d23d93289883db0dd3149c80f8fd8b0500e53f86a5eb", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "231d009be276b64b35cd775904c7c2ee3ecbbf04582acc4cd56f2878ce835738"}, + "elixir_auth_google": {:hex, :elixir_auth_google, "1.2.0", "99672af2cadcf18bad10548a75667dada2f802cf1a8c519d2283346c8b8e99b8", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "8720fd473f471a9a03d6630fc3c8b90dff5e089047e153dc1f78ed68a68740a9"}, "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, "excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"}, "fields": {:hex, :fields, "2.3.1", "30102c844f79a9024cf4e07a1595427c8007bd301f1c6ddd689a31f0f0cbd622", [:mix], [{:argon2_elixir, "~> 2.3.0", [hex: :argon2_elixir, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:html_sanitize_ex, "~> 1.4", [hex: :html_sanitize_ex, repo: "hexpm", optional: false]}], "hexpm", "fc3067bf3226c43aeaf3e7eab2524785da28507fd064d858b61ddac4d971cec8"}, From 45d01f4ae0c764704242ccb4ccedce8039a1415e Mon Sep 17 00:00:00 2001 From: nelsonic Date: Fri, 3 Apr 2020 15:13:22 +0100 Subject: [PATCH 024/189] update AuthWeb.GithubAuthControllerTest to assert "test@gmail.com" --- .../controllers/google_auth_controller.ex | 4 ---- mix.exs | 2 +- mix.lock | 2 +- .../controllers/github_auth_controller_test.exs | 16 ++++++++++++++++ 4 files changed, 18 insertions(+), 6 deletions(-) create mode 100644 test/auth_web/controllers/github_auth_controller_test.exs diff --git a/lib/auth_web/controllers/google_auth_controller.ex b/lib/auth_web/controllers/google_auth_controller.ex index 1282049e..707d373d 100644 --- a/lib/auth_web/controllers/google_auth_controller.ex +++ b/lib/auth_web/controllers/google_auth_controller.ex @@ -5,11 +5,7 @@ defmodule AuthWeb.GoogleAuthController do `index/2` handles the callback from Google Auth API redirect. """ def index(conn, %{"code" => code}) do - env = Mix.env() - IO.inspect(env, label: "auth env") - IO.inspect(System.get_env("MIX_ENV"), label: "MIX_ENV:10") {:ok, token} = ElixirAuthGoogle.get_token(code, conn) - IO.inspect(token, label: "token") {:ok, profile} = ElixirAuthGoogle.get_user_profile(token.access_token) conn |> put_view(AuthWeb.PageView) diff --git a/mix.exs b/mix.exs index 8f800b15..1dc0549d 100644 --- a/mix.exs +++ b/mix.exs @@ -57,7 +57,7 @@ defmodule Auth.Mixfile do {:fields, "~> 2.3.1"}, # Auth: - {:elixir_auth_github, "~> 1.1"}, + {:elixir_auth_github, "~> 1.1.5"}, {:elixir_auth_google, "~> 1.2.0"}, # check test coverage diff --git a/mix.lock b/mix.lock index 5f28c905..2ec89bbd 100644 --- a/mix.lock +++ b/mix.lock @@ -9,7 +9,7 @@ "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, "ecto": {:hex, :ecto, "3.4.0", "a7a83ab8359bf816ce729e5e65981ce25b9fc5adfc89c2ea3980f4fed0bfd7c1", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "5eed18252f5b5bbadec56a24112b531343507dbe046273133176b12190ce19cc"}, "ecto_sql": {:hex, :ecto_sql, "3.4.1", "3c9136ba138f9b74d31286c73c61232a92bd19385f7c5607bdeb3a4587ef91f5", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b4be0bffe7b0bdf5393defcae52712f248e70cc2bc0e8ab6ddb03be66371516"}, - "elixir_auth_github": {:hex, :elixir_auth_github, "1.1.0", "7cc0e88395d41e3062ba06acfab6990172e66017ac0d367b9130ac20bed255b3", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "b0a72250186d46ce59bf947addfb087904a1ad14fece59126af83f497c0aec2b"}, + "elixir_auth_github": {:hex, :elixir_auth_github, "1.1.5", "edb8d171dc6c3c5fcc6c7a9526f2a6909b04458ee58e6d39e9ee8fb0e6f272a4", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "24ca6ae0e1e5a56f422f2775a5a7271b7a1f0c997d69d3dcc10abc99f8333092"}, "elixir_auth_google": {:hex, :elixir_auth_google, "1.2.0", "99672af2cadcf18bad10548a75667dada2f802cf1a8c519d2283346c8b8e99b8", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "8720fd473f471a9a03d6630fc3c8b90dff5e089047e153dc1f78ed68a68740a9"}, "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, "excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"}, diff --git a/test/auth_web/controllers/github_auth_controller_test.exs b/test/auth_web/controllers/github_auth_controller_test.exs new file mode 100644 index 00000000..353e35e6 --- /dev/null +++ b/test/auth_web/controllers/github_auth_controller_test.exs @@ -0,0 +1,16 @@ +defmodule AuthWeb.GithubAuthControllerTest do + use AuthWeb.ConnCase + + test "index/2 handler for google auth callback", %{conn: conn} do + + conn = get(conn, Routes.github_auth_path(conn, :index, + %{code: "123", state: "http://localhost/"})) + # IO.inspect(conn, label: "conn") + + assert html_response(conn, 200) =~ "test@gmail.com" + + # # same again to exercise to the branch where person already exists: + # conn = get(conn, Routes.google_auth_path(conn, :index, %{code: "234", state: "http://localhost/"})) + # assert html_response(conn, 302) + end +end From 556f80e0155528334a46ac3d6f0c8c46670bba00 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Fri, 3 Apr 2020 22:09:10 +0100 Subject: [PATCH 025/189] use latest version of auth packages 1.2.0 with TestDouble simplifies code and tests! --- mix.exs | 2 +- mix.lock | 2 +- .../auth_web/controllers/github_auth_controller_test.exs | 6 ------ .../auth_web/controllers/google_auth_controller_test.exs | 9 ++------- 4 files changed, 4 insertions(+), 15 deletions(-) diff --git a/mix.exs b/mix.exs index 1dc0549d..e98b21f6 100644 --- a/mix.exs +++ b/mix.exs @@ -57,7 +57,7 @@ defmodule Auth.Mixfile do {:fields, "~> 2.3.1"}, # Auth: - {:elixir_auth_github, "~> 1.1.5"}, + {:elixir_auth_github, "~> 1.2.0"}, {:elixir_auth_google, "~> 1.2.0"}, # check test coverage diff --git a/mix.lock b/mix.lock index 2ec89bbd..d1f832e7 100644 --- a/mix.lock +++ b/mix.lock @@ -9,7 +9,7 @@ "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, "ecto": {:hex, :ecto, "3.4.0", "a7a83ab8359bf816ce729e5e65981ce25b9fc5adfc89c2ea3980f4fed0bfd7c1", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "5eed18252f5b5bbadec56a24112b531343507dbe046273133176b12190ce19cc"}, "ecto_sql": {:hex, :ecto_sql, "3.4.1", "3c9136ba138f9b74d31286c73c61232a92bd19385f7c5607bdeb3a4587ef91f5", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b4be0bffe7b0bdf5393defcae52712f248e70cc2bc0e8ab6ddb03be66371516"}, - "elixir_auth_github": {:hex, :elixir_auth_github, "1.1.5", "edb8d171dc6c3c5fcc6c7a9526f2a6909b04458ee58e6d39e9ee8fb0e6f272a4", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "24ca6ae0e1e5a56f422f2775a5a7271b7a1f0c997d69d3dcc10abc99f8333092"}, + "elixir_auth_github": {:hex, :elixir_auth_github, "1.2.0", "b7233c8de0c0c85c6fcd32f0ab6e3d32d6ccb90262d3efbf6bb064d3551369d8", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "7f11269bf0170af6b61e17653bf8fa922347a08ceac59a9fba0e27c980c28938"}, "elixir_auth_google": {:hex, :elixir_auth_google, "1.2.0", "99672af2cadcf18bad10548a75667dada2f802cf1a8c519d2283346c8b8e99b8", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "8720fd473f471a9a03d6630fc3c8b90dff5e089047e153dc1f78ed68a68740a9"}, "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, "excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"}, diff --git a/test/auth_web/controllers/github_auth_controller_test.exs b/test/auth_web/controllers/github_auth_controller_test.exs index 353e35e6..79648941 100644 --- a/test/auth_web/controllers/github_auth_controller_test.exs +++ b/test/auth_web/controllers/github_auth_controller_test.exs @@ -5,12 +5,6 @@ defmodule AuthWeb.GithubAuthControllerTest do conn = get(conn, Routes.github_auth_path(conn, :index, %{code: "123", state: "http://localhost/"})) - # IO.inspect(conn, label: "conn") - assert html_response(conn, 200) =~ "test@gmail.com" - - # # same again to exercise to the branch where person already exists: - # conn = get(conn, Routes.google_auth_path(conn, :index, %{code: "234", state: "http://localhost/"})) - # assert html_response(conn, 302) end end diff --git a/test/auth_web/controllers/google_auth_controller_test.exs b/test/auth_web/controllers/google_auth_controller_test.exs index 62e67e58..d514f478 100644 --- a/test/auth_web/controllers/google_auth_controller_test.exs +++ b/test/auth_web/controllers/google_auth_controller_test.exs @@ -3,13 +3,8 @@ defmodule AuthWeb.GoogleAuthControllerTest do test "index/2 handler for google auth callback", %{conn: conn} do - conn = get(conn, Routes.google_auth_path(conn, :index, %{code: "234", state: "http://localhost/"})) - # IO.inspect(conn, label: "conn") - + conn = get(conn, Routes.google_auth_path(conn, :index, + %{code: "234", state: "http://localhost/"})) assert html_response(conn, 200) =~ "nelson@gmail.com" - - # # same again to exercise to the branch where person already exists: - # conn = get(conn, Routes.google_auth_path(conn, :index, %{code: "234", state: "http://localhost/"})) - # assert html_response(conn, 302) end end From c24889c7e64b0970d1fde032b6d892dd315eec4f Mon Sep 17 00:00:00 2001 From: nelsonic Date: Fri, 3 Apr 2020 22:09:53 +0100 Subject: [PATCH 026/189] create get_referer/1 function to extract HTTP referer --- lib/auth_web/controllers/page_controller.ex | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index 194f562d..cee74670 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -1,15 +1,28 @@ defmodule AuthWeb.PageController do use AuthWeb, :controller - @elixir_auth_google Application.get_env(:auth_mvp, :elixir_auth_google) || ElixirAuthGoogle - @elixir_auth_github Application.get_env(:auth_mvp, :elixir_auth_github) || ElixirAuthGithub - def index(conn, _params) do - oauth_github_url = ElixirAuthGithub.login_url(%{scopes: ["user:email"]}) + get_referer(conn) + oauth_github_url = + ElixirAuthGithub.login_url(%{scopes: ["user:email"]}) oauth_google_url = ElixirAuthGoogle.generate_oauth_url(conn) render(conn, "index.html", [ oauth_github_url: oauth_github_url, oauth_google_url: oauth_google_url ]) end + + + def get_referer(conn) do + # IO.inspect(conn, label: "extact_referer/1:16 conn") + # https://stackoverflow.com/questions/37176911/get-http-referrer + case List.keyfind(conn.req_headers, "referer", 0) do + {"referer", referer} -> + IO.puts referer + nil -> + IO.puts "no referer" + end + conn + end + end From 9d3887c9e36b488fe4097c0e9eb78218f77c7447 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Sun, 12 Apr 2020 23:50:31 +0100 Subject: [PATCH 027/189] add /admin route, handler and template for testing referer #46 --- auth.iml | 151 ++++++++++++++++++++ lib/auth_web/controllers/page_controller.ex | 17 ++- lib/auth_web/router.ex | 1 + lib/auth_web/templates/page/admin.html.eex | 2 + 4 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 auth.iml create mode 100644 lib/auth_web/templates/page/admin.html.eex diff --git a/auth.iml b/auth.iml new file mode 100644 index 00000000..c8aefe45 --- /dev/null +++ b/auth.iml @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index cee74670..18af3b5f 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -2,9 +2,9 @@ defmodule AuthWeb.PageController do use AuthWeb, :controller def index(conn, _params) do - get_referer(conn) + state = get_referer(conn) oauth_github_url = - ElixirAuthGithub.login_url(%{scopes: ["user:email"]}) + ElixirAuthGithub.login_url(%{scopes: ["user:email"], state: state}) oauth_google_url = ElixirAuthGoogle.generate_oauth_url(conn) render(conn, "index.html", [ oauth_github_url: oauth_github_url, @@ -12,17 +12,22 @@ defmodule AuthWeb.PageController do ]) end + def admin(conn, _params) do + conn + |> put_view(AuthWeb.PageView) + |> render(:admin) + end - def get_referer(conn) do - # IO.inspect(conn, label: "extact_referer/1:16 conn") + defp get_referer(conn) do # https://stackoverflow.com/questions/37176911/get-http-referrer case List.keyfind(conn.req_headers, "referer", 0) do {"referer", referer} -> - IO.puts referer + IO.inspect referer, label: "referer" + referer nil -> IO.puts "no referer" + "" end - conn end end diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index a0a6d475..d17ee8fa 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -17,6 +17,7 @@ defmodule AuthWeb.Router do pipe_through :browser get "/", PageController, :index + get "/admin", PageController, :admin get "/auth/github/callback", GithubAuthController, :index get "/auth/google/callback", GoogleAuthController, :index end diff --git a/lib/auth_web/templates/page/admin.html.eex b/lib/auth_web/templates/page/admin.html.eex new file mode 100644 index 00000000..7e8848b1 --- /dev/null +++ b/lib/auth_web/templates/page/admin.html.eex @@ -0,0 +1,2 @@ +Hello World! +Login From ee0de5bf4b24a64ef9f437876a0d4d8b98021b77 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Mon, 13 Apr 2020 22:29:06 +0100 Subject: [PATCH 028/189] add ADMIN_EMAIL environment variable to .env_sample for #47 --- .env_sample | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.env_sample b/.env_sample index ba216153..8eccd2ae 100644 --- a/.env_sample +++ b/.env_sample @@ -1,3 +1,7 @@ +export ADMIN_EMAIL=youremail@gmail.com +export EMAIL_APP_URL=https://dwylmail.herokuapp.com +export GITHUB_CLIENT_ID=CreateGitHubApp +export GITHUB_CLIENT_SECRET=SuperSecret export GOOGLE_CLIENT_ID=YourAppsClientId.apps.googleusercontent.com export GOOGLE_CLIENT_SECRET=SuperSecret export SECRET_KEY_BASE=2PzB7PPnpuLsbWmWtXpGyI+kfSQSQ1zUW2Atz/+8PdZuSEJzHgzGnJWV35nTKRwx From 42df9d1af2642d25bb051cdd4a5bc80935011084 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Mon, 13 Apr 2020 23:15:49 +0100 Subject: [PATCH 029/189] add fields config (argh! see: https://github.com/dwyl/fields/issues/69 ...) --- config/config.exs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/config/config.exs b/config/config.exs index 56e2e137..520468e2 100644 --- a/config/config.exs +++ b/config/config.exs @@ -29,3 +29,17 @@ config :phoenix, :json_library, Jason # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{Mix.env()}.exs" + +config :fields, Fields.AES, + # get the ENCRYPTION_KEYS env variable + keys: + System.get_env("ENCRYPTION_KEYS") + # remove single-quotes around key list in .env + |> String.replace("'", "") + # split the CSV list of keys + |> String.split(",") + # decode the key. + |> Enum.map(fn key -> :base64.decode(key) end) + +config :fields, Fields, + secret_key_base: System.get_env("SECRET_KEY_BASE") From 1beb0e37487853eb5facd52ea503d88580d7d8b3 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Mon, 13 Apr 2020 23:17:44 +0100 Subject: [PATCH 030/189] add start of seed to insert Admin person https://github.com/dwyl/auth/issues/47 --- priv/repo/seeds.exs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index e2497a21..d3a6dfa4 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -9,3 +9,10 @@ # # We recommend using the bang functions (`insert!`, `update!` # and so on) as they will fail if something goes wrong. +alias Auth.Person +alias Auth.Repo +IO.inspect(System.get_env("ADMIN_EMAIL"), label: "ADMIN_EMAIL") + +%Person{email: System.get_env("ADMIN_EMAIL")} +|> Person.changeset(%{email: System.get_env("ADMIN_EMAIL")}) +|> Repo.insert!() From f5f255eefcced011a67c4e07827915acd047af02 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Mon, 13 Apr 2020 23:22:10 +0100 Subject: [PATCH 031/189] add token.ex to use Joken.Config for signing JWTs #12 --- lib/auth/token.ex | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 lib/auth/token.ex diff --git a/lib/auth/token.ex b/lib/auth/token.ex new file mode 100644 index 00000000..d33d1195 --- /dev/null +++ b/lib/auth/token.ex @@ -0,0 +1,13 @@ +defmodule Auth.Token do + @moduledoc """ + Token module to create and validate jwt. + see https://hexdocs.pm/joken/configuration.html#module-approach + """ + use Joken.Config + + @impl true + def token_config do + default_claims(default_exp: 31_537_000 ) # ~ 1 year in seconds + end + +end From ca90aa4b7e1105470dd049f6411dc4a05b630826 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Mon, 13 Apr 2020 23:22:41 +0100 Subject: [PATCH 032/189] add idea to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 95776370..47e2ef53 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,5 @@ auth-*.tar /deps/ *.DS_Store +*.idea +*.iml From 805069d3562e9abad3965b81d9f8bb6e3a672cdb Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 14 Apr 2020 00:37:23 +0100 Subject: [PATCH 033/189] use latest version of fields 2.4.0 removes need for config step --- mix.exs | 5 +++-- mix.lock | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/mix.exs b/mix.exs index e98b21f6..90f887ea 100644 --- a/mix.exs +++ b/mix.exs @@ -54,14 +54,15 @@ defmodule Auth.Mixfile do {:plug_cowboy, "~> 2.0"}, # Field Validation and Encryption: - {:fields, "~> 2.3.1"}, + {:fields, "~> 2.4.0"}, # Auth: {:elixir_auth_github, "~> 1.2.0"}, {:elixir_auth_google, "~> 1.2.0"}, + {:joken, "~> 2.2"}, # check test coverage - {:excoveralls, "~> 0.6", only: :test}, + {:excoveralls, "~> 0.12.3", only: :test}, ] end diff --git a/mix.lock b/mix.lock index d1f832e7..ee4ea950 100644 --- a/mix.lock +++ b/mix.lock @@ -7,13 +7,13 @@ "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"}, "db_connection": {:hex, :db_connection, "2.2.1", "caee17725495f5129cb7faebde001dc4406796f12a62b8949f4ac69315080566", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "2b02ece62d9f983fcd40954e443b7d9e6589664380e5546b2b9b523cd0fb59e1"}, "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, - "ecto": {:hex, :ecto, "3.4.0", "a7a83ab8359bf816ce729e5e65981ce25b9fc5adfc89c2ea3980f4fed0bfd7c1", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "5eed18252f5b5bbadec56a24112b531343507dbe046273133176b12190ce19cc"}, + "ecto": {:hex, :ecto, "3.4.2", "6890af71025769bd27ef62b1ed1925cfe23f7f0460bcb3041da4b705215ff23e", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3959b8a83e086202a4bd86b4b5e6e71f9f1840813de14a57d502d3fc2ef7132"}, "ecto_sql": {:hex, :ecto_sql, "3.4.1", "3c9136ba138f9b74d31286c73c61232a92bd19385f7c5607bdeb3a4587ef91f5", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b4be0bffe7b0bdf5393defcae52712f248e70cc2bc0e8ab6ddb03be66371516"}, "elixir_auth_github": {:hex, :elixir_auth_github, "1.2.0", "b7233c8de0c0c85c6fcd32f0ab6e3d32d6ccb90262d3efbf6bb064d3551369d8", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "7f11269bf0170af6b61e17653bf8fa922347a08ceac59a9fba0e27c980c28938"}, "elixir_auth_google": {:hex, :elixir_auth_google, "1.2.0", "99672af2cadcf18bad10548a75667dada2f802cf1a8c519d2283346c8b8e99b8", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "8720fd473f471a9a03d6630fc3c8b90dff5e089047e153dc1f78ed68a68740a9"}, "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, "excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"}, - "fields": {:hex, :fields, "2.3.1", "30102c844f79a9024cf4e07a1595427c8007bd301f1c6ddd689a31f0f0cbd622", [:mix], [{:argon2_elixir, "~> 2.3.0", [hex: :argon2_elixir, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:html_sanitize_ex, "~> 1.4", [hex: :html_sanitize_ex, repo: "hexpm", optional: false]}], "hexpm", "fc3067bf3226c43aeaf3e7eab2524785da28507fd064d858b61ddac4d971cec8"}, + "fields": {:hex, :fields, "2.4.0", "af1f26a56d2462888c9838de1d7a6afc6deaf624b4aead8901c388d2d2ddfbfc", [:mix], [{:argon2_elixir, "~> 2.3.0", [hex: :argon2_elixir, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:html_sanitize_ex, "~> 1.4", [hex: :html_sanitize_ex, repo: "hexpm", optional: false]}], "hexpm", "48bf472db9d5c221b635a36d01a2a832a933a9d5ad815fab5f0cb6e63a85bca8"}, "file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm", "97a3b6f8d63ef53bd0113070102db2ce05352ecf0d25390eb8d747c2bde98bca"}, "gettext": {:hex, :gettext, "0.17.4", "f13088e1ec10ce01665cf25f5ff779e7df3f2dc71b37084976cf89d1aa124d5c", [:mix], [], "hexpm", "3c75b5ea8288e2ee7ea503ff9e30dfe4d07ad3c054576a6e60040e79a801e14d"}, "hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "e0100f8ef7d1124222c11ad362c857d3df7cb5f4204054f9f0f4a728666591fc"}, @@ -21,6 +21,8 @@ "httpoison": {:hex, :httpoison, "1.6.2", "ace7c8d3a361cebccbed19c283c349b3d26991eff73a1eaaa8abae2e3c8089b6", [:mix], [{:hackney, "~> 1.15 and >= 1.15.2", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "aa2c74bd271af34239a3948779612f87df2422c2fdcfdbcec28d9c105f0773fe"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, "jason": {:hex, :jason, "1.2.0", "10043418c42d2493d0ee212d3fddd25d7ffe484380afad769a0a38795938e448", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "116747dbe057794c3a3e4e143b7c8390b29f634e16c78a7f59ba75bfa6852e7f"}, + "joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"}, + "jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, From d0263259d84d7807b2dc9e0d310aeef4541c10d7 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 14 Apr 2020 00:37:49 +0100 Subject: [PATCH 034/189] remove fields config as no longer required! https://github.com/dwyl/fields/issues/69 --- config/config.exs | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/config/config.exs b/config/config.exs index 520468e2..56e2e137 100644 --- a/config/config.exs +++ b/config/config.exs @@ -29,17 +29,3 @@ config :phoenix, :json_library, Jason # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{Mix.env()}.exs" - -config :fields, Fields.AES, - # get the ENCRYPTION_KEYS env variable - keys: - System.get_env("ENCRYPTION_KEYS") - # remove single-quotes around key list in .env - |> String.replace("'", "") - # split the CSV list of keys - |> String.split(",") - # decode the key. - |> Enum.map(fn key -> :base64.decode(key) end) - -config :fields, Fields, - secret_key_base: System.get_env("SECRET_KEY_BASE") From 9af1f777b35f54df88f8e1244783c9d45572cbd9 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 14 Apr 2020 14:55:51 +0100 Subject: [PATCH 035/189] update README.md instructions --- README.md | 207 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 125 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index 1a1abd48..8066ccc4 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,12 @@ # `auth` A ***complete authentication solution*** for **Phoenix** Apps/APIs -you can setup in ***5 minutes***. +you can setup in ***7 minutes***. [![Build Status](https://img.shields.io/travis/dwyl/fields/master.svg?style=flat-square)](https://travis-ci.org/dwyl/fields) [![codecov.io](https://img.shields.io/codecov/c/github/dwyl/fields/master.svg?style=flat-square)](http://codecov.io/github/dwyl/fields?branch=master) [![Hex.pm](https://img.shields.io/hexpm/v/fields?color=brightgreen&style=flat-square)](https://hex.pm/packages/fields) +[![Libraries.io dependency status](https://img.shields.io/librariesio/release/hex/auth?logoColor=brightgreen&style=flat-square)](https://github.com/dwyl/auth/blob/master/mix.exs) [![docs](https://img.shields.io/badge/docs-maintained-brightgreen?style=flat-square)](https://hexdocs.pm/fields/api-reference.html) [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat-square)](https://github.com/dwyl/fields/issues) + +# How? As the description suggests, this module is built for apps built with the [**Phoenix**](https://github.com/dwyl/learn-phoenix-framework) web framework. @@ -86,107 +98,134 @@ If you or *anyone* on your team are new to Phoenix, we have an **introductory tutorial**: [github.com/dwyl/**learn-phoenix-framework**](https://github.com/dwyl/learn-phoenix-framework) -### _5 Minute_ Setup -To start your Phoenix app: +## 7 Minute 7 Step Setup -+ Install dependencies with `mix deps.get` -+ Create and migrate your database with `mix ecto.create && mix ecto.migrate`
-(_ensure that PostgreSQL is running on your localhost before you - run these commands_) -+ Install Node.js dependencies with `npm install` - -+ Start Phoenix endpoint with `mix phoenix.server` - -Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. - -Ready to run in production? Please [check our deployment guides](http://www.phoenixframework.org/docs/deployment). +### 1. Clone the project: +```sh +git clone git@github.com:dwyl/auth.git && cd auth +``` +### 2. Install dependencies: +```sh +mix deps.get && npm install --prefix assets +``` -### Environment Variables? +### 3. Environment Variables -This plugin checks for the presence of +The Auth App checks for the presence of _specific **Environment Variables**_ to enable each authentication provider. +> If you are totally new to Environment Variables, +see: [github.com/dwyl/**learn-environment-variables**](https://github.com/dwyl/learn-environment-variables) + An authentication provider (_endpoint_) will only work if the Environment Variable(s) for that service are present. -> If you are new to Environment Variables, -see: https://github.com/dwyl/learn-environment-variables - -### Email +If you want to enable a specific 3rd Party Authentication service, +simply ensure that the relevant Environment Variables are defined. -This repo has two send email examples. One takes a string which becomes the body -of the email, the other takes a html template. The two functions are defined in -`lib/email.ex`. They use SES and Bamboo. +#### Google Auth -For these functions to work you must have defined the following env variables: +To enable Google Auth +you will need to have two Environment Variables set: +```sh +GOOGLE_CLIENT_ID=YourAppsClientId.apps.googleusercontent.com +GOOGLE_CLIENT_SECRET=SuperSecret ``` -SMTP_USERNAME -SMTP_PASSWORD -SES_SERVER -SES_PORT +To *get* these Environment Variables, +You will need to create an App on https://console.developers.google.com +and get your `CLIENT_ID` & `CLIENT_SECRET`. + +Full instructions to create your Google Auth App: +[create-google-app-guide.md](https://github.com/dwyl/elixir-auth-google/blob/master/create-google-app-guide.md) + +#### GitHub Auth + +Similarly, for GitHub Auth, +you will need to have these environment variables: +```sh +export GITHUB_CLIENT_ID=CreateGitHubApp +export GITHUB_CLIENT_SECRET=SuperSecret ``` -To understand more about how to set them up and how they work see the full -tutorial here: https://github.com/dwyl/learn-phoenix-framework/blob/master/sending-emails.md +Full instructions to create your GitHub App: +[create-github-app-guide.md](https://github.com/dwyl/elixir-auth-github/blob/master/create-github-app-guide.md) + -To test out the string email go to the endpoint: `/email` -To test out the html template email go to the endpoint: `/html-email` +For the _full_ list of environment variables +the `Auth` App expects, see: +[`.env_sample`](https://github.com/dwyl/auth/blob/master/.env_sample) -Using html gives you the ability to add an image. Inline styling gives you the -ability to add colour, centering and padding like in the template in -`html_email/email.html.eex`: -![html-email](https://user-images.githubusercontent.com/16775804/54907608-d896a100-4edd-11e9-803e-d19af5e6d42f.png) +For completing the setup of the `Auth` App, +you will need to have the `ADMIN_EMAIL` environment variable defined. +And for sending emails you will need the +`SECRET_KEY_BASE` and `EMAIL_APP_URL` defined. -### Google Auth -There were -[***900 Million***](http://techcrunch.com/2015/05/28/gmail-now-has-900m-active-users-75-on-mobile/) -people using GMail (_in 2015, the last available public statistics_) -and -[***1.4 billion active Android devices***](http://www.theverge.com/2015/9/29/9409071/google-android-stats-users-downloads-sales) -(_also 2015 stat_) which are _certainly_ higher now, -_so_ Google is ***by far*** the most popular "account" people have. -Offering people the option of logging into -your App(s) using their Google Account makes a lot of sense. +### 4. Create and migrate your database: + +> Ensure that PostgreSQL is running + on your localhost before you + run this command. -To enable Google Auth you will need to have two Environment Variables set: ```sh -GOOGLE_CLIENT_ID=YourAppsClientId.apps.googleusercontent.com -GOOGLE_CLIENT_SECRET=SuperSecret +mix ecto.setup ``` -To *get* these Environment Variables, -You will need to create an App on https://console.developers.google.com -and get your `CLIENT_ID` & `CLIENT_SECRET`. -We export these two variables prefixed with `GOOGLE_` -to distinguish them from other services. + +### 5. Start Phoenix App + +```sh +mix phoenix.server +``` + +Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. +
### Dependencies This project builds on the _fantastic_ work done _many_ people in the Elixir/Phoenix community. + + + Phoenix default session handling (_so your app handles sessions for authenticated users the same way the example apps in all the Phoenix docs_) -+ ['ueberauth'](https://github.com/ueberauth/ueberauth) -by @hassox & @scrogson & friends -which is "_inspired by_" -[`omniauth`](https://github.com/omniauth/omniauth) (_from Ruby land_). ++ GitHub OAuth2 Authentication: +https://github.com/dwyl/elixir-auth-github ++ Google OAuth Authentication: +https://github.com/dwyl/elixir-auth-google + + +
+ + +### Email -The purpose of _this_ project is to have a more "_turnkey_" solution -rather than having the ingredients for the meal, we want the meal to be _ready_! +When people register with their `email` address +or authenticate with a 3rd party Authentication provider (e.g: Google), +an email is sent to the `email` address welcoming them. +The `Auth` App uses an external email service +for sending emails: +https://github.com/dwyl/email -# tl;dr +![app-services-diagram](https://user-images.githubusercontent.com/194400/77526292-41628180-6e82-11ea-8044-dacbc57ba895.png) + +[Edit this diagram](https://docs.google.com/presentation/d/1PUKzbRQOEgHaOmaEheU7T3AHQhRT8mhGuqVKotEJkM0/edit#slide=id.g71eb641cbd_0_0) + +The Email app provides a simplified interface for sending emails +that ensures our main App can focus on it's core functionality. + +

## Frequently Asked/Answered Questions @@ -200,14 +239,17 @@ We care about privacy so we _have_ to know _exactly_ where the login details (_Email Address, Name, etc._) of people _using_ our apps is _stored_. -If you prefer to use (_and pay for_) one of the existing services +If you prefer to use (_and pay for_) +one of the existing +["black box"](https://en.wikipedia.org/wiki/Black_box) +services and "not have to think about auth" then go for it! _This_ repo/project is for people who _do_ want to think about auth, want to _know_ where sensitive data is stored and want to be able to extend the code if they choose to. -### Phoenix Has a Session System Already, Does this Use It? +### Phoenix Has a Session System, Does this Use It? Phoenix has a built-in mechanism for sessions: http://www.phoenixframework.org/docs/sessions @@ -215,10 +257,10 @@ http://www.phoenixframework.org/docs/sessions This project _uses_ and _extends_ it to support several 3rd party auth services. + From 7e151b68355e41a3bb1c617a72f037315714bfa3 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 14 Apr 2020 16:31:31 +0100 Subject: [PATCH 036/189] add package desription to publish Hex.pm --- mix.exs | 18 +++++++++++++++++- mix.lock | 5 +++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 90f887ea..f4aa2fe5 100644 --- a/mix.exs +++ b/mix.exs @@ -18,7 +18,9 @@ defmodule Auth.Mixfile do build_embedded: Mix.env() == :prod, start_permanent: Mix.env() == :prod, aliases: aliases(), - deps: deps() + deps: deps(), + package: package(), + description: "Turnkey Auth Auth Application" ] end @@ -63,6 +65,10 @@ defmodule Auth.Mixfile do # check test coverage {:excoveralls, "~> 0.12.3", only: :test}, + + # For publishing Hex.docs: + {:ex_doc, "~> 0.21.3", only: :dev} + ] end @@ -79,4 +85,14 @@ defmodule Auth.Mixfile do test: ["ecto.create --quiet", "ecto.migrate", "test"] ] end + + defp package() do + [ + files: ~w(lib LICENSE mix.exs README.md), + name: "auth", + licenses: ["GNU GPL v2.0"], + maintainers: ["dwyl"], + links: %{"GitHub" => "https://github.com/dwyl/auth"} + ] + end end diff --git a/mix.lock b/mix.lock index ee4ea950..9f92da6a 100644 --- a/mix.lock +++ b/mix.lock @@ -7,11 +7,13 @@ "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"}, "db_connection": {:hex, :db_connection, "2.2.1", "caee17725495f5129cb7faebde001dc4406796f12a62b8949f4ac69315080566", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "2b02ece62d9f983fcd40954e443b7d9e6589664380e5546b2b9b523cd0fb59e1"}, "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, + "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, "ecto": {:hex, :ecto, "3.4.2", "6890af71025769bd27ef62b1ed1925cfe23f7f0460bcb3041da4b705215ff23e", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3959b8a83e086202a4bd86b4b5e6e71f9f1840813de14a57d502d3fc2ef7132"}, "ecto_sql": {:hex, :ecto_sql, "3.4.1", "3c9136ba138f9b74d31286c73c61232a92bd19385f7c5607bdeb3a4587ef91f5", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b4be0bffe7b0bdf5393defcae52712f248e70cc2bc0e8ab6ddb03be66371516"}, "elixir_auth_github": {:hex, :elixir_auth_github, "1.2.0", "b7233c8de0c0c85c6fcd32f0ab6e3d32d6ccb90262d3efbf6bb064d3551369d8", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "7f11269bf0170af6b61e17653bf8fa922347a08ceac59a9fba0e27c980c28938"}, "elixir_auth_google": {:hex, :elixir_auth_google, "1.2.0", "99672af2cadcf18bad10548a75667dada2f802cf1a8c519d2283346c8b8e99b8", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "8720fd473f471a9a03d6630fc3c8b90dff5e089047e153dc1f78ed68a68740a9"}, "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, + "ex_doc": {:hex, :ex_doc, "0.21.3", "857ec876b35a587c5d9148a2512e952e24c24345552259464b98bfbb883c7b42", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0db1ee8d1547ab4877c5b5dffc6604ef9454e189928d5ba8967d4a58a801f161"}, "excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"}, "fields": {:hex, :fields, "2.4.0", "af1f26a56d2462888c9838de1d7a6afc6deaf624b4aead8901c388d2d2ddfbfc", [:mix], [{:argon2_elixir, "~> 2.3.0", [hex: :argon2_elixir, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:html_sanitize_ex, "~> 1.4", [hex: :html_sanitize_ex, repo: "hexpm", optional: false]}], "hexpm", "48bf472db9d5c221b635a36d01a2a832a933a9d5ad815fab5f0cb6e63a85bca8"}, "file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm", "97a3b6f8d63ef53bd0113070102db2ce05352ecf0d25390eb8d747c2bde98bca"}, @@ -23,10 +25,13 @@ "jason": {:hex, :jason, "1.2.0", "10043418c42d2493d0ee212d3fddd25d7ffe484380afad769a0a38795938e448", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "116747dbe057794c3a3e4e143b7c8390b29f634e16c78a7f59ba75bfa6852e7f"}, "joken": {:hex, :joken, "2.2.0", "2daa1b12be05184aff7b5ace1d43ca1f81345962285fff3f88db74927c954d3a", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "b4f92e30388206f869dd25d1af628a1d99d7586e5cf0672f64d4df84c4d2f5e9"}, "jose": {:hex, :jose, "1.10.1", "16d8e460dae7203c6d1efa3f277e25b5af8b659febfc2f2eb4bacf87f128b80a", [:mix, :rebar3], [], "hexpm", "3c7ddc8a9394b92891db7c2771da94bf819834a1a4c92e30857b7d582e2f8257"}, + "makeup": {:hex, :makeup, "1.0.1", "82f332e461dc6c79dbd82fbe2a9c10d48ed07146f0a478286e590c83c52010b5", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "49736fe5b66a08d8575bf5321d716bac5da20c8e6b97714fec2bcd6febcfa1f8"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "d4b316c7222a85bbaa2fd7c6e90e37e953257ad196dc229505137c5e505e9eff"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm", "6cbe761d6a0ca5a31a0931bf4c63204bceb64538e664a8ecf784a9a6f3b875f1"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mochiweb": {:hex, :mochiweb, "2.20.1", "e4dbd0ed716f076366ecf62ada5755a844e1d95c781e8c77df1d4114be868cdf", [:rebar3], [], "hexpm", "d1aeee7870470d2fa9eae0b3d5ab6c33801aa2d82b10e9dade885c5c921b36aa"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"}, "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"}, "phoenix": {:hex, :phoenix, "1.4.16", "2cbbe0c81e6601567c44cc380c33aa42a1372ac1426e3de3d93ac448a7ec4308", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "856cc1a032fa53822737413cf51aa60e750525d7ece7d1c0576d90d7c0f05c24"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.1.0", "a044d0756d0464c5a541b4a0bf4bcaf89bffcaf92468862408290682c73ae50d", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "c5e666a341ff104d0399d8f0e4ff094559b2fde13a5985d4cb5023b2c2ac558b"}, From 04ce28b02caffaed233c09e9b40445e4c9d69dd2 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 14 Apr 2020 21:26:44 +0100 Subject: [PATCH 037/189] successfully create person with associated status="verified" #47 --- priv/repo/seeds.exs | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index d3a6dfa4..39dcfbbd 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -1,18 +1,25 @@ # Script for populating the database. You can run it as: # -# mix run priv/repo/seeds.exs +# mix run priv/repo/seeds.exs # -# Inside the script, you can read and write to any of your -# repositories directly: +# The seeds.exs script will also run +# when you execute the following command +# to setup the database: # -# Auth.Repo.insert!(%Auth.SomeSchema{}) -# -# We recommend using the bang functions (`insert!`, `update!` -# and so on) as they will fail if something goes wrong. -alias Auth.Person -alias Auth.Repo -IO.inspect(System.get_env("ADMIN_EMAIL"), label: "ADMIN_EMAIL") +# mix ecto.setup +defmodule Auth.Seeds do + alias Auth.{Person, Repo, Status} + import Ecto.Changeset # put_assoc + IO.inspect(System.get_env("ADMIN_EMAIL"), label: "ADMIN_EMAIL") + + def create_admin do + person = %Person{email: System.get_env("ADMIN_EMAIL")} + |> Person.changeset(%{email: System.get_env("ADMIN_EMAIL")}) + |> put_assoc(:statuses, [%Status{text: "verified"}]) + |> Repo.insert!() + + IO.inspect(person, label: "first person") + end +end -%Person{email: System.get_env("ADMIN_EMAIL")} -|> Person.changeset(%{email: System.get_env("ADMIN_EMAIL")}) -|> Repo.insert!() +Auth.Seeds.create_admin() From b62faa1d5d8cacdcfab7731141d9ac790d1d8fe6 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 14 Apr 2020 21:31:11 +0100 Subject: [PATCH 038/189] run seeds script in tests see: https://github.com/dwyl/auth/issues/47#issuecomment-613665596 --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index f4aa2fe5..a222d558 100644 --- a/mix.exs +++ b/mix.exs @@ -82,7 +82,7 @@ defmodule Auth.Mixfile do [ "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], "ecto.reset": ["ecto.drop", "ecto.setup"], - test: ["ecto.create --quiet", "ecto.migrate", "test"] + test: ["ecto.create --quiet", "ecto.migrate", "run priv/repo/seeds.exs", "test"] ] end From ae385aab26b210c785d0726f387da7b816e1a2a3 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 14 Apr 2020 23:39:05 +0100 Subject: [PATCH 039/189] ensure we dont duplicate admin person in seeds.exs https://github.com/dwyl/auth/issues/47#issuecomment-613125622 --- priv/repo/seeds.exs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 39dcfbbd..3b714388 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -10,15 +10,25 @@ defmodule Auth.Seeds do alias Auth.{Person, Repo, Status} import Ecto.Changeset # put_assoc - IO.inspect(System.get_env("ADMIN_EMAIL"), label: "ADMIN_EMAIL") + # IO.inspect(System.get_env("ADMIN_EMAIL"), label: "ADMIN_EMAIL") def create_admin do - person = %Person{email: System.get_env("ADMIN_EMAIL")} - |> Person.changeset(%{email: System.get_env("ADMIN_EMAIL")}) - |> put_assoc(:statuses, [%Status{text: "verified"}]) - |> Repo.insert!() + email = System.get_env("ADMIN_EMAIL") - IO.inspect(person, label: "first person") + person = case Person.get_person_by_email(email) do + nil -> + %Person{email: email} + |> Person.changeset(%{email: email}) + |> put_assoc(:statuses, [%Status{text: "verified"}]) + |> Repo.insert!() + # |> IO.inspect( label: "inserted") + + person -> + person + end + + IO.inspect(person.id, label: "seeds.exs person.id") + IO.puts("- - - - - - - - - - - - - - - - - - - - - - ") end end From d6d0e8c463314bf652a35a32f672969c697ace6c Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 14 Apr 2020 23:52:05 +0100 Subject: [PATCH 040/189] silence mix ecto.setup --- mix.exs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index a222d558..4ad5458d 100644 --- a/mix.exs +++ b/mix.exs @@ -80,9 +80,10 @@ defmodule Auth.Mixfile do # See the documentation for `Mix` for more info on aliases. defp aliases do [ - "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], - "ecto.reset": ["ecto.drop", "ecto.setup"], - test: ["ecto.create --quiet", "ecto.migrate", "run priv/repo/seeds.exs", "test"] + "ecto.setup": ["ecto.create --quiet", "ecto.migrate --quiet", "seeds"], + "ecto.reset": ["ecto.drop --quiet", "ecto.setup"], + seeds: ["run priv/repo/seeds.exs"], + test: ["ecto.reset", "test"] ] end From 9cd960a09a8f2d1765577f555bcfe8349e96c654 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 14 Apr 2020 23:52:20 +0100 Subject: [PATCH 041/189] create status_test.exs (Failing) --- test/auth/status_test.exs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 test/auth/status_test.exs diff --git a/test/auth/status_test.exs b/test/auth/status_test.exs new file mode 100644 index 00000000..64d53867 --- /dev/null +++ b/test/auth/status_test.exs @@ -0,0 +1,19 @@ +defmodule Auth.StatusTest do + use Auth.DataCase + alias Auth.{Status} + + test "upsert_status/1 inserts or updates a status record" do + status = Status.upsert_status("verified") + assert status.id == 1 + + new_status = Status.upsert_status("amaze") + IO.inspect(new_status, label: "new_status") + # assert new_status.id == 2 + end + + # test "create_status/1" do + # # tag = tag_fixture() + # assert Ctx.list_tags() == [tag] + # end + +end From f5f444c9ec9f747c729843f53e26bef58102ae8e Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 00:04:56 +0100 Subject: [PATCH 042/189] upsert_status/1 test passes! --- lib/auth/status.ex | 26 ++++++++++++++++++++++++-- test/auth/status_test.exs | 10 ++-------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/lib/auth/status.ex b/lib/auth/status.ex index da1b8061..cc8f00f9 100644 --- a/lib/auth/status.ex +++ b/lib/auth/status.ex @@ -1,6 +1,8 @@ defmodule Auth.Status do use Ecto.Schema import Ecto.Changeset + alias Auth.Repo + alias __MODULE__ # https://stackoverflow.com/a/47501059/1148249 schema "status" do field :text, :string @@ -16,7 +18,27 @@ defmodule Auth.Status do |> validate_required([:text]) end - def get_status_verified() do - Auth.Repo.get_by(Status, text: "verified") + + def create_status(text, person) do + # IO.inspect(text, label: "create_status/2 > text") + # IO.inspect(person, label: "create_status/2 > person") + # IO.inspect(__MODULE__, label: "__MODULE__") + %Status{} + |> changeset(%{text: text}) + |> put_assoc(:person, person) + |> Repo.insert!() + end + + def upsert_status(text) do + case Auth.Repo.get_by(__MODULE__, text: text) do + nil -> # create status + email = System.get_env("ADMIN_EMAIL") + person = Auth.Person.get_person_by_email(email) + create_status(text, person) + + status -> + # IO.inspect(status, label: "status") + status + end end end diff --git a/test/auth/status_test.exs b/test/auth/status_test.exs index 64d53867..0712c864 100644 --- a/test/auth/status_test.exs +++ b/test/auth/status_test.exs @@ -7,13 +7,7 @@ defmodule Auth.StatusTest do assert status.id == 1 new_status = Status.upsert_status("amaze") - IO.inspect(new_status, label: "new_status") - # assert new_status.id == 2 + # IO.inspect(new_status, label: "new_status") + assert new_status.id == 2 end - - # test "create_status/1" do - # # tag = tag_fixture() - # assert Ctx.list_tags() == [tag] - # end - end From cf722208227246c9a76ab21ba7476ed3e358a814 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 00:27:45 +0100 Subject: [PATCH 043/189] finally dug myself out of the hole of failing tests! --- README.md | 13 +- auth.iml | 151 ------------------ lib/auth/person.ex | 85 +++++++--- .../controllers/github_auth_controller.ex | 24 ++- .../controllers/google_auth_controller.ex | 8 +- lib/auth_web/controllers/page_controller.ex | 6 +- .../templates/page/welcome_google.html.eex | 6 +- mix.exs | 2 +- .../github_auth_controller_test.exs | 2 +- .../google_auth_controller_test.exs | 1 + 10 files changed, 111 insertions(+), 187 deletions(-) delete mode 100644 auth.iml diff --git a/README.md b/README.md index 8066ccc4..ec664d27 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # `auth` A ***complete authentication solution*** for **Phoenix** Apps/APIs -you can setup in ***7 minutes***. +you can setup in ***5 minutes***. [![Build Status](https://img.shields.io/travis/dwyl/fields/master.svg?style=flat-square)](https://travis-ci.org/dwyl/fields) [![codecov.io](https://img.shields.io/codecov/c/github/dwyl/fields/master.svg?style=flat-square)](http://codecov.io/github/dwyl/fields?branch=master) @@ -101,7 +101,7 @@ have an **introductory tutorial**: -## 7 Minute 7 Step Setup +## 5 Minute 5 Step Setup ### 1. Clone the project: @@ -131,6 +131,7 @@ if the Environment Variable(s) for that service are present. If you want to enable a specific 3rd Party Authentication service, simply ensure that the relevant Environment Variables are defined. + #### Google Auth To enable Google Auth @@ -146,6 +147,7 @@ and get your `CLIENT_ID` & `CLIENT_SECRET`. Full instructions to create your Google Auth App: [create-google-app-guide.md](https://github.com/dwyl/elixir-auth-google/blob/master/create-google-app-guide.md) + #### GitHub Auth Similarly, for GitHub Auth, @@ -158,13 +160,16 @@ export GITHUB_CLIENT_SECRET=SuperSecret Full instructions to create your GitHub App: [create-github-app-guide.md](https://github.com/dwyl/elixir-auth-github/blob/master/create-github-app-guide.md) +#### Full List of Environment Variables For the _full_ list of environment variables the `Auth` App expects, see: [`.env_sample`](https://github.com/dwyl/auth/blob/master/.env_sample) + For completing the setup of the `Auth` App, -you will need to have the `ADMIN_EMAIL` environment variable defined. +you will need to have the `ADMIN_EMAIL` +environment variable defined.
And for sending emails you will need the `SECRET_KEY_BASE` and `EMAIL_APP_URL` defined. @@ -186,6 +191,8 @@ mix ecto.setup mix phoenix.server ``` +> It may take a couple of minutes to compile the app the first time. ⏳ + Now you can visit [`localhost:4000`](http://localhost:4000) from your browser. diff --git a/auth.iml b/auth.iml deleted file mode 100644 index c8aefe45..00000000 --- a/auth.iml +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 5247478c..849c2fef 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -1,6 +1,8 @@ defmodule Auth.Person do use Ecto.Schema import Ecto.Changeset + alias Auth.Repo + alias __MODULE__ # https://stackoverflow.com/a/47501059/1148249 schema "people" do field :email, Fields.EmailEncrypted @@ -17,7 +19,8 @@ defmodule Auth.Person do field :tag, :id field :key_id, :integer - has_many :sessions, Auth.Session, on_delete: :delete_all + has_many :statuses, Auth.Status + # has_many :sessions, Auth.Session, on_delete: :delete_all timestamps() end @@ -25,6 +28,8 @@ defmodule Auth.Person do Default attributes validation for Person """ def changeset(person, attrs) do + # IO.inspect(person, label: "person") + # IO.inspect(attrs, label: "attrs") person |> cast(attrs, [ :username, @@ -44,9 +49,28 @@ defmodule Auth.Person do # :password_hash, # :key_id ]) + # |> IO.inspect(label: "changeset (before email hash)") |> put_email_hash() end + def create_google_person(profile) do + # IO.inspect(profile, label: "profile") + # IO.puts(" - - - - - - - - - - - - - - - - - ") + person = transform_profile_data_to_person(profile) + # IO.inspect(person, label: "person:60") + person = %Person{} + |> google_changeset(person) + + case Person.get_person_by_email(profile.email) do + nil -> + Repo.insert!(person) + + person -> + person + end + + end + @doc """ Changeset used for Google OAuth authentication Add email hash and set status verified @@ -59,27 +83,6 @@ defmodule Auth.Person do |> put_email_status_verified() end - defp put_email_hash(changeset) do - case changeset do - %{valid?: true, changes: %{email: email}} -> - put_change(changeset, :email_hash, email) - - _ -> - changeset - end - end - - defp put_email_status_verified(changeset) do - status_verified = Auth.Status.get_status_verified() - - case changeset do - %{valid?: true} -> - put_change(changeset, :status, status_verified.id) - - _ -> - changeset - end - end @doc """ `transform_profile_data_to_person/1` transforms the profile data @@ -113,6 +116,7 @@ defmodule Auth.Person do } """ def transform_profile_data_to_person(profile) do + # IO.inspect(profile, label: "profile transform_profile_data_to_person/1") profile |> Map.put(:familyName, profile.family_name) |> Map.put(:givenName, profile.given_name) @@ -134,6 +138,31 @@ defmodule Auth.Person do |> put_pass_hash() end + defp put_email_hash(changeset) do + # IO.inspect(changeset, label: "changeset put_email_hash/1") + # IO.inspect(changeset.data, label: "changeset.data") + case changeset do + %{valid?: true, data: %{email: email}} -> + put_change(changeset, :email_hash, email) + # |> IO.inspect(label: "changeset with :email_hash") + + _ -> + changeset + end + end + + defp put_email_status_verified(changeset) do + status_verified = Auth.Status.upsert_status("verified") + + case changeset do + %{valid?: true} -> + put_change(changeset, :status, status_verified.id) + + _ -> + changeset + end + end + defp put_pass_hash(changeset) do case changeset do %Ecto.Changeset{valid?: true, changes: %{password: pass}} -> @@ -143,4 +172,16 @@ defmodule Auth.Person do changeset end end + + @doc """ + `get_person_by_email/1` returns the person based on email address. + """ + def get_person_by_email(email) do + # IO.inspect(email, label: "get_person_by_email/1 > email") + # {:ok, email_hash} = Fields.EmailHash.dump(email) + # IO.inspect(email_hash, label: "get_person_by_email/1 > email_hash") + __MODULE__ + |> Repo.get_by(email_hash: email) + # |> IO.inspect(label: "get_person_by_email/1") + end end diff --git a/lib/auth_web/controllers/github_auth_controller.ex b/lib/auth_web/controllers/github_auth_controller.ex index 3694064b..0adc250c 100644 --- a/lib/auth_web/controllers/github_auth_controller.ex +++ b/lib/auth_web/controllers/github_auth_controller.ex @@ -4,10 +4,26 @@ defmodule AuthWeb.GithubAuthController do @doc """ `index/2` handles the callback from GitHub Auth API redirect. """ - def index(conn, %{"code" => code}) do + def index(conn, %{"code" => code, "state" => state}) do {:ok, profile} = ElixirAuthGithub.github_auth(code) - conn - |> put_view(AuthWeb.PageView) - |> render(:welcome_github, profile: profile) + + case not is_nil(state) and state =~ "//" do + true -> # state = redirect + url = state <> "?jwt=this.is.amaze" + conn + # |> put_req_header("authorization", "MY.JWT.HERE") + |> redirect(external: url) + # |> halt() + false -> # no state + conn + |> put_view(AuthWeb.PageView) + |> render(:welcome_github, profile: profile) + end + end + + def redirect_to_referer_with_jwt(conn, state, person) do + IO.inspect(conn, label: "conn") + IO.inspect(state, label: "state") + IO.inspect(person, label: "person") end end diff --git a/lib/auth_web/controllers/google_auth_controller.ex b/lib/auth_web/controllers/google_auth_controller.ex index 707d373d..7590fcc5 100644 --- a/lib/auth_web/controllers/google_auth_controller.ex +++ b/lib/auth_web/controllers/google_auth_controller.ex @@ -5,10 +5,16 @@ defmodule AuthWeb.GoogleAuthController do `index/2` handles the callback from Google Auth API redirect. """ def index(conn, %{"code" => code}) do + # IO.inspect(code, label: "code") + # IO.inspect(state, label: "state") {:ok, token} = ElixirAuthGoogle.get_token(code, conn) {:ok, profile} = ElixirAuthGoogle.get_user_profile(token.access_token) + + # save profile to people: + person = Auth.Person.create_google_person(profile) + # IO.inspect(person, label: "person") conn |> put_view(AuthWeb.PageView) - |> render(:welcome_google, profile: profile) + |> render(:welcome_google, person: person) end end diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index 18af3b5f..db1841cf 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -12,7 +12,11 @@ defmodule AuthWeb.PageController do ]) end - def admin(conn, _params) do + # https://github.com/dwyl/auth/issues/46 + def admin(conn, params) do + IO.inspect(conn.req_headers, label: "conn.req_headers") + IO.inspect(params, label: "params") + conn |> put_view(AuthWeb.PageView) |> render(:admin) diff --git a/lib/auth_web/templates/page/welcome_google.html.eex b/lib/auth_web/templates/page/welcome_google.html.eex index 72c3b063..7c7d1909 100644 --- a/lib/auth_web/templates/page/welcome_google.html.eex +++ b/lib/auth_web/templates/page/welcome_google.html.eex @@ -1,9 +1,9 @@
-

Welcome <%= @profile.given_name %>! - +

Welcome <%= @person.givenName %>! +

You are signed in with your Google Account
- <%= @profile.email %> + <%= @person.email %>

diff --git a/mix.exs b/mix.exs index 4ad5458d..7b278a19 100644 --- a/mix.exs +++ b/mix.exs @@ -81,7 +81,7 @@ defmodule Auth.Mixfile do defp aliases do [ "ecto.setup": ["ecto.create --quiet", "ecto.migrate --quiet", "seeds"], - "ecto.reset": ["ecto.drop --quiet", "ecto.setup"], + "ecto.reset": ["ecto.drop", "ecto.setup"], seeds: ["run priv/repo/seeds.exs"], test: ["ecto.reset", "test"] ] diff --git a/test/auth_web/controllers/github_auth_controller_test.exs b/test/auth_web/controllers/github_auth_controller_test.exs index 79648941..99ba6b2e 100644 --- a/test/auth_web/controllers/github_auth_controller_test.exs +++ b/test/auth_web/controllers/github_auth_controller_test.exs @@ -5,6 +5,6 @@ defmodule AuthWeb.GithubAuthControllerTest do conn = get(conn, Routes.github_auth_path(conn, :index, %{code: "123", state: "http://localhost/"})) - assert html_response(conn, 200) =~ "test@gmail.com" + assert html_response(conn, 302) =~ "http://localhost" end end diff --git a/test/auth_web/controllers/google_auth_controller_test.exs b/test/auth_web/controllers/google_auth_controller_test.exs index d514f478..39443036 100644 --- a/test/auth_web/controllers/google_auth_controller_test.exs +++ b/test/auth_web/controllers/google_auth_controller_test.exs @@ -5,6 +5,7 @@ defmodule AuthWeb.GoogleAuthControllerTest do conn = get(conn, Routes.google_auth_path(conn, :index, %{code: "234", state: "http://localhost/"})) + assert html_response(conn, 200) =~ "nelson@gmail.com" end end From 89c1b7a114e1f08d67a9e112d563e1bee7ad53c7 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 00:33:51 +0100 Subject: [PATCH 044/189] run mix ecto.setup (with seeds) in .travis.yml #47 --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a09673f2..4170a96a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ services: env: - MIX_ENV=test before_script: - - mix do ecto.create, ecto.migrate + - mix ecto.setup script: - mix do deps.get, coveralls.json # See: github.com/dwyl/repo-badges#documentation @@ -17,3 +17,7 @@ after_script: - MIX_ENV=docs mix inch.report after_success: - bash <(curl -s https://codecov.io/bash) +cache: + directories: + - _build + - deps From b12ad94ae1ca5f74bce8df78b2c413d3750bce7d Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 20:50:29 +0100 Subject: [PATCH 045/189] create auth_controller.ex to refactor (tests passing) --- coveralls.json | 1 + lib/auth/person.ex | 76 +++++++++---------- lib/auth_web/controllers/auth_controller.ex | 43 +++++++++++ .../controllers/github_auth_controller.ex | 4 +- lib/auth_web/router.ex | 2 +- .../controllers/auth_controller_test.exs | 19 +++++ .../github_auth_controller_test.exs | 10 --- .../google_auth_controller_test.exs | 6 -- 8 files changed, 104 insertions(+), 57 deletions(-) create mode 100644 lib/auth_web/controllers/auth_controller.ex create mode 100644 test/auth_web/controllers/auth_controller_test.exs delete mode 100644 test/auth_web/controllers/github_auth_controller_test.exs diff --git a/coveralls.json b/coveralls.json index aa06e6c8..6aadc599 100644 --- a/coveralls.json +++ b/coveralls.json @@ -4,6 +4,7 @@ }, "skip_files": [ "test/", + "lib/auth/application.ex", "lib/auth_web.ex", "lib/auth_web/views/error_helpers.ex" ] diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 849c2fef..f0622a43 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -28,8 +28,6 @@ defmodule Auth.Person do Default attributes validation for Person """ def changeset(person, attrs) do - # IO.inspect(person, label: "person") - # IO.inspect(attrs, label: "attrs") person |> cast(attrs, [ :username, @@ -39,51 +37,61 @@ defmodule Auth.Person do :password_hash, :key_id, :locale, - :picture + :picture, + :username ]) - |> validate_required([ - # :username, - :email, - # :givenName, - # :familyName, - # :password_hash, - # :key_id - ]) - # |> IO.inspect(label: "changeset (before email hash)") + |> validate_required([:email]) |> put_email_hash() end - def create_google_person(profile) do - # IO.inspect(profile, label: "profile") - # IO.puts(" - - - - - - - - - - - - - - - - - ") - person = transform_profile_data_to_person(profile) - # IO.inspect(person, label: "person:60") - person = %Person{} - |> google_changeset(person) - - case Person.get_person_by_email(profile.email) do + def create_person(person) do + case get_person_by_email(person.changes.email) do nil -> Repo.insert!(person) person -> person end + end + def create_google_person(profile) do + person = transform_google_profile_data_to_person(profile) + %Person{} + |> changeset(person) + |> put_email_status_verified() + |> create_person() end @doc """ - Changeset used for Google OAuth authentication - Add email hash and set status verified + `transform_github_profile_data_to_person/1` transforms the profile data + received from invoking `ElixirAuthGithub.github_auth/1` + into a `person` record that can be inserted into the people table. + + ## Example + + iex> transform_profile_data_to_person(%{ + avatar_url: "https://avatars3.githubusercontent.com/u/194400?v=4", + email: "alex@gmail.com", + followers: 2846, + login: "alex", + name: "Alex McAwesome", + type: "User", + url: "https://api.github.com/users/alex" + }) + %{ + "email" => "nelson@gmail.com", + "picture" => "https://avatars3.githubusercontent.com/u/194400?v=4", + "status" => 1, + "givenName" => "Alex McAwesome" + } """ - def google_changeset(profile, attrs) do + def transform_github_profile_data_to_person(profile) do profile - |> cast(attrs, [:email, :givenName, :familyName, :picture, :locale]) - |> validate_required([:email]) - |> put_email_hash() - |> put_email_status_verified() + |> Map.put(:username, profile.login) + |> Map.put(:givenName, profile.name) + |> Map.put(:picture, profile.avatar_url) end - @doc """ `transform_profile_data_to_person/1` transforms the profile data received from invoking `ElixirAuthGoogle.get_user_profile/1` @@ -115,8 +123,7 @@ defmodule Auth.Person do "givenName" => "Nelson" } """ - def transform_profile_data_to_person(profile) do - # IO.inspect(profile, label: "profile transform_profile_data_to_person/1") + def transform_google_profile_data_to_person(profile) do profile |> Map.put(:familyName, profile.family_name) |> Map.put(:givenName, profile.given_name) @@ -139,12 +146,9 @@ defmodule Auth.Person do end defp put_email_hash(changeset) do - # IO.inspect(changeset, label: "changeset put_email_hash/1") - # IO.inspect(changeset.data, label: "changeset.data") case changeset do %{valid?: true, data: %{email: email}} -> put_change(changeset, :email_hash, email) - # |> IO.inspect(label: "changeset with :email_hash") _ -> changeset @@ -177,11 +181,7 @@ defmodule Auth.Person do `get_person_by_email/1` returns the person based on email address. """ def get_person_by_email(email) do - # IO.inspect(email, label: "get_person_by_email/1 > email") - # {:ok, email_hash} = Fields.EmailHash.dump(email) - # IO.inspect(email_hash, label: "get_person_by_email/1 > email_hash") __MODULE__ |> Repo.get_by(email_hash: email) - # |> IO.inspect(label: "get_person_by_email/1") end end diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex new file mode 100644 index 00000000..872f41c9 --- /dev/null +++ b/lib/auth_web/controllers/auth_controller.ex @@ -0,0 +1,43 @@ +defmodule AuthWeb.AuthController do + use AuthWeb, :controller + + @doc """ + `github_auth/2` handles the callback from GitHub Auth API redirect. + """ + def github_handler(conn, %{"code" => code, "state" => state}) do + {:ok, profile} = ElixirAuthGithub.github_auth(code) + # handler(conn, profile, state) + # IO.inspect(profile, label: "github profile") + conn + |> put_view(AuthWeb.PageView) + |> render(:welcome_github, profile: profile) + end + + + + + @doc """ + `handler/3` responds to successful auth requests. + if the state is defined, redirect to it. + """ + def handler(conn, person, state) do + case not is_nil(state) and state =~ "//" do + true -> # redirect + url = state <> "?jwt=this.is.amaze" + conn + # |> put_req_header("authorization", "MY.JWT.HERE") + |> redirect(external: url) + # |> halt() + false -> # no state + conn + |> put_view(AuthWeb.PageView) + |> render(:welcome_github, person: person) + end + end + + def redirect_to_referer_with_jwt(conn, referer, person) do + IO.inspect(conn, label: "conn") + IO.inspect(referer, label: "referer") + IO.inspect(person, label: "person") + end +end diff --git a/lib/auth_web/controllers/github_auth_controller.ex b/lib/auth_web/controllers/github_auth_controller.ex index 0adc250c..be7a945c 100644 --- a/lib/auth_web/controllers/github_auth_controller.ex +++ b/lib/auth_web/controllers/github_auth_controller.ex @@ -21,9 +21,9 @@ defmodule AuthWeb.GithubAuthController do end end - def redirect_to_referer_with_jwt(conn, state, person) do + def redirect_to_referer_with_jwt(conn, referer, person) do IO.inspect(conn, label: "conn") - IO.inspect(state, label: "state") + IO.inspect(referer, label: "referer") IO.inspect(person, label: "person") end end diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index d17ee8fa..d93f2ef9 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -18,7 +18,7 @@ defmodule AuthWeb.Router do get "/", PageController, :index get "/admin", PageController, :admin - get "/auth/github/callback", GithubAuthController, :index + get "/auth/github/callback", AuthController, :github_handler get "/auth/google/callback", GoogleAuthController, :index end diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs new file mode 100644 index 00000000..2351ae71 --- /dev/null +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -0,0 +1,19 @@ +defmodule AuthWeb.AuthControllerTest do + use AuthWeb.ConnCase + + test "github_handler/2 github auth callback", %{conn: conn} do + conn = get(conn, "/auth/github/callback", + %{code: "123", state: "http://localhost/"}) + assert html_response(conn, 200) =~ "test@gmail.com" + # assert html_response(conn, 302) =~ "http://localhost" + end + + + test "index/2 handler for google auth callback", %{conn: conn} do + + conn = get(conn, Routes.google_auth_path(conn, :index, + %{code: "234", state: "http://localhost/"})) + + assert html_response(conn, 200) =~ "nelson@gmail.com" + end +end diff --git a/test/auth_web/controllers/github_auth_controller_test.exs b/test/auth_web/controllers/github_auth_controller_test.exs deleted file mode 100644 index 99ba6b2e..00000000 --- a/test/auth_web/controllers/github_auth_controller_test.exs +++ /dev/null @@ -1,10 +0,0 @@ -defmodule AuthWeb.GithubAuthControllerTest do - use AuthWeb.ConnCase - - test "index/2 handler for google auth callback", %{conn: conn} do - - conn = get(conn, Routes.github_auth_path(conn, :index, - %{code: "123", state: "http://localhost/"})) - assert html_response(conn, 302) =~ "http://localhost" - end -end diff --git a/test/auth_web/controllers/google_auth_controller_test.exs b/test/auth_web/controllers/google_auth_controller_test.exs index 39443036..d02f6086 100644 --- a/test/auth_web/controllers/google_auth_controller_test.exs +++ b/test/auth_web/controllers/google_auth_controller_test.exs @@ -1,11 +1,5 @@ defmodule AuthWeb.GoogleAuthControllerTest do use AuthWeb.ConnCase - test "index/2 handler for google auth callback", %{conn: conn} do - conn = get(conn, Routes.google_auth_path(conn, :index, - %{code: "234", state: "http://localhost/"})) - - assert html_response(conn, 200) =~ "nelson@gmail.com" - end end From 6cdfb1e147710f968ed8efc32bf9c493dd8175a0 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 21:19:28 +0100 Subject: [PATCH 046/189] add auth_provider to person schema --- lib/auth/person.ex | 30 ++++++++++++------- ...130210036_add_picture_locale_to_people.exs | 1 + priv/repo/seeds.exs | 3 +- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/auth/person.ex b/lib/auth/person.ex index f0622a43..ac3e2f80 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -5,6 +5,7 @@ defmodule Auth.Person do alias __MODULE__ # https://stackoverflow.com/a/47501059/1148249 schema "people" do + field :auth_provider, :string field :email, Fields.EmailEncrypted field :email_hash, Fields.EmailHash field :familyName, Fields.Encrypted @@ -38,13 +39,18 @@ defmodule Auth.Person do :key_id, :locale, :picture, - :username + :username, + :auth_provider ]) |> validate_required([:email]) |> put_email_hash() end def create_person(person) do + person = %Person{} + |> changeset(person) + |> put_email_status_verified() + case get_person_by_email(person.changes.email) do nil -> Repo.insert!(person) @@ -54,14 +60,6 @@ defmodule Auth.Person do end end - def create_google_person(profile) do - person = transform_google_profile_data_to_person(profile) - %Person{} - |> changeset(person) - |> put_email_status_verified() - |> create_person() - end - @doc """ `transform_github_profile_data_to_person/1` transforms the profile data received from invoking `ElixirAuthGithub.github_auth/1` @@ -90,6 +88,7 @@ defmodule Auth.Person do |> Map.put(:username, profile.login) |> Map.put(:givenName, profile.name) |> Map.put(:picture, profile.avatar_url) + |> Map.put(:auth_provider, "github") end @doc """ @@ -129,6 +128,13 @@ defmodule Auth.Person do |> Map.put(:givenName, profile.given_name) |> Map.put(:locale, profile.locale) |> Map.put(:picture, profile.picture) + |> Map.put(:auth_provider, "google") + end + + def create_google_person(profile) do + transform_github_profile_data_to_person(profile) + # IO.inspect(person, label: "person") + |> create_person() end @doc """ @@ -146,9 +152,11 @@ defmodule Auth.Person do end defp put_email_hash(changeset) do + IO.inspect(changeset, label: "changeset:157") + IO.inspect(changeset.data, label: "changeset.data:158") case changeset do - %{valid?: true, data: %{email: email}} -> - put_change(changeset, :email_hash, email) + %{valid?: true} -> + put_change(changeset, :email_hash, changeset.changes.email) _ -> changeset diff --git a/priv/repo/migrations/20191130210036_add_picture_locale_to_people.exs b/priv/repo/migrations/20191130210036_add_picture_locale_to_people.exs index c0d89848..b4665c65 100644 --- a/priv/repo/migrations/20191130210036_add_picture_locale_to_people.exs +++ b/priv/repo/migrations/20191130210036_add_picture_locale_to_people.exs @@ -5,6 +5,7 @@ defmodule Auth.Repo.Migrations.AddPictureLocaleToPeople do alter table(:people) do add :picture, :binary add :locale, :string, default: "en" + add :auth_provider, :string end end end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 3b714388..4308baf3 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -17,11 +17,10 @@ defmodule Auth.Seeds do person = case Person.get_person_by_email(email) do nil -> - %Person{email: email} + %Person{} |> Person.changeset(%{email: email}) |> put_assoc(:statuses, [%Status{text: "verified"}]) |> Repo.insert!() - # |> IO.inspect( label: "inserted") person -> person From 96e6b812353b3f28f07ace53e3fcdf2e2769a37f Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 21:31:21 +0100 Subject: [PATCH 047/189] use generic handler/3 for auth requests --- lib/auth/person.ex | 6 +----- lib/auth_web/controllers/auth_controller.ex | 14 ++++++-------- lib/auth_web/controllers/google_auth_controller.ex | 6 ++---- .../{welcome_google.html.eex => welcome.html.eex} | 0 .../templates/page/welcome_github.html.eex | 9 --------- test/auth_web/controllers/auth_controller_test.exs | 4 ++-- 6 files changed, 11 insertions(+), 28 deletions(-) rename lib/auth_web/templates/page/{welcome_google.html.eex => welcome.html.eex} (100%) delete mode 100644 lib/auth_web/templates/page/welcome_github.html.eex diff --git a/lib/auth/person.ex b/lib/auth/person.ex index ac3e2f80..f9a168c5 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -132,8 +132,7 @@ defmodule Auth.Person do end def create_google_person(profile) do - transform_github_profile_data_to_person(profile) - # IO.inspect(person, label: "person") + transform_google_profile_data_to_person(profile) |> create_person() end @@ -152,8 +151,6 @@ defmodule Auth.Person do end defp put_email_hash(changeset) do - IO.inspect(changeset, label: "changeset:157") - IO.inspect(changeset.data, label: "changeset.data:158") case changeset do %{valid?: true} -> put_change(changeset, :email_hash, changeset.changes.email) @@ -165,7 +162,6 @@ defmodule Auth.Person do defp put_email_status_verified(changeset) do status_verified = Auth.Status.upsert_status("verified") - case changeset do %{valid?: true} -> put_change(changeset, :status, status_verified.id) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 872f41c9..ae09d49d 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -1,19 +1,17 @@ defmodule AuthWeb.AuthController do use AuthWeb, :controller + alias Auth.Person @doc """ `github_auth/2` handles the callback from GitHub Auth API redirect. """ def github_handler(conn, %{"code" => code, "state" => state}) do {:ok, profile} = ElixirAuthGithub.github_auth(code) - # handler(conn, profile, state) - # IO.inspect(profile, label: "github profile") - conn - |> put_view(AuthWeb.PageView) - |> render(:welcome_github, profile: profile) - end - + person = Person.transform_github_profile_data_to_person(profile) + |> Person.create_person() + handler(conn, person, state) + end @doc """ @@ -31,7 +29,7 @@ defmodule AuthWeb.AuthController do false -> # no state conn |> put_view(AuthWeb.PageView) - |> render(:welcome_github, person: person) + |> render(:welcome, person: person) end end diff --git a/lib/auth_web/controllers/google_auth_controller.ex b/lib/auth_web/controllers/google_auth_controller.ex index 7590fcc5..78300c89 100644 --- a/lib/auth_web/controllers/google_auth_controller.ex +++ b/lib/auth_web/controllers/google_auth_controller.ex @@ -5,16 +5,14 @@ defmodule AuthWeb.GoogleAuthController do `index/2` handles the callback from Google Auth API redirect. """ def index(conn, %{"code" => code}) do - # IO.inspect(code, label: "code") - # IO.inspect(state, label: "state") {:ok, token} = ElixirAuthGoogle.get_token(code, conn) {:ok, profile} = ElixirAuthGoogle.get_user_profile(token.access_token) # save profile to people: person = Auth.Person.create_google_person(profile) - # IO.inspect(person, label: "person") + conn |> put_view(AuthWeb.PageView) - |> render(:welcome_google, person: person) + |> render(:welcome, person: person) end end diff --git a/lib/auth_web/templates/page/welcome_google.html.eex b/lib/auth_web/templates/page/welcome.html.eex similarity index 100% rename from lib/auth_web/templates/page/welcome_google.html.eex rename to lib/auth_web/templates/page/welcome.html.eex diff --git a/lib/auth_web/templates/page/welcome_github.html.eex b/lib/auth_web/templates/page/welcome_github.html.eex deleted file mode 100644 index ac95b82c..00000000 --- a/lib/auth_web/templates/page/welcome_github.html.eex +++ /dev/null @@ -1,9 +0,0 @@ -
-

Welcome <%= @profile.name %>! - -

-

You are signed in - with your GitHub Account
- <%= @profile.email %> -

-

diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 2351ae71..927f844f 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -4,8 +4,8 @@ defmodule AuthWeb.AuthControllerTest do test "github_handler/2 github auth callback", %{conn: conn} do conn = get(conn, "/auth/github/callback", %{code: "123", state: "http://localhost/"}) - assert html_response(conn, 200) =~ "test@gmail.com" - # assert html_response(conn, 302) =~ "http://localhost" + # assert html_response(conn, 200) =~ "test@gmail.com" + assert html_response(conn, 302) =~ "http://localhost" end From 3bc3ac1d067fd217cebb1592adce7fb701b07424 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 21:32:44 +0100 Subject: [PATCH 048/189] display auth_provider in welcome template --- lib/auth_web/templates/page/welcome.html.eex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/auth_web/templates/page/welcome.html.eex b/lib/auth_web/templates/page/welcome.html.eex index 7c7d1909..69e00f27 100644 --- a/lib/auth_web/templates/page/welcome.html.eex +++ b/lib/auth_web/templates/page/welcome.html.eex @@ -3,7 +3,7 @@

You are signed in - with your Google Account
+ with your <%= @person.auth_provider %> account
<%= @person.email %>

From 1138f43f43570181fa64f13ac2fddba9514ba58f Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 21:45:32 +0100 Subject: [PATCH 049/189] if no http referer is set, use the base url --- lib/auth_web/controllers/auth_controller.ex | 12 ++++++++++++ lib/auth_web/controllers/google_auth_controller.ex | 14 -------------- lib/auth_web/controllers/page_controller.ex | 6 ++++-- lib/auth_web/router.ex | 2 +- test/auth_web/controllers/auth_controller_test.exs | 7 ++++--- 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index ae09d49d..e2c2be3f 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -9,7 +9,19 @@ defmodule AuthWeb.AuthController do {:ok, profile} = ElixirAuthGithub.github_auth(code) person = Person.transform_github_profile_data_to_person(profile) |> Person.create_person() + # render or redirect: + handler(conn, person, state) + end + @doc """ + `google_handler/2` handles the callback from Google Auth API redirect. + """ + def google_handler(conn, %{"code" => code, "state" => state}) do + {:ok, token} = ElixirAuthGoogle.get_token(code, conn) + {:ok, profile} = ElixirAuthGoogle.get_user_profile(token.access_token) + # save profile to people: + person = Auth.Person.create_google_person(profile) + # render or redirect: handler(conn, person, state) end diff --git a/lib/auth_web/controllers/google_auth_controller.ex b/lib/auth_web/controllers/google_auth_controller.ex index 78300c89..25a48d4a 100644 --- a/lib/auth_web/controllers/google_auth_controller.ex +++ b/lib/auth_web/controllers/google_auth_controller.ex @@ -1,18 +1,4 @@ defmodule AuthWeb.GoogleAuthController do use AuthWeb, :controller - @doc """ - `index/2` handles the callback from Google Auth API redirect. - """ - def index(conn, %{"code" => code}) do - {:ok, token} = ElixirAuthGoogle.get_token(code, conn) - {:ok, profile} = ElixirAuthGoogle.get_user_profile(token.access_token) - - # save profile to people: - person = Auth.Person.create_google_person(profile) - - conn - |> put_view(AuthWeb.PageView) - |> render(:welcome, person: person) - end end diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index db1841cf..ed94d13a 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -5,7 +5,7 @@ defmodule AuthWeb.PageController do state = get_referer(conn) oauth_github_url = ElixirAuthGithub.login_url(%{scopes: ["user:email"], state: state}) - oauth_google_url = ElixirAuthGoogle.generate_oauth_url(conn) + oauth_google_url = ElixirAuthGoogle.generate_oauth_url(conn, state) render(conn, "index.html", [ oauth_github_url: oauth_github_url, oauth_google_url: oauth_google_url @@ -23,6 +23,7 @@ defmodule AuthWeb.PageController do end defp get_referer(conn) do + IO.inspect(conn) # https://stackoverflow.com/questions/37176911/get-http-referrer case List.keyfind(conn.req_headers, "referer", 0) do {"referer", referer} -> @@ -30,7 +31,8 @@ defmodule AuthWeb.PageController do referer nil -> IO.puts "no referer" - "" + ElixirAuthGoogle.get_baseurl_from_conn(conn) + |> IO.inspect(label: "baseurl") end end diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index d93f2ef9..bf0e344d 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -19,7 +19,7 @@ defmodule AuthWeb.Router do get "/", PageController, :index get "/admin", PageController, :admin get "/auth/github/callback", AuthController, :github_handler - get "/auth/google/callback", GoogleAuthController, :index + get "/auth/google/callback", AuthController, :google_handler end # Other scopes may use custom stacks. diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 927f844f..237937bb 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -11,9 +11,10 @@ defmodule AuthWeb.AuthControllerTest do test "index/2 handler for google auth callback", %{conn: conn} do - conn = get(conn, Routes.google_auth_path(conn, :index, - %{code: "234", state: "http://localhost/"})) + conn = get(conn, "/auth/google/callback", + %{code: "234", state: "http://localhost/"}) - assert html_response(conn, 200) =~ "nelson@gmail.com" + # assert html_response(conn, 200) =~ "nelson@gmail.com" + assert html_response(conn, 302) =~ "http://localhost" end end From b225f8de6d506b7ca9a04c6fbac92642a6dae8a0 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 21:46:38 +0100 Subject: [PATCH 050/189] remove provider specific controllers. use generic AuthController --- .../controllers/github_auth_controller.ex | 29 ------------------- .../controllers/google_auth_controller.ex | 4 --- 2 files changed, 33 deletions(-) delete mode 100644 lib/auth_web/controllers/github_auth_controller.ex delete mode 100644 lib/auth_web/controllers/google_auth_controller.ex diff --git a/lib/auth_web/controllers/github_auth_controller.ex b/lib/auth_web/controllers/github_auth_controller.ex deleted file mode 100644 index be7a945c..00000000 --- a/lib/auth_web/controllers/github_auth_controller.ex +++ /dev/null @@ -1,29 +0,0 @@ -defmodule AuthWeb.GithubAuthController do - use AuthWeb, :controller - - @doc """ - `index/2` handles the callback from GitHub Auth API redirect. - """ - def index(conn, %{"code" => code, "state" => state}) do - {:ok, profile} = ElixirAuthGithub.github_auth(code) - - case not is_nil(state) and state =~ "//" do - true -> # state = redirect - url = state <> "?jwt=this.is.amaze" - conn - # |> put_req_header("authorization", "MY.JWT.HERE") - |> redirect(external: url) - # |> halt() - false -> # no state - conn - |> put_view(AuthWeb.PageView) - |> render(:welcome_github, profile: profile) - end - end - - def redirect_to_referer_with_jwt(conn, referer, person) do - IO.inspect(conn, label: "conn") - IO.inspect(referer, label: "referer") - IO.inspect(person, label: "person") - end -end diff --git a/lib/auth_web/controllers/google_auth_controller.ex b/lib/auth_web/controllers/google_auth_controller.ex deleted file mode 100644 index 25a48d4a..00000000 --- a/lib/auth_web/controllers/google_auth_controller.ex +++ /dev/null @@ -1,4 +0,0 @@ -defmodule AuthWeb.GoogleAuthController do - use AuthWeb, :controller - -end From ef45ebb7f3cacedd04b1de011c77b4388e52136f Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 21:48:21 +0100 Subject: [PATCH 051/189] mix format --- lib/auth/application.ex | 1 + lib/auth/person.ex | 7 ++++-- lib/auth/status.ex | 7 +++--- lib/auth/token.ex | 4 ++-- lib/auth_web/controllers/auth_controller.ex | 16 +++++++++---- lib/auth_web/controllers/page_controller.ex | 15 ++++++------ mix.exs | 1 - priv/repo/seeds.exs | 23 +++++++++++-------- .../controllers/auth_controller_test.exs | 8 ++----- .../google_auth_controller_test.exs | 2 -- 10 files changed, 46 insertions(+), 38 deletions(-) diff --git a/lib/auth/application.ex b/lib/auth/application.ex index d04e495e..f227a14d 100644 --- a/lib/auth/application.ex +++ b/lib/auth/application.ex @@ -30,5 +30,6 @@ defmodule Auth.Application do AuthWeb.Endpoint.config_change(changed, removed) :ok end + # coveralls-ignore-stop end diff --git a/lib/auth/person.ex b/lib/auth/person.ex index f9a168c5..0240e83d 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -2,7 +2,8 @@ defmodule Auth.Person do use Ecto.Schema import Ecto.Changeset alias Auth.Repo - alias __MODULE__ # https://stackoverflow.com/a/47501059/1148249 + # https://stackoverflow.com/a/47501059/1148249 + alias __MODULE__ schema "people" do field :auth_provider, :string @@ -47,7 +48,8 @@ defmodule Auth.Person do end def create_person(person) do - person = %Person{} + person = + %Person{} |> changeset(person) |> put_email_status_verified() @@ -162,6 +164,7 @@ defmodule Auth.Person do defp put_email_status_verified(changeset) do status_verified = Auth.Status.upsert_status("verified") + case changeset do %{valid?: true} -> put_change(changeset, :status, status_verified.id) diff --git a/lib/auth/status.ex b/lib/auth/status.ex index cc8f00f9..6ccfd1d3 100644 --- a/lib/auth/status.ex +++ b/lib/auth/status.ex @@ -2,7 +2,8 @@ defmodule Auth.Status do use Ecto.Schema import Ecto.Changeset alias Auth.Repo - alias __MODULE__ # https://stackoverflow.com/a/47501059/1148249 + # https://stackoverflow.com/a/47501059/1148249 + alias __MODULE__ schema "status" do field :text, :string @@ -18,7 +19,6 @@ defmodule Auth.Status do |> validate_required([:text]) end - def create_status(text, person) do # IO.inspect(text, label: "create_status/2 > text") # IO.inspect(person, label: "create_status/2 > person") @@ -31,7 +31,8 @@ defmodule Auth.Status do def upsert_status(text) do case Auth.Repo.get_by(__MODULE__, text: text) do - nil -> # create status + # create status + nil -> email = System.get_env("ADMIN_EMAIL") person = Auth.Person.get_person_by_email(email) create_status(text, person) diff --git a/lib/auth/token.ex b/lib/auth/token.ex index d33d1195..b62eff53 100644 --- a/lib/auth/token.ex +++ b/lib/auth/token.ex @@ -7,7 +7,7 @@ defmodule Auth.Token do @impl true def token_config do - default_claims(default_exp: 31_537_000 ) # ~ 1 year in seconds + # ~ 1 year in seconds + default_claims(default_exp: 31_537_000) end - end diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index e2c2be3f..b95752d6 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -7,8 +7,11 @@ defmodule AuthWeb.AuthController do """ def github_handler(conn, %{"code" => code, "state" => state}) do {:ok, profile} = ElixirAuthGithub.github_auth(code) - person = Person.transform_github_profile_data_to_person(profile) + + person = + Person.transform_github_profile_data_to_person(profile) |> Person.create_person() + # render or redirect: handler(conn, person, state) end @@ -25,20 +28,23 @@ defmodule AuthWeb.AuthController do handler(conn, person, state) end - @doc """ `handler/3` responds to successful auth requests. if the state is defined, redirect to it. """ def handler(conn, person, state) do case not is_nil(state) and state =~ "//" do - true -> # redirect + # redirect + true -> url = state <> "?jwt=this.is.amaze" + conn # |> put_req_header("authorization", "MY.JWT.HERE") |> redirect(external: url) - # |> halt() - false -> # no state + + # |> halt() + # no state + false -> conn |> put_view(AuthWeb.PageView) |> render(:welcome, person: person) diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index ed94d13a..904d6ec1 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -3,13 +3,13 @@ defmodule AuthWeb.PageController do def index(conn, _params) do state = get_referer(conn) - oauth_github_url = - ElixirAuthGithub.login_url(%{scopes: ["user:email"], state: state}) + oauth_github_url = ElixirAuthGithub.login_url(%{scopes: ["user:email"], state: state}) oauth_google_url = ElixirAuthGoogle.generate_oauth_url(conn, state) - render(conn, "index.html", [ + + render(conn, "index.html", oauth_github_url: oauth_github_url, oauth_google_url: oauth_google_url - ]) + ) end # https://github.com/dwyl/auth/issues/46 @@ -27,13 +27,14 @@ defmodule AuthWeb.PageController do # https://stackoverflow.com/questions/37176911/get-http-referrer case List.keyfind(conn.req_headers, "referer", 0) do {"referer", referer} -> - IO.inspect referer, label: "referer" + IO.inspect(referer, label: "referer") referer + nil -> - IO.puts "no referer" + IO.puts("no referer") + ElixirAuthGoogle.get_baseurl_from_conn(conn) |> IO.inspect(label: "baseurl") end end - end diff --git a/mix.exs b/mix.exs index 7b278a19..342ccd0b 100644 --- a/mix.exs +++ b/mix.exs @@ -68,7 +68,6 @@ defmodule Auth.Mixfile do # For publishing Hex.docs: {:ex_doc, "~> 0.21.3", only: :dev} - ] end diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 4308baf3..db099e9a 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -9,22 +9,25 @@ # mix ecto.setup defmodule Auth.Seeds do alias Auth.{Person, Repo, Status} - import Ecto.Changeset # put_assoc + # put_assoc + import Ecto.Changeset + # IO.inspect(System.get_env("ADMIN_EMAIL"), label: "ADMIN_EMAIL") def create_admin do email = System.get_env("ADMIN_EMAIL") - person = case Person.get_person_by_email(email) do - nil -> - %Person{} - |> Person.changeset(%{email: email}) - |> put_assoc(:statuses, [%Status{text: "verified"}]) - |> Repo.insert!() + person = + case Person.get_person_by_email(email) do + nil -> + %Person{} + |> Person.changeset(%{email: email}) + |> put_assoc(:statuses, [%Status{text: "verified"}]) + |> Repo.insert!() - person -> - person - end + person -> + person + end IO.inspect(person.id, label: "seeds.exs person.id") IO.puts("- - - - - - - - - - - - - - - - - - - - - - ") diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 237937bb..0ac9f010 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -2,17 +2,13 @@ defmodule AuthWeb.AuthControllerTest do use AuthWeb.ConnCase test "github_handler/2 github auth callback", %{conn: conn} do - conn = get(conn, "/auth/github/callback", - %{code: "123", state: "http://localhost/"}) + conn = get(conn, "/auth/github/callback", %{code: "123", state: "http://localhost/"}) # assert html_response(conn, 200) =~ "test@gmail.com" assert html_response(conn, 302) =~ "http://localhost" end - test "index/2 handler for google auth callback", %{conn: conn} do - - conn = get(conn, "/auth/google/callback", - %{code: "234", state: "http://localhost/"}) + conn = get(conn, "/auth/google/callback", %{code: "234", state: "http://localhost/"}) # assert html_response(conn, 200) =~ "nelson@gmail.com" assert html_response(conn, 302) =~ "http://localhost" diff --git a/test/auth_web/controllers/google_auth_controller_test.exs b/test/auth_web/controllers/google_auth_controller_test.exs index d02f6086..1ac6f596 100644 --- a/test/auth_web/controllers/google_auth_controller_test.exs +++ b/test/auth_web/controllers/google_auth_controller_test.exs @@ -1,5 +1,3 @@ defmodule AuthWeb.GoogleAuthControllerTest do use AuthWeb.ConnCase - - end From a075902558486b8e391e17e6a2d82a7f2841fe4b Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 22:02:08 +0100 Subject: [PATCH 052/189] add google_handler/2 test with invalid state --- lib/auth_web/controllers/auth_controller.ex | 10 +++++----- lib/auth_web/controllers/page_controller.ex | 7 +------ test/auth_web/controllers/auth_controller_test.exs | 7 +++++++ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index b95752d6..5c7bea83 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -51,9 +51,9 @@ defmodule AuthWeb.AuthController do end end - def redirect_to_referer_with_jwt(conn, referer, person) do - IO.inspect(conn, label: "conn") - IO.inspect(referer, label: "referer") - IO.inspect(person, label: "person") - end + # def redirect_to_referer_with_jwt(conn, referer, person) do + # IO.inspect(conn, label: "conn") + # IO.inspect(referer, label: "referer") + # IO.inspect(person, label: "person") + # end end diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index 904d6ec1..4393e10e 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -14,7 +14,7 @@ defmodule AuthWeb.PageController do # https://github.com/dwyl/auth/issues/46 def admin(conn, params) do - IO.inspect(conn.req_headers, label: "conn.req_headers") + # IO.inspect(conn.req_headers, label: "conn.req_headers") IO.inspect(params, label: "params") conn @@ -23,18 +23,13 @@ defmodule AuthWeb.PageController do end defp get_referer(conn) do - IO.inspect(conn) # https://stackoverflow.com/questions/37176911/get-http-referrer case List.keyfind(conn.req_headers, "referer", 0) do {"referer", referer} -> - IO.inspect(referer, label: "referer") referer nil -> - IO.puts("no referer") - ElixirAuthGoogle.get_baseurl_from_conn(conn) - |> IO.inspect(label: "baseurl") end end end diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 0ac9f010..00527411 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -13,4 +13,11 @@ defmodule AuthWeb.AuthControllerTest do # assert html_response(conn, 200) =~ "nelson@gmail.com" assert html_response(conn, 302) =~ "http://localhost" end + + test "google_handler/2 with invalid state", %{conn: conn} do + conn = get(conn, "/auth/google/callback", %{code: "234", state: "NY"}) + + assert html_response(conn, 200) =~ "nelson@gmail.com" + # assert html_response(conn, 302) =~ "http://localhost" + end end From 04f13ed07004aae4fbd0a46d1247424bd80bb6ea Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 22:14:42 +0100 Subject: [PATCH 053/189] add config :joken for signing tokens #12 --- config/config.exs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/config.exs b/config/config.exs index 56e2e137..86db2400 100644 --- a/config/config.exs +++ b/config/config.exs @@ -26,6 +26,9 @@ config :logger, :console, # Use Jason for JSON parsing in Phoenix config :phoenix, :json_library, Jason +# https://hexdocs.pm/joken/introduction.html#usage +config :joken, default_signer: System.get_env("SECRET_KEY_BASE") + # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. import_config "#{Mix.env()}.exs" From 9dfbb13cc1c5fdb35afc31bee941478f1c7fcf60 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 22:16:42 +0100 Subject: [PATCH 054/189] add JWT to referer url #12 --- lib/auth/person.ex | 5 ++++ lib/auth_web/controllers/auth_controller.ex | 30 ++++++++++++--------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 0240e83d..fdbf9ee7 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -93,6 +93,11 @@ defmodule Auth.Person do |> Map.put(:auth_provider, "github") end + def create_github_person(profile) do + transform_github_profile_data_to_person(profile) + |> create_person() + end + @doc """ `transform_profile_data_to_person/1` transforms the profile data received from invoking `ElixirAuthGoogle.get_user_profile/1` diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 5c7bea83..3095a0dc 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -7,11 +7,8 @@ defmodule AuthWeb.AuthController do """ def github_handler(conn, %{"code" => code, "state" => state}) do {:ok, profile} = ElixirAuthGithub.github_auth(code) - - person = - Person.transform_github_profile_data_to_person(profile) - |> Person.create_person() - + # save profile to people: + person = Person.create_github_person(profile) # render or redirect: handler(conn, person, state) end @@ -23,7 +20,7 @@ defmodule AuthWeb.AuthController do {:ok, token} = ElixirAuthGoogle.get_token(code, conn) {:ok, profile} = ElixirAuthGoogle.get_user_profile(token.access_token) # save profile to people: - person = Auth.Person.create_google_person(profile) + person = Person.create_google_person(profile) # render or redirect: handler(conn, person, state) end @@ -36,7 +33,7 @@ defmodule AuthWeb.AuthController do case not is_nil(state) and state =~ "//" do # redirect true -> - url = state <> "?jwt=this.is.amaze" + url = add_jwt_url_param(person, state) conn # |> put_req_header("authorization", "MY.JWT.HERE") @@ -51,9 +48,18 @@ defmodule AuthWeb.AuthController do end end - # def redirect_to_referer_with_jwt(conn, referer, person) do - # IO.inspect(conn, label: "conn") - # IO.inspect(referer, label: "referer") - # IO.inspect(person, label: "person") - # end + def add_jwt_url_param(person, state) do + IO.inspect(state, label: "state") + IO.inspect(person, label: "person") + data = %{ + auth_provider: person.auth_provider, + givenName: person.givenName, + id: person.id, + picture: person.picture, + status: person.status, + } + jwt = Auth.Token.generate_and_sign!(data) + |> IO.inspect(label: "jwt") + state <> "?jwt=" <> jwt + end end From 02f41fc78cc7f8eb7e1de1ecfcc2e1253b89256d Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 22:31:53 +0100 Subject: [PATCH 055/189] add LICENSE file so we can publish draft --- LICENSE | 340 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..8cdb8451 --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + From 0a58849deeb68184ee876838d1547a731f97c58c Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 22:52:23 +0100 Subject: [PATCH 056/189] use SECRET_KEY_BASE in config.exs --- config/config.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.exs b/config/config.exs index 86db2400..bb2f06fc 100644 --- a/config/config.exs +++ b/config/config.exs @@ -13,7 +13,7 @@ config :auth, # Configures the endpoint config :auth, AuthWeb.Endpoint, url: [host: "localhost"], - secret_key_base: "dGzC3qel+EMmLNQQPhQphV5vdNjODKh9IGCbL8j2lipAwuoAWeysUTtfLDsYVlq7", + secret_key_base: System.get_env("SECRET_KEY_BASE"), render_errors: [view: AuthWeb.ErrorView, accepts: ~w(html json)], pubsub: [name: Auth.PubSub, adapter: Phoenix.PubSub.PG2], live_view: [signing_salt: "G+UI6RIv"] From ea5e810126f12793dd4e047b7be9724eb2763d5e Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 23:11:46 +0100 Subject: [PATCH 057/189] add sendemail using email app #41 --- lib/auth/email.ex | 34 +++++++++++++++++++++ lib/auth_web/controllers/auth_controller.ex | 5 ++- 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 lib/auth/email.ex diff --git a/lib/auth/email.ex b/lib/auth/email.ex new file mode 100644 index 00000000..b40d279e --- /dev/null +++ b/lib/auth/email.ex @@ -0,0 +1,34 @@ +defmodule Auth.Email do + + @doc """ + `sendemail/1` sends an email using AWS SES. + see: https://github.com/dwyl/email#sending-email + params is a map that *must* contain the keys: email, name and template. + + ## Examples + + iex> sendemail(%{"email" => "te@st.co", "name" => "Al", "template" => "hi"}) + %{ + "aud" => "Joken", + "email" => "te@st.co", + "exp" => 1616864371, + "iat" => 1585327371, + "id" => 33, + "iss" => "Joken", + "jti" => "2o03dm2ktf6f1j74es0001e3", + "name" => "Al", + "nbf" => 1585327371, + "status" => "Pending", + "template" => "hi" + } + """ + + def sendemail(params) do + url = System.get_env("EMAIL_APP_URL") <> "/api/send" + jwt = Auth.Token.generate_and_sign!(params) + headers = ["Authorization": "#{jwt}"] + options = [ssl: [{:versions, [:'tlsv1.2']}], recv_timeout: 10000] + {:ok, response} = HTTPoison.post(url, "_nobody", headers, options) + Jason.decode!(response.body) + end +end diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 3095a0dc..cb03582d 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -30,6 +30,7 @@ defmodule AuthWeb.AuthController do if the state is defined, redirect to it. """ def handler(conn, person, state) do + Auth.Email.sendemail(%{email: person.email, template: "welcome"}) case not is_nil(state) and state =~ "//" do # redirect true -> @@ -49,8 +50,6 @@ defmodule AuthWeb.AuthController do end def add_jwt_url_param(person, state) do - IO.inspect(state, label: "state") - IO.inspect(person, label: "person") data = %{ auth_provider: person.auth_provider, givenName: person.givenName, @@ -59,7 +58,7 @@ defmodule AuthWeb.AuthController do status: person.status, } jwt = Auth.Token.generate_and_sign!(data) - |> IO.inspect(label: "jwt") + # |> IO.inspect(label: "jwt") state <> "?jwt=" <> jwt end end From 416ec77f83eeb1d2d034d11d1f5f05827aff4b6e Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 23:11:59 +0100 Subject: [PATCH 058/189] add sendemail test fixes #41 --- test/auth/email_test.exs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test/auth/email_test.exs diff --git a/test/auth/email_test.exs b/test/auth/email_test.exs new file mode 100644 index 00000000..97a047b1 --- /dev/null +++ b/test/auth/email_test.exs @@ -0,0 +1,17 @@ +defmodule Auth.EmailTest do + use ExUnit.Case + + describe "AuthMvp.Email" do + test "sendemail/1 an email" do + params = %{ + "email" => "success@simulator.amazonses.com", + "name" => "Super Successful", + "template" => "welcome" + } + # IO.inspect(params, label: "params") + res = Auth.Email.sendemail(params) + assert Map.get(params, "email") == Map.get(res, "email") + assert Map.get(res, "id") > 0 + end + end +end From e44cbf3fccaf2e00c3228b696c6ea590273f10b6 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 15 Apr 2020 23:15:43 +0100 Subject: [PATCH 059/189] tidy up handler/3 --- lib/auth_web/controllers/auth_controller.ex | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index cb03582d..365eb7ea 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -30,18 +30,15 @@ defmodule AuthWeb.AuthController do if the state is defined, redirect to it. """ def handler(conn, person, state) do + # Send welcome email: Auth.Email.sendemail(%{email: person.email, template: "welcome"}) + # check if valid state (HTTP referer) is defined: case not is_nil(state) and state =~ "//" do # redirect true -> - url = add_jwt_url_param(person, state) - conn - # |> put_req_header("authorization", "MY.JWT.HERE") - |> redirect(external: url) + |> redirect(external: add_jwt_url_param(person, state)) - # |> halt() - # no state false -> conn |> put_view(AuthWeb.PageView) From 731e6dbd52784e29e5865158720a1560d4a66e5c Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 16 Apr 2020 20:41:28 +0100 Subject: [PATCH 060/189] update dependencies to latest versions --- mix.exs | 12 ++++++------ mix.lock | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/mix.exs b/mix.exs index 342ccd0b..6dd39614 100644 --- a/mix.exs +++ b/mix.exs @@ -46,14 +46,14 @@ defmodule Auth.Mixfile do # Phoenix core: {:phoenix, "~> 1.4.16"}, {:phoenix_pubsub, "~> 1.1"}, - {:phoenix_ecto, "~> 4.0"}, - {:ecto_sql, "~> 3.1"}, + {:phoenix_ecto, "~> 4.1.0"}, + {:ecto_sql, "~> 3.4.2"}, {:postgrex, ">= 0.0.0"}, - {:phoenix_html, "~> 2.11"}, + {:phoenix_html, "~> 2.14.1"}, {:phoenix_live_reload, "~> 1.2", only: :dev}, - {:gettext, "~> 0.11"}, - {:jason, "~> 1.0"}, - {:plug_cowboy, "~> 2.0"}, + {:gettext, "~> 0.17.2"}, + {:jason, "~> 1.2.0"}, + {:plug_cowboy, "~> 2.1.3"}, # Field Validation and Encryption: {:fields, "~> 2.4.0"}, diff --git a/mix.lock b/mix.lock index 9f92da6a..1864f729 100644 --- a/mix.lock +++ b/mix.lock @@ -9,7 +9,7 @@ "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, "ecto": {:hex, :ecto, "3.4.2", "6890af71025769bd27ef62b1ed1925cfe23f7f0460bcb3041da4b705215ff23e", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3959b8a83e086202a4bd86b4b5e6e71f9f1840813de14a57d502d3fc2ef7132"}, - "ecto_sql": {:hex, :ecto_sql, "3.4.1", "3c9136ba138f9b74d31286c73c61232a92bd19385f7c5607bdeb3a4587ef91f5", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b4be0bffe7b0bdf5393defcae52712f248e70cc2bc0e8ab6ddb03be66371516"}, + "ecto_sql": {:hex, :ecto_sql, "3.4.2", "3d842665a81ba2137b62aa70151afe81dae44824cd09b2076a255937ab4e2dc9", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f2b064102467e1525314a464b6fea0707ff28ee132a15006727ccf51b73492ff"}, "elixir_auth_github": {:hex, :elixir_auth_github, "1.2.0", "b7233c8de0c0c85c6fcd32f0ab6e3d32d6ccb90262d3efbf6bb064d3551369d8", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "7f11269bf0170af6b61e17653bf8fa922347a08ceac59a9fba0e27c980c28938"}, "elixir_auth_google": {:hex, :elixir_auth_google, "1.2.0", "99672af2cadcf18bad10548a75667dada2f802cf1a8c519d2283346c8b8e99b8", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "8720fd473f471a9a03d6630fc3c8b90dff5e089047e153dc1f78ed68a68740a9"}, "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, @@ -39,7 +39,7 @@ "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.1", "274a4b07c4adbdd7785d45a8b0bb57634d0b4f45b18d2c508b26c0344bd59b8f", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "41b4103a2fa282cfd747d377233baf213c648fdcc7928f432937676532490eee"}, "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm", "1f13f9f0f3e769a667a6b6828d29dec37497a082d195cc52dbef401a9b69bf38"}, "plug": {:hex, :plug, "1.10.0", "6508295cbeb4c654860845fb95260737e4a8838d34d115ad76cd487584e2fc4d", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "422a9727e667be1bf5ab1de03be6fa0ad67b775b2d84ed908f3264415ef29d4a"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.1.2", "8b0addb5908c5238fac38e442e81b6fcd32788eaa03246b4d55d147c47c5805e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "7d722581ce865a237e14da6d946f92704101740a256bd13ec91e63c0b122fc70"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.1.3", "38999a3e85e39f0e6bdfdf820761abac61edde1632cfebbacc445cdcb6ae1333", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "056f41f814dbb38ea44613e0f613b3b2b2f2c6afce64126e252837669eba84db"}, "plug_crypto": {:hex, :plug_crypto, "1.1.2", "bdd187572cc26dbd95b87136290425f2b580a116d3fb1f564216918c9730d227", [:mix], [], "hexpm", "6b8b608f895b6ffcfad49c37c7883e8df98ae19c6a28113b02aa1e9c5b22d6b5"}, "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, "postgrex": {:hex, :postgrex, "0.15.3", "5806baa8a19a68c4d07c7a624ccdb9b57e89cbc573f1b98099e3741214746ae4", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "4737ce62a31747b4c63c12b20c62307e51bb4fcd730ca0c32c280991e0606c90"}, From 5083e28cc56ae0e784f0612af5fd0e78310cd566 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 16 Apr 2020 21:06:25 +0100 Subject: [PATCH 061/189] create tests for extracting referer headers for #46 --- lib/auth/email.ex | 5 ++--- lib/auth/person.ex | 17 +++++++---------- lib/auth_web/controllers/auth_controller.ex | 12 ++++++++++-- test/auth/email_test.exs | 1 + .../controllers/page_controller_test.exs | 13 +++++++++++++ 5 files changed, 33 insertions(+), 15 deletions(-) diff --git a/lib/auth/email.ex b/lib/auth/email.ex index b40d279e..ddc3f494 100644 --- a/lib/auth/email.ex +++ b/lib/auth/email.ex @@ -1,5 +1,4 @@ defmodule Auth.Email do - @doc """ `sendemail/1` sends an email using AWS SES. see: https://github.com/dwyl/email#sending-email @@ -26,8 +25,8 @@ defmodule Auth.Email do def sendemail(params) do url = System.get_env("EMAIL_APP_URL") <> "/api/send" jwt = Auth.Token.generate_and_sign!(params) - headers = ["Authorization": "#{jwt}"] - options = [ssl: [{:versions, [:'tlsv1.2']}], recv_timeout: 10000] + headers = [Authorization: "#{jwt}"] + options = [ssl: [{:versions, [:"tlsv1.2"]}], recv_timeout: 10000] {:ok, response} = HTTPoison.post(url, "_nobody", headers, options) Jason.decode!(response.body) end diff --git a/lib/auth/person.ex b/lib/auth/person.ex index fdbf9ee7..d6f36b51 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -94,8 +94,7 @@ defmodule Auth.Person do end def create_github_person(profile) do - transform_github_profile_data_to_person(profile) - |> create_person() + transform_github_profile_data_to_person(profile) |> create_person() end @doc """ @@ -130,17 +129,15 @@ defmodule Auth.Person do } """ def transform_google_profile_data_to_person(profile) do - profile - |> Map.put(:familyName, profile.family_name) - |> Map.put(:givenName, profile.given_name) - |> Map.put(:locale, profile.locale) - |> Map.put(:picture, profile.picture) - |> Map.put(:auth_provider, "google") + Map.merge(profile, %{ + familyName: profile.family_name, + givenName: profile.given_name, + auth_provider: "google" + }) end def create_google_person(profile) do - transform_google_profile_data_to_person(profile) - |> create_person() + transform_google_profile_data_to_person(profile) |> create_person() end @doc """ diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 365eb7ea..862ac78d 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -30,8 +30,15 @@ defmodule AuthWeb.AuthController do if the state is defined, redirect to it. """ def handler(conn, person, state) do + IO.inspect(person, label: "person") # Send welcome email: - Auth.Email.sendemail(%{email: person.email, template: "welcome"}) + Auth.Email.sendemail(%{ + email: person.email, + name: person.givenName, + template: "welcome" + }) + |> IO.inspect(label: "email") + # check if valid state (HTTP referer) is defined: case not is_nil(state) and state =~ "//" do # redirect @@ -52,8 +59,9 @@ defmodule AuthWeb.AuthController do givenName: person.givenName, id: person.id, picture: person.picture, - status: person.status, + status: person.status } + jwt = Auth.Token.generate_and_sign!(data) # |> IO.inspect(label: "jwt") state <> "?jwt=" <> jwt diff --git a/test/auth/email_test.exs b/test/auth/email_test.exs index 97a047b1..d37a1654 100644 --- a/test/auth/email_test.exs +++ b/test/auth/email_test.exs @@ -8,6 +8,7 @@ defmodule Auth.EmailTest do "name" => "Super Successful", "template" => "welcome" } + # IO.inspect(params, label: "params") res = Auth.Email.sendemail(params) assert Map.get(params, "email") == Map.get(res, "email") diff --git a/test/auth_web/controllers/page_controller_test.exs b/test/auth_web/controllers/page_controller_test.exs index 68c7bd12..5016927f 100644 --- a/test/auth_web/controllers/page_controller_test.exs +++ b/test/auth_web/controllers/page_controller_test.exs @@ -5,4 +5,17 @@ defmodule AuthWeb.PageControllerTest do conn = get(conn, "/") assert html_response(conn, 200) =~ "login to" end + + test "GET /admin", %{conn: conn} do + conn = get(conn, "/admin") + assert html_response(conn, 200) =~ "Login" + end + + test "get_referer/1", %{conn: conn} do + conn = conn + |> put_req_header("referer", "http://localhost/admin") + |> get("/") + + assert conn.resp_body =~ "state=http://localhost/admin" + end end From e89e5608dc803d96c2fd3d8e58ea8d16d0a5d1e2 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 16 Apr 2020 21:07:23 +0100 Subject: [PATCH 062/189] temporarily comment out tag and session as not (yet) used --- lib/auth/session.ex | 46 ++++++++++++++++++++++----------------------- lib/auth/tag.ex | 34 ++++++++++++++++----------------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/lib/auth/session.ex b/lib/auth/session.ex index afb22cc9..c41cb2e7 100644 --- a/lib/auth/session.ex +++ b/lib/auth/session.ex @@ -1,23 +1,23 @@ -defmodule Auth.Session do - use Ecto.Schema - import Ecto.Changeset - - schema "sessions" do - field :auth_token, Fields.Encrypted - field :refresh_token, Fields.Encrypted - field :key_id, :integer - - belongs_to :person, Auth.Person - timestamps() - end - - def changeset(people, attrs) do - Ecto.build_assoc(people, :sessions) - |> cast(attrs, [:auth_token, :refresh_token]) - |> validate_required([:auth_token, :refresh_token]) - end - - def basic_changeset(people, _attrs) do - Ecto.build_assoc(people, :sessions) - end -end +# defmodule Auth.Session do +# use Ecto.Schema +# import Ecto.Changeset +# +# schema "sessions" do +# field :auth_token, Fields.Encrypted +# field :refresh_token, Fields.Encrypted +# field :key_id, :integer +# +# belongs_to :person, Auth.Person +# timestamps() +# end +# +# def changeset(people, attrs) do +# Ecto.build_assoc(people, :sessions) +# |> cast(attrs, [:auth_token, :refresh_token]) +# |> validate_required([:auth_token, :refresh_token]) +# end +# +# def basic_changeset(people, _attrs) do +# Ecto.build_assoc(people, :sessions) +# end +# end diff --git a/lib/auth/tag.ex b/lib/auth/tag.ex index ad5d0709..6b815994 100644 --- a/lib/auth/tag.ex +++ b/lib/auth/tag.ex @@ -1,17 +1,17 @@ -defmodule Auth.Tag do - use Ecto.Schema - import Ecto.Changeset - - schema "tags" do - field :text, :string - belongs_to :person, Auth.Person - timestamps() - end - - @doc false - def changeset(tag, attrs) do - tag - |> cast(attrs, [:text]) - |> validate_required([:text]) - end -end +# defmodule Auth.Tag do +# use Ecto.Schema +# import Ecto.Changeset +# +# schema "tags" do +# field :text, :string +# belongs_to :person, Auth.Person +# timestamps() +# end +# +# @doc false +# def changeset(tag, attrs) do +# tag +# |> cast(attrs, [:text]) +# |> validate_required([:text]) +# end +# end From 4cdb5da280aad46ec2ba60ca52596935af0c6182 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 16 Apr 2020 21:12:32 +0100 Subject: [PATCH 063/189] delete old GoogleAuthControllerTest as consolidated into general Auth Tests --- lib/auth_web/templates/page/admin.html.eex | 1 - test/auth_web/controllers/google_auth_controller_test.exs | 3 --- 2 files changed, 4 deletions(-) delete mode 100644 test/auth_web/controllers/google_auth_controller_test.exs diff --git a/lib/auth_web/templates/page/admin.html.eex b/lib/auth_web/templates/page/admin.html.eex index 7e8848b1..d40c9396 100644 --- a/lib/auth_web/templates/page/admin.html.eex +++ b/lib/auth_web/templates/page/admin.html.eex @@ -1,2 +1 @@ -Hello World! Login diff --git a/test/auth_web/controllers/google_auth_controller_test.exs b/test/auth_web/controllers/google_auth_controller_test.exs deleted file mode 100644 index 1ac6f596..00000000 --- a/test/auth_web/controllers/google_auth_controller_test.exs +++ /dev/null @@ -1,3 +0,0 @@ -defmodule AuthWeb.GoogleAuthControllerTest do - use AuthWeb.ConnCase -end From 7b2df5bcf2a757ca19048e1aef55379cb28625a3 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 16 Apr 2020 21:25:37 +0100 Subject: [PATCH 064/189] add create_person/1 test ... 100%! --- lib/auth/person.ex | 63 ++++++++++++++++----------------------- mix.exs | 2 +- test/auth/person_test.exs | 15 ++++++++++ 3 files changed, 41 insertions(+), 39 deletions(-) create mode 100644 test/auth/person_test.exs diff --git a/lib/auth/person.ex b/lib/auth/person.ex index d6f36b51..1eb23eea 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -140,51 +140,38 @@ defmodule Auth.Person do transform_google_profile_data_to_person(profile) |> create_person() end - @doc """ - Changeset function used for email/password registration - Define email hash and password hash - """ - def changeset_registration(profile, attrs) do - profile - |> cast(attrs, [:email, :password]) - |> validate_required([:email, :password]) - |> validate_length(:password, min: 6, max: 100) - |> unique_constraint(:email) - |> put_email_hash() - |> put_pass_hash() - end + # @doc """ + # Changeset function used for email/password registration + # Define email hash and password hash + # """ + # def changeset_registration(profile, attrs) do + # profile + # |> cast(attrs, [:email, :password]) + # |> validate_required([:email, :password]) + # |> validate_length(:password, min: 6, max: 100) + # |> unique_constraint(:email) + # |> put_email_hash() + # |> put_pass_hash() + # end defp put_email_hash(changeset) do - case changeset do - %{valid?: true} -> - put_change(changeset, :email_hash, changeset.changes.email) - - _ -> - changeset - end + put_change(changeset, :email_hash, changeset.changes.email) end - defp put_email_status_verified(changeset) do + def put_email_status_verified(changeset) do status_verified = Auth.Status.upsert_status("verified") - - case changeset do - %{valid?: true} -> - put_change(changeset, :status, status_verified.id) - - _ -> - changeset - end + put_change(changeset, :status, status_verified.id) end - defp put_pass_hash(changeset) do - case changeset do - %Ecto.Changeset{valid?: true, changes: %{password: pass}} -> - put_change(changeset, :password_hash, pass) - - _ -> - changeset - end - end + # defp put_pass_hash(changeset) do + # case changeset do + # %Ecto.Changeset{valid?: true, changes: %{password: pass}} -> + # put_change(changeset, :password_hash, pass) + # + # _ -> + # changeset + # end + # end @doc """ `get_person_by_email/1` returns the person based on email address. diff --git a/mix.exs b/mix.exs index 6dd39614..3277355d 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Auth.Mixfile do def project do [ app: :auth, - version: "1.2.0", + version: "1.2.1", elixir: "~> 1.9", elixirc_paths: elixirc_paths(Mix.env()), compilers: [:phoenix, :gettext] ++ Mix.compilers(), diff --git a/test/auth/person_test.exs b/test/auth/person_test.exs new file mode 100644 index 00000000..7ed2b8bc --- /dev/null +++ b/test/auth/person_test.exs @@ -0,0 +1,15 @@ +defmodule Auth.PersonTest do + use Auth.DataCase + alias Auth.{Person} + + test "create_person/1" do + alex = %{email: "alex@gmail.com"} + person = Person.create_person(alex) + # IO.inspect(person, label: "person:8") + assert person.id > 1 + + # attempt to recreate alex (just returns existing record): + person2 = Person.create_person(alex) + assert person2.id == person.id + end +end From da911b238f70d5cfd048e91a0c28828783142ce1 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 16 Apr 2020 21:38:55 +0100 Subject: [PATCH 065/189] update readme badges --- README.md | 2 +- test/auth/person_test.exs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index ec664d27..cae5bec5 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ you can setup in ***5 minutes***. [![Build Status](https://img.shields.io/travis/dwyl/fields/master.svg?style=flat-square)](https://travis-ci.org/dwyl/fields) [![codecov.io](https://img.shields.io/codecov/c/github/dwyl/fields/master.svg?style=flat-square)](http://codecov.io/github/dwyl/fields?branch=master) -[![Hex.pm](https://img.shields.io/hexpm/v/fields?color=brightgreen&style=flat-square)](https://hex.pm/packages/fields) +[![Hex.pm](https://img.shields.io/hexpm/v/auth?color=brightgreen&style=flat-square)](https://hex.pm/packages/auth) [![Libraries.io dependency status](https://img.shields.io/librariesio/release/hex/auth?logoColor=brightgreen&style=flat-square)](https://github.com/dwyl/auth/blob/master/mix.exs) [![docs](https://img.shields.io/badge/docs-maintained-brightgreen?style=flat-square)](https://hexdocs.pm/fields/api-reference.html) [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat-square)](https://github.com/dwyl/fields/issues) diff --git a/test/auth/person_test.exs b/test/auth/person_test.exs index 7ed2b8bc..5d4ffb0b 100644 --- a/test/auth/person_test.exs +++ b/test/auth/person_test.exs @@ -5,7 +5,6 @@ defmodule Auth.PersonTest do test "create_person/1" do alex = %{email: "alex@gmail.com"} person = Person.create_person(alex) - # IO.inspect(person, label: "person:8") assert person.id > 1 # attempt to recreate alex (just returns existing record): From 4c1a1c446ab83037a5b70b6a312cf486e9336719 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 22 Apr 2020 13:37:43 +0100 Subject: [PATCH 066/189] create elixir_buildpack.config for deploying to heroku --- elixir_buildpack.config | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 elixir_buildpack.config diff --git a/elixir_buildpack.config b/elixir_buildpack.config new file mode 100644 index 00000000..e650d238 --- /dev/null +++ b/elixir_buildpack.config @@ -0,0 +1,8 @@ +# Elixir version +elixir_version=1.10 + +# Erlang version +# available versions https://github.com/HashNuke/heroku-buildpack-elixir-otp-builds/blob/master/otp-versions +erlang_version=22.2.7 + +# always_rebuild=true From 0cc7cf5376306836209e68a11063dffb2ca70e1c Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 22 Apr 2020 13:38:48 +0100 Subject: [PATCH 067/189] link directly to https://libraries.io/hex/auth dependency status --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cae5bec5..e91c363f 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ you can setup in ***5 minutes***. [![Build Status](https://img.shields.io/travis/dwyl/fields/master.svg?style=flat-square)](https://travis-ci.org/dwyl/fields) [![codecov.io](https://img.shields.io/codecov/c/github/dwyl/fields/master.svg?style=flat-square)](http://codecov.io/github/dwyl/fields?branch=master) [![Hex.pm](https://img.shields.io/hexpm/v/auth?color=brightgreen&style=flat-square)](https://hex.pm/packages/auth) -[![Libraries.io dependency status](https://img.shields.io/librariesio/release/hex/auth?logoColor=brightgreen&style=flat-square)](https://github.com/dwyl/auth/blob/master/mix.exs) +[![Libraries.io dependency status](https://img.shields.io/librariesio/release/hex/auth?logoColor=brightgreen&style=flat-square)](https://libraries.io/hex/auth) [![docs](https://img.shields.io/badge/docs-maintained-brightgreen?style=flat-square)](https://hexdocs.pm/fields/api-reference.html) [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat-square)](https://github.com/dwyl/fields/issues) + From 99aba24e5c4c8d1224ba87d4d385414c23f15d85 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Sun, 26 Apr 2020 23:48:22 +0100 Subject: [PATCH 101/189] add clarifying comment to index.html.eex for #50 --- lib/auth_web/templates/page/index.html.eex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/auth_web/templates/page/index.html.eex b/lib/auth_web/templates/page/index.html.eex index 348db3c3..a9a78a96 100644 --- a/lib/auth_web/templates/page/index.html.eex +++ b/lib/auth_web/templates/page/index.html.eex @@ -44,5 +44,5 @@
- + From 27710477f8e5fa566df68894b36d321bdd11c1b0 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Sun, 26 Apr 2020 23:55:21 +0100 Subject: [PATCH 102/189] add test for /profile endpoint (back up to 100%) --- lib/auth_web/controllers/page_controller.ex | 14 +++++--------- test/auth_web/controllers/page_controller_test.exs | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index c144f7a7..96eb8582 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -13,15 +13,11 @@ defmodule AuthWeb.PageController do end # https://github.com/dwyl/auth/issues/46 - # def admin(conn, params) do - # # IO.inspect(conn.req_headers, label: "conn.req_headers") - # IO.inspect(params, label: "params") - # IO.inspect(conn.assigns) - # - # conn - # |> put_view(AuthWeb.PageView) - # |> render(:welcome) - # end + def admin(conn, _params) do + conn + |> put_view(AuthWeb.PageView) + |> render(:welcome) + end def get_referer(conn) do # https://stackoverflow.com/questions/37176911/get-http-referrer diff --git a/test/auth_web/controllers/page_controller_test.exs b/test/auth_web/controllers/page_controller_test.exs index 17a9a268..912611fd 100644 --- a/test/auth_web/controllers/page_controller_test.exs +++ b/test/auth_web/controllers/page_controller_test.exs @@ -42,4 +42,18 @@ defmodule AuthWeb.PageControllerTest do assert html_response(conn, 200) =~ "google account" # assert html_response(conn, 302) =~ "redirected" end + + test "admin/2 show welcome page", %{conn: conn} do + data = %{ + email: "nelson@gmail.com", + givenName: "McTestin", + picture: "https://youtu.be/naoknj1ebqI", + auth_provider: "google" + } + person = Auth.Person.create_person(data) # |> IO.inspect(label: "person") + conn = AuthPlug.create_jwt_session(conn, Map.merge(data, %{id: person.id})) + conn = get(conn, "/profile", %{}) + assert html_response(conn, 200) =~ "google account" + # assert html_response(conn, 302) =~ "redirected" + end end From 810d4fea5755860e0e1112dd7b01b868589d9b32 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 28 Apr 2020 13:42:00 +0100 Subject: [PATCH 103/189] tests work with exbase58 https://github.com/dwyl/base58/issues/20 --- README.md | 13 ++++++------- lib/auth/person.ex | 9 ++++++++- lib/auth_web/controllers/auth_controller.ex | 5 ++--- lib/auth_web/router.ex | 6 +++--- mix.lock | 4 ++-- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index e91c363f..36dc9d53 100644 --- a/README.md +++ b/README.md @@ -5,16 +5,15 @@ A ***complete authentication solution*** for **Phoenix** Apps/APIs you can setup in ***5 minutes***. -[![Build Status](https://img.shields.io/travis/dwyl/fields/master.svg?style=flat-square)](https://travis-ci.org/dwyl/fields) -[![codecov.io](https://img.shields.io/codecov/c/github/dwyl/fields/master.svg?style=flat-square)](http://codecov.io/github/dwyl/fields?branch=master) +[![Build Status](https://img.shields.io/travis/dwyl/auth/master.svg?style=flat-square)](https://travis-ci.org/dwyl/auth) +[![codecov.io](https://img.shields.io/codecov/c/github/dwyl/auth/master.svg?style=flat-square)](http://codecov.io/github/dwyl/auth?branch=master) [![Hex.pm](https://img.shields.io/hexpm/v/auth?color=brightgreen&style=flat-square)](https://hex.pm/packages/auth) [![Libraries.io dependency status](https://img.shields.io/librariesio/release/hex/auth?logoColor=brightgreen&style=flat-square)](https://libraries.io/hex/auth) -[![docs](https://img.shields.io/badge/docs-maintained-brightgreen?style=flat-square)](https://hexdocs.pm/fields/api-reference.html) -[![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat-square)](https://github.com/dwyl/fields/issues) -
diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 1eb23eea..6a1933c2 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -48,10 +48,12 @@ defmodule Auth.Person do end def create_person(person) do + IO.inspect(person, label: "create_person:51") person = %Person{} |> changeset(person) |> put_email_status_verified() + |> IO.inspect(label: "after put_email_status_verified") case get_person_by_email(person.changes.email) do nil -> @@ -129,15 +131,19 @@ defmodule Auth.Person do } """ def transform_google_profile_data_to_person(profile) do + IO.inspect(profile, label: "profile") Map.merge(profile, %{ familyName: profile.family_name, givenName: profile.given_name, auth_provider: "google" }) + |> IO.inspect(label: "merged") end def create_google_person(profile) do - transform_google_profile_data_to_person(profile) |> create_person() + transform_google_profile_data_to_person(profile) + |> create_person() + |> IO.inspect(label: "create_person:") end # @doc """ @@ -156,6 +162,7 @@ defmodule Auth.Person do defp put_email_hash(changeset) do put_change(changeset, :email_hash, changeset.changes.email) + |> IO.inspect(label: "changeset with :email_hash") end def put_email_status_verified(changeset) do diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 3d0a2d50..8805b148 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -50,10 +50,9 @@ defmodule AuthWeb.AuthController do }) |> IO.inspect(label: "email") + IO.inspect(state, label: "state handler/3:53") + # check if valid state (HTTP referer) is defined: - base_url = AuthPlug.Helpers.get_baseurl_from_conn(conn) - IO.inspect(state, label: "state") - IO.inspect(base_url, label: "base_url") case not is_nil(state) do true -> # redirect conn diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index 58c012c2..c5980937 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -26,12 +26,12 @@ defmodule AuthWeb.Router do plug(AuthPlug, %{auth_url: "https://dwylauth.herokuapp.com"}) end - scope "/", AuthWeb do + scope "/profile", AuthWeb do pipe_through :browser pipe_through :auth - get "/profile", PageController, :admin - resources "/profile/apikeys", ApikeyController + get "/", PageController, :admin + resources "/apikeys", ApikeyController end # Other scopes may use custom stacks. diff --git a/mix.lock b/mix.lock index a3caf1f8..12a4aa89 100644 --- a/mix.lock +++ b/mix.lock @@ -9,8 +9,8 @@ "db_connection": {:hex, :db_connection, "2.2.2", "3bbca41b199e1598245b716248964926303b5d4609ff065125ce98bcd368939e", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm", "642af240d8a8affb93b4ba5a6fcd2bbcbdc327e1a524b825d383711536f8070c"}, "decimal": {:hex, :decimal, "1.8.1", "a4ef3f5f3428bdbc0d35374029ffcf4ede8533536fa79896dd450168d9acdf3c", [:mix], [], "hexpm", "3cb154b00225ac687f6cbd4acc4b7960027c757a5152b369923ead9ddbca7aec"}, "earmark": {:hex, :earmark, "1.4.3", "364ca2e9710f6bff494117dbbd53880d84bebb692dafc3a78eb50aa3183f2bfd", [:mix], [], "hexpm", "8cf8a291ebf1c7b9539e3cddb19e9cef066c2441b1640f13c34c1d3cfc825fec"}, - "ecto": {:hex, :ecto, "3.4.2", "6890af71025769bd27ef62b1ed1925cfe23f7f0460bcb3041da4b705215ff23e", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3959b8a83e086202a4bd86b4b5e6e71f9f1840813de14a57d502d3fc2ef7132"}, - "ecto_sql": {:hex, :ecto_sql, "3.4.2", "3d842665a81ba2137b62aa70151afe81dae44824cd09b2076a255937ab4e2dc9", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f2b064102467e1525314a464b6fea0707ff28ee132a15006727ccf51b73492ff"}, + "ecto": {:hex, :ecto, "3.4.3", "3a14c2500c3964165245a4f24a463e080762f7ccd0c632c763ea589f75ca205f", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b6f18dea95f2004d0369f6a8346513ca3f706614f4ede219a5f3fe5db5dd962"}, + "ecto_sql": {:hex, :ecto_sql, "3.4.3", "c552aa8a7ccff2b64024f835503b3155d8e73452c180298527fbdbcd6e79710b", [:mix], [{:db_connection, "~> 2.2", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.3.0 or ~> 0.4.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.0", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ec9e59d6fa3f8cfda9963ada371e9e6659167c2338a997bd7ea23b10b245842b"}, "elixir_auth_github": {:hex, :elixir_auth_github, "1.2.0", "b7233c8de0c0c85c6fcd32f0ab6e3d32d6ccb90262d3efbf6bb064d3551369d8", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "7f11269bf0170af6b61e17653bf8fa922347a08ceac59a9fba0e27c980c28938"}, "elixir_auth_google": {:hex, :elixir_auth_google, "1.2.0", "99672af2cadcf18bad10548a75667dada2f802cf1a8c519d2283346c8b8e99b8", [:mix], [{:httpoison, "~> 1.6.2", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 4.0.1", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "8720fd473f471a9a03d6630fc3c8b90dff5e089047e153dc1f78ed68a68740a9"}, "elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm", "d522695b93b7f0b4c0fcb2dfe73a6b905b1c301226a5a55cb42e5b14d509e050"}, From 52b4f365dea99da63b5be2a648695acef88b94ff Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 28 Apr 2020 14:45:24 +0100 Subject: [PATCH 104/189] use auth_plug 0.12.0 with client_id in referer https://github.com/dwyl/auth/issues/55 --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index a9d6be7a..71d1b6a3 100644 --- a/mix.exs +++ b/mix.exs @@ -61,7 +61,7 @@ defmodule Auth.Mixfile do # https://github.com/dwyl/elixir-auth-google {:elixir_auth_google, "~> 1.2.0"}, # https://github.com/dwyl/auth_plug - {:auth_plug, "~> 0.11.0"}, + {:auth_plug, "~> 0.12.0"}, # Field Validation and Encryption: github.com/dwyl/fields {:fields, "~> 2.4.0"}, diff --git a/mix.lock b/mix.lock index 12a4aa89..f4c58a06 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ "argon2_elixir": {:hex, :argon2_elixir, "2.3.0", "e251bdafd69308e8c1263e111600e6d68bd44f23d2cccbe43fcb1a417a76bc8e", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "28ccb63bff213aecec1f7f3dde9648418b031f822499973281d8f494b9d5a3b3"}, - "auth_plug": {:hex, :auth_plug, "0.11.0", "d1a6596ad35de45929432018fa1b0bf4e642fc2f92ef5a1f24bd6117bb01db96", [:mix], [{:joken, "~> 2.2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "b0a95785243915ad80b76588de60c3e0dcbca17e19367b6a610d1d84de43d83c"}, + "auth_plug": {:hex, :auth_plug, "0.12.0", "8cbd8eebfd4760a01137c0be5c4585cf43d4a7f4bdba418fce0c968b152f5eb2", [:mix], [{:joken, "~> 2.2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "91a9a607359149045aa8c42e6d57a56763ea1ca86d2a5f14ca7d33524bf1e6e7"}, "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"}, "comeonin": {:hex, :comeonin, "5.3.1", "7fe612b739c78c9c1a75186ef2d322ce4d25032d119823269d0aa1e2f1e20025", [:mix], [], "hexpm", "d6222483060c17f0977fad1b7401ef0c5863c985a64352755f366aee3799c245"}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, From 3cdd44ca55ffd15d4617df8f98f25eb0ab688e20 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 28 Apr 2020 15:32:38 +0100 Subject: [PATCH 105/189] create_apikey_for_admin/1 in seeds.exs for #55 --- priv/repo/seeds.exs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index db099e9a..6699a59a 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -31,7 +31,25 @@ defmodule Auth.Seeds do IO.inspect(person.id, label: "seeds.exs person.id") IO.puts("- - - - - - - - - - - - - - - - - - - - - - ") + + person + end + + def create_apikey_for_admin(person) do + + {:ok, key} = %{"name" => "default key", "url" => "http://localhost:4000"} + |> AuthWeb.ApikeyController.make_apikey(person.id) + # |> IO.inspect(label: "apikey_params") + |> Auth.Apikey.create_apikey() + + # IO.inspect(key, label: "key") + api_key = key.client_id <> "/" <> key.client_secret + # Set the AUTH_API_KEY to a valid value that is in the DB: + System.put_env("AUTH_API_KEY", api_key) + IO.inspect(System.get_env("AUTH_API_KEY"), label: "AUTH_API_KEY") + key end end Auth.Seeds.create_admin() +|> Auth.Seeds.create_apikey_for_admin() From 144a3cfc472b6cee4ea7b6c96ff8a1faa6a0cf0c Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 28 Apr 2020 18:23:13 +0100 Subject: [PATCH 106/189] use client_id to sign JWT https://github.com/dwyl/auth/issues/55 --- .env_sample | 1 + lib/auth/email.ex | 5 ++++- lib/auth/person.ex | 10 ++++----- lib/auth_web/controllers/apikey_controller.ex | 16 ++++++++------ lib/auth_web/controllers/auth_controller.ex | 15 +++++++------ mix.exs | 2 +- mix.lock | 2 +- test/auth/apikey_test.exs | 4 ++-- .../controllers/auth_controller_test.exs | 4 ++-- .../controllers/page_controller_test.exs | 22 ++++++++++--------- 10 files changed, 45 insertions(+), 36 deletions(-) diff --git a/.env_sample b/.env_sample index 8eccd2ae..ae68a1bf 100644 --- a/.env_sample +++ b/.env_sample @@ -1,4 +1,5 @@ export ADMIN_EMAIL=youremail@gmail.com +export AUTH_API_KEY=2PzB7PPnpuLsbWmWtXpGyI+kfSQSQ1zUW2Atz/+8PdZuSEJzHgzGnJWV35nTKRwx export EMAIL_APP_URL=https://dwylmail.herokuapp.com export GITHUB_CLIENT_ID=CreateGitHubApp export GITHUB_CLIENT_SECRET=SuperSecret diff --git a/lib/auth/email.ex b/lib/auth/email.ex index 40f5c19a..eefc739e 100644 --- a/lib/auth/email.ex +++ b/lib/auth/email.ex @@ -22,8 +22,11 @@ defmodule Auth.Email do } """ def sendemail(params) do + # IO.inspect(params, label: "sendemail/1 params:") url = System.get_env("EMAIL_APP_URL") <> "/api/send" - jwt = AuthPlug.Token.generate_jwt!(params) + # until further notice use the SECRET_KEY_BASE for sending email: + secret = System.get_env("SECRET_KEY_BASE") + jwt = AuthPlug.Token.generate_jwt!(params, secret) headers = [Authorization: "#{jwt}"] options = [ssl: [{:versions, [:"tlsv1.2"]}], timeout: 50_000, recv_timeout: 50_000] # github.com/dwyl/auth/issues/48 diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 6a1933c2..e2bd5de8 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -48,12 +48,12 @@ defmodule Auth.Person do end def create_person(person) do - IO.inspect(person, label: "create_person:51") + # IO.inspect(person, label: "create_person:51") person = %Person{} |> changeset(person) |> put_email_status_verified() - |> IO.inspect(label: "after put_email_status_verified") + # |> IO.inspect(label: "after put_email_status_verified") case get_person_by_email(person.changes.email) do nil -> @@ -137,13 +137,13 @@ defmodule Auth.Person do givenName: profile.given_name, auth_provider: "google" }) - |> IO.inspect(label: "merged") + # |> IO.inspect(label: "merged") end def create_google_person(profile) do transform_google_profile_data_to_person(profile) |> create_person() - |> IO.inspect(label: "create_person:") + # |> IO.inspect(label: "create_person:") end # @doc """ @@ -162,7 +162,7 @@ defmodule Auth.Person do defp put_email_hash(changeset) do put_change(changeset, :email_hash, changeset.changes.email) - |> IO.inspect(label: "changeset with :email_hash") + # |> IO.inspect(label: "changeset with :email_hash") end def put_email_status_verified(changeset) do diff --git a/lib/auth_web/controllers/apikey_controller.ex b/lib/auth_web/controllers/apikey_controller.ex index 183c5eb5..df74bfc1 100644 --- a/lib/auth_web/controllers/apikey_controller.ex +++ b/lib/auth_web/controllers/apikey_controller.ex @@ -29,17 +29,19 @@ defmodule AuthWeb.ApikeyController do key |> String.split("/") |> List.first() |> decode_decrypt() end - def create(conn, %{"apikey" => apikey_params}) do - # IO.inspect(apikey_params, label: "apikey_params") - person_id = conn.assigns.decoded.id - - params = Map.merge(apikey_params, %{ + def make_apikey(apikey_params, person_id) do + Map.merge(apikey_params, %{ "client_secret" => encrypt_encode(person_id), "client_id" => encrypt_encode(person_id), "person_id" => person_id - }) + }) + end - {:ok, apikey} = Apikey.create_apikey(params) + def create(conn, %{"apikey" => apikey_params}) do + {:ok, apikey} = + apikey_params + |> make_apikey(conn.assigns.decoded.id) + |> Apikey.create_apikey() conn |> put_flash(:info, "Apikey created successfully.") diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 8805b148..320f452e 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -41,14 +41,14 @@ defmodule AuthWeb.AuthController do if the state is defined, redirect to it. """ def handler(conn, person, state) do - # IO.inspect(person, label: "handler/3 > person") + IO.inspect(person, label: "handler/3 > person") # Send welcome email: - Auth.Email.sendemail(%{ - email: person.email, - name: person.givenName, - template: "welcome" - }) - |> IO.inspect(label: "email") + # Auth.Email.sendemail(%{ + # email: person.email, + # name: person.givenName, + # template: "welcome" + # }) + # |> IO.inspect(label: "email") IO.inspect(state, label: "state handler/3:53") @@ -61,6 +61,7 @@ defmodule AuthWeb.AuthController do false -> # display welcome page conn |> put_view(AuthWeb.PageView) + # |> AuthPlug.create_jwt_session(person) |> render(:welcome, person: person) end end diff --git a/mix.exs b/mix.exs index 71d1b6a3..06c90dee 100644 --- a/mix.exs +++ b/mix.exs @@ -61,7 +61,7 @@ defmodule Auth.Mixfile do # https://github.com/dwyl/elixir-auth-google {:elixir_auth_google, "~> 1.2.0"}, # https://github.com/dwyl/auth_plug - {:auth_plug, "~> 0.12.0"}, + {:auth_plug, "~> 0.14.0"}, # Field Validation and Encryption: github.com/dwyl/fields {:fields, "~> 2.4.0"}, diff --git a/mix.lock b/mix.lock index f4c58a06..9e655a03 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ "argon2_elixir": {:hex, :argon2_elixir, "2.3.0", "e251bdafd69308e8c1263e111600e6d68bd44f23d2cccbe43fcb1a417a76bc8e", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "28ccb63bff213aecec1f7f3dde9648418b031f822499973281d8f494b9d5a3b3"}, - "auth_plug": {:hex, :auth_plug, "0.12.0", "8cbd8eebfd4760a01137c0be5c4585cf43d4a7f4bdba418fce0c968b152f5eb2", [:mix], [{:joken, "~> 2.2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "91a9a607359149045aa8c42e6d57a56763ea1ca86d2a5f14ca7d33524bf1e6e7"}, + "auth_plug": {:hex, :auth_plug, "0.14.0", "2067a1d9c688ed491c25dbb20ce91d48568b1fe98571a41a59665743148da27a", [:mix], [{:joken, "~> 2.2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "50dd20e44621a5a0f6d3a6b5bd21ff526b8ea49a35bc1d88098c25ef0c951d37"}, "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"}, "comeonin": {:hex, :comeonin, "5.3.1", "7fe612b739c78c9c1a75186ef2d322ce4d25032d119823269d0aa1e2f1e20025", [:mix], [], "hexpm", "d6222483060c17f0977fad1b7401ef0c5863c985a64352755f366aee3799c245"}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, diff --git a/test/auth/apikey_test.exs b/test/auth/apikey_test.exs index d156359a..8439abf0 100644 --- a/test/auth/apikey_test.exs +++ b/test/auth/apikey_test.exs @@ -10,7 +10,7 @@ defmodule Auth.ApikeyTest do keys = Auth.Apikey.list_apikeys_for_person(person.id) # IO.inspect(keys, label: "keys") - assert keys == [] + assert length(keys) == 1 # Insert Two API keys: params = %{ @@ -27,7 +27,7 @@ defmodule Auth.ApikeyTest do }) |> Auth.Apikey.create_apikey() keys = Auth.Apikey.list_apikeys_for_person(person.id) - assert length(keys) == 2 + assert length(keys) == 3 end diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index a5a63dd0..3d84fcfa 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -33,11 +33,11 @@ defmodule AuthWeb.AuthControllerTest do auth_provider: "google" } person = Auth.Person.create_person(data) # |> IO.inspect(label: "person") - conn = AuthPlug.create_jwt_session(conn, Map.merge(data, %{id: person.id})) + conn = AuthPlug.create_jwt_session(conn, Map.merge(data, person)) conn = get(conn, "/auth/google/callback", %{code: "234", state: nil}) - assert html_response(conn, 200) =~ "google account" + # assert html_response(conn, 200) =~ "google account" # assert html_response(conn, 302) =~ "redirected" end end diff --git a/test/auth_web/controllers/page_controller_test.exs b/test/auth_web/controllers/page_controller_test.exs index 912611fd..8c38cbb1 100644 --- a/test/auth_web/controllers/page_controller_test.exs +++ b/test/auth_web/controllers/page_controller_test.exs @@ -28,16 +28,18 @@ defmodule AuthWeb.PageControllerTest do end test "google_handler/2 show welcome (state=nil) > handler/3", %{conn: conn} do - data = %{ - email: "nelson@gmail.com", - givenName: "McTestin", - picture: "https://youtu.be/naoknj1ebqI", - auth_provider: "google" - } - person = Auth.Person.create_person(data) # |> IO.inspect(label: "person") - conn = AuthPlug.create_jwt_session(conn, Map.merge(data, %{id: person.id})) - conn = get(conn, "/auth/google/callback", - %{code: "234", state: nil}) + # IO.inspect(System.get_env("AUTH_API_KEY"), label: "AUTH_API_KEY") + IO.inspect(AuthPlug.Token.client_id(), label: "AuthPlug.Token.client_id()") + # data = %{ + # email: "nelson@gmail.com", + # givenName: "McTestin", + # picture: "https://youtu.be/naoknj1ebqI", + # auth_provider: "google" + # } + # person = Auth.Person.create_person(data) |> IO.inspect(label: "person") + # conn = AuthPlug.create_jwt_session(conn, Map.merge(data, %{id: person.id})) + # |> IO.inspect(label: "conn") + conn = get(conn, "/auth/google/callback", %{code: "234", state: nil}) assert html_response(conn, 200) =~ "google account" # assert html_response(conn, 302) =~ "redirected" From 6fdec95cf10562860c82e834323d7587202cc0f7 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 28 Apr 2020 19:07:54 +0100 Subject: [PATCH 107/189] update fields to v2.5.0 for https://github.com/dwyl/fields/issues/71 --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 06c90dee..62392a68 100644 --- a/mix.exs +++ b/mix.exs @@ -64,7 +64,7 @@ defmodule Auth.Mixfile do {:auth_plug, "~> 0.14.0"}, # Field Validation and Encryption: github.com/dwyl/fields - {:fields, "~> 2.4.0"}, + {:fields, "~> 2.5.0"}, {:exbase58, "~> 1.0.2"}, # pending: github.com/dwyl/base58/pull/17 # Check test coverage diff --git a/mix.lock b/mix.lock index 9e655a03..301168e2 100644 --- a/mix.lock +++ b/mix.lock @@ -17,7 +17,7 @@ "ex_doc": {:hex, :ex_doc, "0.21.3", "857ec876b35a587c5d9148a2512e952e24c24345552259464b98bfbb883c7b42", [:mix], [{:earmark, "~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0db1ee8d1547ab4877c5b5dffc6604ef9454e189928d5ba8967d4a58a801f161"}, "exbase58": {:hex, :exbase58, "1.0.2", "2caa5df4d769b5c555cde11b85e93199037ed8b41f1da23e812619c10e3a3424", [:mix], [], "hexpm", "fe6b6b465750bdc1bd01c7b33b265902dabd63061f7db24e663509b45b4bba3c"}, "excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"}, - "fields": {:hex, :fields, "2.4.0", "af1f26a56d2462888c9838de1d7a6afc6deaf624b4aead8901c388d2d2ddfbfc", [:mix], [{:argon2_elixir, "~> 2.3.0", [hex: :argon2_elixir, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:html_sanitize_ex, "~> 1.4", [hex: :html_sanitize_ex, repo: "hexpm", optional: false]}], "hexpm", "48bf472db9d5c221b635a36d01a2a832a933a9d5ad815fab5f0cb6e63a85bca8"}, + "fields": {:hex, :fields, "2.5.0", "b42c84f7dd35d175bbb03c9cdad9c188d73176b996a3f79f245429f82f10b4c5", [:mix], [{:argon2_elixir, "~> 2.3.0", [hex: :argon2_elixir, repo: "hexpm", optional: false]}, {:ecto, "~> 3.4.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:html_sanitize_ex, "~> 1.4", [hex: :html_sanitize_ex, repo: "hexpm", optional: false]}], "hexpm", "7b090e38dcb9fca100ff9094c03b88afaf0719672d9c5813ddfc814c971edfde"}, "file_system": {:hex, :file_system, "0.2.8", "f632bd287927a1eed2b718f22af727c5aeaccc9a98d8c2bd7bff709e851dc986", [:mix], [], "hexpm", "97a3b6f8d63ef53bd0113070102db2ce05352ecf0d25390eb8d747c2bde98bca"}, "gettext": {:hex, :gettext, "0.17.4", "f13088e1ec10ce01665cf25f5ff779e7df3f2dc71b37084976cf89d1aa124d5c", [:mix], [], "hexpm", "3c75b5ea8288e2ee7ea503ff9e30dfe4d07ad3c054576a6e60040e79a801e14d"}, "hackney": {:hex, :hackney, "1.15.2", "07e33c794f8f8964ee86cebec1a8ed88db5070e52e904b8f12209773c1036085", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.5", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "e0100f8ef7d1124222c11ad362c857d3df7cb5f4204054f9f0f4a728666591fc"}, From c9a6860a7f5851527123674a3bc81fdae755ab6a Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 28 Apr 2020 19:51:12 +0100 Subject: [PATCH 108/189] Map.delete(person, :email_hash) before attempting JWT session https://github.com/dwyl/auth/issues/56 --- lib/auth_web/controllers/auth_controller.ex | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 320f452e..43be8c02 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -59,9 +59,10 @@ defmodule AuthWeb.AuthController do |> redirect(external: add_jwt_url_param(person, state)) false -> # display welcome page + person = Map.delete(person, :email_hash) # Jason chokes on binary data! conn |> put_view(AuthWeb.PageView) - # |> AuthPlug.create_jwt_session(person) + |> AuthPlug.create_jwt_session(person) |> render(:welcome, person: person) end end From c4d55de1c5850ac5e46fbdfa3dd04132048e3c9b Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 28 Apr 2020 19:52:12 +0100 Subject: [PATCH 109/189] use auth_plug v0.15 with AuthPlug.Helpers.strip_struct_metadata/1 #56 --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 62392a68..a7f39dff 100644 --- a/mix.exs +++ b/mix.exs @@ -61,7 +61,7 @@ defmodule Auth.Mixfile do # https://github.com/dwyl/elixir-auth-google {:elixir_auth_google, "~> 1.2.0"}, # https://github.com/dwyl/auth_plug - {:auth_plug, "~> 0.14.0"}, + {:auth_plug, "~> 0.15.0"}, # Field Validation and Encryption: github.com/dwyl/fields {:fields, "~> 2.5.0"}, diff --git a/mix.lock b/mix.lock index 301168e2..23c15bd4 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ "argon2_elixir": {:hex, :argon2_elixir, "2.3.0", "e251bdafd69308e8c1263e111600e6d68bd44f23d2cccbe43fcb1a417a76bc8e", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "28ccb63bff213aecec1f7f3dde9648418b031f822499973281d8f494b9d5a3b3"}, - "auth_plug": {:hex, :auth_plug, "0.14.0", "2067a1d9c688ed491c25dbb20ce91d48568b1fe98571a41a59665743148da27a", [:mix], [{:joken, "~> 2.2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "50dd20e44621a5a0f6d3a6b5bd21ff526b8ea49a35bc1d88098c25ef0c951d37"}, + "auth_plug": {:hex, :auth_plug, "0.15.0", "7e269ad5e09fb2ffead6f4cbe8ee69ff01bc475c7750c48097368ed1dfc5df6f", [:mix], [{:joken, "~> 2.2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e1b82b2ecded8198bc99d0c6a13ba48996b1e832579a12397e866459eba678b2"}, "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"}, "comeonin": {:hex, :comeonin, "5.3.1", "7fe612b739c78c9c1a75186ef2d322ce4d25032d119823269d0aa1e2f1e20025", [:mix], [], "hexpm", "d6222483060c17f0977fad1b7401ef0c5863c985a64352755f366aee3799c245"}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, From dd8231e8b16299b1d3f92d429979e33b9b9b88f5 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 28 Apr 2020 20:10:37 +0100 Subject: [PATCH 110/189] tidy up tests! --- lib/auth/person.ex | 2 +- lib/auth_web/controllers/auth_controller.ex | 19 +++++++++---------- lib/auth_web/controllers/page_controller.ex | 8 ++++---- mix.exs | 2 +- mix.lock | 2 +- priv/repo/seeds.exs | 1 + .../controllers/apikey_controller_test.exs | 4 ++-- .../controllers/auth_controller_test.exs | 2 +- .../controllers/page_controller_test.exs | 2 +- 9 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/auth/person.ex b/lib/auth/person.ex index e2bd5de8..b61d61ab 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -131,7 +131,7 @@ defmodule Auth.Person do } """ def transform_google_profile_data_to_person(profile) do - IO.inspect(profile, label: "profile") + # IO.inspect(profile, label: "profile") Map.merge(profile, %{ familyName: profile.family_name, givenName: profile.given_name, diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 43be8c02..f91e998b 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -7,10 +7,10 @@ defmodule AuthWeb.AuthController do """ def github_handler(conn, %{"code" => code, "state" => state}) do {:ok, profile} = ElixirAuthGithub.github_auth(code) - IO.inspect(profile, label: "github profile") + # IO.inspect(profile, label: "github profile") # save profile to people: person = Person.create_github_person(profile) - IO.inspect(person, label: "github profile > person") + # IO.inspect(person, label: "github profile > person") # render or redirect: handler(conn, person, state) end @@ -41,16 +41,16 @@ defmodule AuthWeb.AuthController do if the state is defined, redirect to it. """ def handler(conn, person, state) do - IO.inspect(person, label: "handler/3 > person") + # IO.inspect(person, label: "handler/3 > person") # Send welcome email: - # Auth.Email.sendemail(%{ - # email: person.email, - # name: person.givenName, - # template: "welcome" - # }) + Auth.Email.sendemail(%{ + email: person.email, + name: person.givenName, + template: "welcome" + }) # |> IO.inspect(label: "email") - IO.inspect(state, label: "state handler/3:53") + # IO.inspect(state, label: "state handler/3:53") # check if valid state (HTTP referer) is defined: case not is_nil(state) do @@ -59,7 +59,6 @@ defmodule AuthWeb.AuthController do |> redirect(external: add_jwt_url_param(person, state)) false -> # display welcome page - person = Map.delete(person, :email_hash) # Jason chokes on binary data! conn |> put_view(AuthWeb.PageView) |> AuthPlug.create_jwt_session(person) diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index 96eb8582..938a097c 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -24,20 +24,20 @@ defmodule AuthWeb.PageController do case List.keyfind(conn.req_headers, "referer", 0) do {"referer", referer} -> referer - |> IO.inspect(label: "req_headers referer") + # |> IO.inspect(label: "req_headers referer") nil -> # referer not in headers, check URL query: case conn.query_string =~ "referer" do true -> query = URI.decode_query(conn.query_string) Map.get(query, "referer") - |> IO.inspect(label: "url referer") + # |> IO.inspect(label: "url referer") false -> # no referer, redirect back to this app. - IO.inspect("false: no referer") + # IO.inspect("false: no referer") AuthPlug.Helpers.get_baseurl_from_conn(conn) <> "/profile" end end - |> URI.encode |> IO.inspect(label: "referer") + |> URI.encode # |> IO.inspect(label: "referer") end end diff --git a/mix.exs b/mix.exs index a7f39dff..f6ec38de 100644 --- a/mix.exs +++ b/mix.exs @@ -61,7 +61,7 @@ defmodule Auth.Mixfile do # https://github.com/dwyl/elixir-auth-google {:elixir_auth_google, "~> 1.2.0"}, # https://github.com/dwyl/auth_plug - {:auth_plug, "~> 0.15.0"}, + {:auth_plug, "~> 0.16.0"}, # Field Validation and Encryption: github.com/dwyl/fields {:fields, "~> 2.5.0"}, diff --git a/mix.lock b/mix.lock index 23c15bd4..cf113532 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ "argon2_elixir": {:hex, :argon2_elixir, "2.3.0", "e251bdafd69308e8c1263e111600e6d68bd44f23d2cccbe43fcb1a417a76bc8e", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "28ccb63bff213aecec1f7f3dde9648418b031f822499973281d8f494b9d5a3b3"}, - "auth_plug": {:hex, :auth_plug, "0.15.0", "7e269ad5e09fb2ffead6f4cbe8ee69ff01bc475c7750c48097368ed1dfc5df6f", [:mix], [{:joken, "~> 2.2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e1b82b2ecded8198bc99d0c6a13ba48996b1e832579a12397e866459eba678b2"}, + "auth_plug": {:hex, :auth_plug, "0.16.0", "a4af82b1ba74c15a7fb9d3bd60320389e2dd30a4eb5f2008ea8c767bcadb88c2", [:mix], [{:joken, "~> 2.2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "38a520a798647b79bb5ae160d353bb0b74437ef2da276cc88d9a602993e06da7"}, "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"}, "comeonin": {:hex, :comeonin, "5.3.1", "7fe612b739c78c9c1a75186ef2d322ce4d25032d119823269d0aa1e2f1e20025", [:mix], [], "hexpm", "d6222483060c17f0977fad1b7401ef0c5863c985a64352755f366aee3799c245"}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index 6699a59a..5a016362 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -47,6 +47,7 @@ defmodule Auth.Seeds do # Set the AUTH_API_KEY to a valid value that is in the DB: System.put_env("AUTH_API_KEY", api_key) IO.inspect(System.get_env("AUTH_API_KEY"), label: "AUTH_API_KEY") + IO.puts("- - - - - - - - - - - - - - - - - - - - - - ") key end end diff --git a/test/auth_web/controllers/apikey_controller_test.exs b/test/auth_web/controllers/apikey_controller_test.exs index aa9e6349..c6726e68 100644 --- a/test/auth_web/controllers/apikey_controller_test.exs +++ b/test/auth_web/controllers/apikey_controller_test.exs @@ -38,7 +38,7 @@ defmodule AuthWeb.ApikeyControllerTest do test "decrypt_api_key/1 decrypts a DWYL_API_KEY" do person_id = 1234 - key = create_api_key(person_id) |> IO.inspect() + key = create_api_key(person_id) # |> IO.inspect() # IO.inspect(String.length(key), label: "String.length(key)") decrypted = decrypt_api_key(key) # |> IO.inspect() assert decrypted == person_id @@ -75,7 +75,7 @@ defmodule AuthWeb.ApikeyControllerTest do test "renders form", %{conn: conn} do person = Auth.Person.get_person_by_email(@email) conn = AuthPlug.create_jwt_session(conn, %{email: @email, id: person.id}) - + conn = get(conn, Routes.apikey_path(conn, :new)) assert html_response(conn, 200) =~ "New Apikey" end diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 3d84fcfa..adf0d8a2 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -37,7 +37,7 @@ defmodule AuthWeb.AuthControllerTest do conn = get(conn, "/auth/google/callback", %{code: "234", state: nil}) - # assert html_response(conn, 200) =~ "google account" + assert html_response(conn, 200) =~ "google account" # assert html_response(conn, 302) =~ "redirected" end end diff --git a/test/auth_web/controllers/page_controller_test.exs b/test/auth_web/controllers/page_controller_test.exs index 8c38cbb1..0e36fa21 100644 --- a/test/auth_web/controllers/page_controller_test.exs +++ b/test/auth_web/controllers/page_controller_test.exs @@ -29,7 +29,7 @@ defmodule AuthWeb.PageControllerTest do test "google_handler/2 show welcome (state=nil) > handler/3", %{conn: conn} do # IO.inspect(System.get_env("AUTH_API_KEY"), label: "AUTH_API_KEY") - IO.inspect(AuthPlug.Token.client_id(), label: "AuthPlug.Token.client_id()") + # IO.inspect(AuthPlug.Token.client_id(), label: "AuthPlug.Token.client_id()") # data = %{ # email: "nelson@gmail.com", # givenName: "McTestin", From 3cd0d63ae50754a4cc57138dfd7af7ef568d8e93 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 28 Apr 2020 22:43:13 +0100 Subject: [PATCH 111/189] extract client_id from query params https://github.com/dwyl/auth/issues/55#issuecomment-620871143 --- lib/auth_web/controllers/page_controller.ex | 11 ++++++++--- test/auth_web/controllers/auth_controller_test.exs | 4 +++- test/auth_web/controllers/page_controller_test.exs | 4 +++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index 938a097c..b9ca88a0 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -19,6 +19,10 @@ defmodule AuthWeb.PageController do |> render(:welcome) end + def append_client_id(ref, client_id) do + ref <> "?client_id=" <> client_id + end + def get_referer(conn) do # https://stackoverflow.com/questions/37176911/get-http-referrer case List.keyfind(conn.req_headers, "referer", 0) do @@ -30,14 +34,15 @@ defmodule AuthWeb.PageController do case conn.query_string =~ "referer" do true -> query = URI.decode_query(conn.query_string) - Map.get(query, "referer") - # |> IO.inspect(label: "url referer") + ref = Map.get(query, "referer") + client_id = Map.get(query, "client_id") + ref |> append_client_id(client_id) false -> # no referer, redirect back to this app. # IO.inspect("false: no referer") AuthPlug.Helpers.get_baseurl_from_conn(conn) <> "/profile" end end - |> URI.encode # |> IO.inspect(label: "referer") + |> URI.encode |> IO.inspect(label: "referer") end end diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index adf0d8a2..33bfc81c 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -19,7 +19,9 @@ defmodule AuthWeb.AuthControllerTest do test "google_handler/2 show welcome page", %{conn: conn} do conn = get(conn, "/auth/google/callback", - %{code: "234", state: AuthPlug.Helpers.get_baseurl_from_conn(conn)}) + %{code: "234", + state: AuthPlug.Helpers.get_baseurl_from_conn(conn) <> + "&client_id=" <> AuthPlug.Token.client_id() }) # assert html_response(conn, 200) =~ "nelson@gmail.com" assert html_response(conn, 302) =~ "redirected" diff --git a/test/auth_web/controllers/page_controller_test.exs b/test/auth_web/controllers/page_controller_test.exs index 0e36fa21..1cd011ea 100644 --- a/test/auth_web/controllers/page_controller_test.exs +++ b/test/auth_web/controllers/page_controller_test.exs @@ -22,7 +22,9 @@ defmodule AuthWeb.PageControllerTest do test "get_referer/1 query_string", %{conn: conn} do conn = conn - |> get("/?referer=" <> URI.encode("http://localhost/admin")) + |> get("/?referer=" <> URI.encode("http://localhost/admin") + <> "&client_id=" <> AuthPlug.Token.client_id() + ) assert conn.resp_body =~ "state=http://localhost/admin" end From fa73f370c93a92373f0fe4d4076d97172797e337 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 28 Apr 2020 23:14:24 +0100 Subject: [PATCH 112/189] extract client_id from state (returned by successful auth) and lookup in apikeys #55 --- lib/auth_web/controllers/auth_controller.ex | 28 ++++++++++++++++++++- lib/auth_web/controllers/page_controller.ex | 1 - 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index f91e998b..c49efae7 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -66,7 +66,33 @@ defmodule AuthWeb.AuthController do end end + + + + def add_jwt_url_param(person, state) do + + IO.inspect(state, label: "state") + query = URI.decode_query(state) + # IO.inspect(query, label: "query") + client_id = Map.get(query, "client_id") + IO.inspect(client_id, label: "client_id") + client_secret = case not is_nil(client_id) do + true -> + # Lookup client_id in apikeys table + person_id = AuthWeb.ApikeyController.decode_decrypt(client_id) + IO.inspect(person_id, label: "person_id") + apikeys = Auth.Apikey.list_apikeys_for_person(person_id) + IO.inspect(apikeys) + Enum.filter(apikeys, fn(k) -> + k.client_id == client_id # and state =~ k.url + end) |> List.first() |> Map.get(:client_secret) + + false -> + # use client_id: + AuthPlug.Token.client_secret() + end + data = %{ auth_provider: person.auth_provider, givenName: person.givenName, @@ -75,7 +101,7 @@ defmodule AuthWeb.AuthController do status: person.status } - jwt = AuthPlug.Token.generate_jwt!(data) + jwt = AuthPlug.Token.generate_jwt!(data, client_secret) URI.decode(state) <> "?jwt=" <> jwt end end diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index b9ca88a0..3b9af187 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -28,7 +28,6 @@ defmodule AuthWeb.PageController do case List.keyfind(conn.req_headers, "referer", 0) do {"referer", referer} -> referer - # |> IO.inspect(label: "req_headers referer") nil -> # referer not in headers, check URL query: case conn.query_string =~ "referer" do From 0a181ccf2d14ed574546151cb2a5bed53e22694d Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 11:03:01 +0100 Subject: [PATCH 113/189] create get_client_secret_from_state/1 function for #55 --- lib/auth_web/controllers/apikey_controller.ex | 12 +++- lib/auth_web/controllers/auth_controller.ex | 61 ++++++++++++------- .../controllers/auth_controller_test.exs | 18 +++++- 3 files changed, 67 insertions(+), 24 deletions(-) diff --git a/lib/auth_web/controllers/apikey_controller.ex b/lib/auth_web/controllers/apikey_controller.ex index df74bfc1..44b85292 100644 --- a/lib/auth_web/controllers/apikey_controller.ex +++ b/lib/auth_web/controllers/apikey_controller.ex @@ -21,8 +21,18 @@ defmodule AuthWeb.ApikeyController do encrypt_encode(person_id) <> "/" <> encrypt_encode(person_id) end + @doc""" + `decode_decrypt/1` accepts a `key` and attempts to Base58.decode + followed by AES.decrypt it. If decode or decrypt fails, return 0 (zero). + """ def decode_decrypt(key) do - key |> Base58.decode |> Fields.AES.decrypt() |> String.to_integer() + try do + key |> Base58.decode |> Fields.AES.decrypt() |> String.to_integer() + rescue + ArgumentError -> + # IO.puts("AES.decrypt() unable to decrypt client_id") + 0 + end end def decrypt_api_key(key) do diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index c49efae7..53954b84 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -48,15 +48,22 @@ defmodule AuthWeb.AuthController do name: person.givenName, template: "welcome" }) - # |> IO.inspect(label: "email") + |> IO.inspect(label: "email") # IO.inspect(state, label: "state handler/3:53") # check if valid state (HTTP referer) is defined: case not is_nil(state) do true -> # redirect - conn - |> redirect(external: add_jwt_url_param(person, state)) + case get_client_secret_from_state(state) do + 0 -> + IO.inspect("client_secret is 0 (error)") + unauthorized(conn) + secret -> + IO.inspect(secret, label: "secret") + conn + |> redirect(external: add_jwt_url_param(person, state, secret)) + end false -> # display welcome page conn @@ -66,32 +73,44 @@ defmodule AuthWeb.AuthController do end end + defp unauthorized(conn) do + conn + |> put_resp_header("www-authenticate", "Bearer realm=\"Person access\"") + |> send_resp(401, "invalid client_id") + |> halt() + end - - - - def add_jwt_url_param(person, state) do - - IO.inspect(state, label: "state") + def get_client_secret_from_state(state) do query = URI.decode_query(state) # IO.inspect(query, label: "query") client_id = Map.get(query, "client_id") IO.inspect(client_id, label: "client_id") - client_secret = case not is_nil(client_id) do - true -> - # Lookup client_id in apikeys table + case not is_nil(client_id) do + true -> # Lookup client_id in apikeys table person_id = AuthWeb.ApikeyController.decode_decrypt(client_id) IO.inspect(person_id, label: "person_id") - apikeys = Auth.Apikey.list_apikeys_for_person(person_id) - IO.inspect(apikeys) - Enum.filter(apikeys, fn(k) -> - k.client_id == client_id # and state =~ k.url - end) |> List.first() |> Map.get(:client_secret) - - false -> - # use client_id: - AuthPlug.Token.client_secret() + if person_id == 0 do # decode_decrypt fails with state 0 + # IO.inspect(person_id, label: "person_id:88") + 0 + else + apikeys = Auth.Apikey.list_apikeys_for_person(person_id) + # IO.inspect(apikeys) + Enum.filter(apikeys, fn(k) -> + k.client_id == client_id # and state =~ k.url + end) |> List.first() |> Map.get(:client_secret) + # check for URL match! + end + + false -> # state without client_id is not valid + 0 end + end + + + + def add_jwt_url_param(person, state, client_secret) do + + IO.inspect(state, label: "state") data = %{ auth_provider: person.auth_provider, diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 33bfc81c..8e6a2dd1 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -4,14 +4,16 @@ defmodule AuthWeb.AuthControllerTest do test "github_handler/2 github auth callback", %{conn: conn} do conn = get(conn, "/auth/github/callback", - %{code: "123", state: "http://localhost/"}) + %{code: "123", state: "http://localhost/" <> + "&client_id=" <> AuthPlug.Token.client_id() }) # assert html_response(conn, 200) =~ "test@gmail.com" assert html_response(conn, 302) =~ "http://localhost" end test "google_handler/2 for google auth callback", %{conn: conn} do conn = get(conn, "/auth/google/callback", - %{code: "234", state: "http://localhost/"}) + %{code: "234", state: "http://localhost/" <> + "&client_id=" <> AuthPlug.Token.client_id() }) # assert html_response(conn, 200) =~ "nelson@gmail.com" assert html_response(conn, 302) =~ "http://localhost" @@ -42,4 +44,16 @@ defmodule AuthWeb.AuthControllerTest do assert html_response(conn, 200) =~ "google account" # assert html_response(conn, 302) =~ "redirected" end + + test "decode_decrypt/1 with invalid client_id" do + valid_key = AuthWeb.ApikeyController.encrypt_encode(1) + person_id = AuthWeb.ApikeyController.decode_decrypt(valid_key) + assert person_id == 1 + + invalid_key = String.slice(valid_key, 0..-2) + error = AuthWeb.ApikeyController.decode_decrypt(invalid_key) + assert error == 0 + end + + end From d379fb6d4494cbf11f87c49416487e523c57c683 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 11:14:57 +0100 Subject: [PATCH 114/189] add client_id to state in all auth_controller tests #55 --- lib/auth_web/controllers/auth_controller.ex | 11 +++++++++-- test/auth_web/controllers/auth_controller_test.exs | 13 ++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 53954b84..70e45cdd 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -48,7 +48,7 @@ defmodule AuthWeb.AuthController do name: person.givenName, template: "welcome" }) - |> IO.inspect(label: "email") + # |> IO.inspect(label: "email") # IO.inspect(state, label: "state handler/3:53") @@ -80,6 +80,13 @@ defmodule AuthWeb.AuthController do |> halt() end + + @doc """ + `get_client_secret_from_state/1` gets the client_id from state, + attempts to decode_decrypt it and then look it up in apikeys + if it finds the corresponding client_secret it returns the client_secret. + All other failure conditions return a 0 (zero) which results in a 401. + """ def get_client_secret_from_state(state) do query = URI.decode_query(state) # IO.inspect(query, label: "query") @@ -96,7 +103,7 @@ defmodule AuthWeb.AuthController do apikeys = Auth.Apikey.list_apikeys_for_person(person_id) # IO.inspect(apikeys) Enum.filter(apikeys, fn(k) -> - k.client_id == client_id # and state =~ k.url + k.client_id == client_id and state =~ k.url end) |> List.first() |> Map.get(:client_secret) # check for URL match! end diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 8e6a2dd1..793595c9 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -4,7 +4,7 @@ defmodule AuthWeb.AuthControllerTest do test "github_handler/2 github auth callback", %{conn: conn} do conn = get(conn, "/auth/github/callback", - %{code: "123", state: "http://localhost/" <> + %{code: "123", state: "http://localhost:4000/" <> "&client_id=" <> AuthPlug.Token.client_id() }) # assert html_response(conn, 200) =~ "test@gmail.com" assert html_response(conn, 302) =~ "http://localhost" @@ -12,7 +12,7 @@ defmodule AuthWeb.AuthControllerTest do test "google_handler/2 for google auth callback", %{conn: conn} do conn = get(conn, "/auth/google/callback", - %{code: "234", state: "http://localhost/" <> + %{code: "234", state: "http://localhost:4000/" <> "&client_id=" <> AuthPlug.Token.client_id() }) # assert html_response(conn, 200) =~ "nelson@gmail.com" @@ -20,10 +20,17 @@ defmodule AuthWeb.AuthControllerTest do end test "google_handler/2 show welcome page", %{conn: conn} do + # IO.inspect(AuthPlug.Helpers.get_baseurl_from_conn(conn), label: "baseurl") + # Google Auth Mock makes the state https://www.example.com + # so we need to create a new API_KEY with that url: + {:ok, key} = %{"name" => "example key", "url" => "https://www.example.com"} + |> AuthWeb.ApikeyController.make_apikey(1) + |> Auth.Apikey.create_apikey() + conn = get(conn, "/auth/google/callback", %{code: "234", state: AuthPlug.Helpers.get_baseurl_from_conn(conn) <> - "&client_id=" <> AuthPlug.Token.client_id() }) + "&client_id=" <> key.client_id }) # assert html_response(conn, 200) =~ "nelson@gmail.com" assert html_response(conn, 302) =~ "redirected" From 254e7a5fdcb2d17b2898697965e54adf9d8134ab Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 11:22:51 +0100 Subject: [PATCH 115/189] add test for invalid client_id for #55 --- lib/auth_web/controllers/auth_controller.ex | 8 +++++--- .../controllers/apikey_controller_test.exs | 10 ++++++++++ .../auth_web/controllers/auth_controller_test.exs | 15 +++++++-------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 70e45cdd..874a5258 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -57,10 +57,10 @@ defmodule AuthWeb.AuthController do true -> # redirect case get_client_secret_from_state(state) do 0 -> - IO.inspect("client_secret is 0 (error)") + # IO.inspect("client_secret is 0 (error)") unauthorized(conn) secret -> - IO.inspect(secret, label: "secret") + # IO.inspect(secret, label: "secret") conn |> redirect(external: add_jwt_url_param(person, state, secret)) end @@ -74,8 +74,10 @@ defmodule AuthWeb.AuthController do end defp unauthorized(conn) do + # IO.inspect(conn) conn - |> put_resp_header("www-authenticate", "Bearer realm=\"Person access\"") + # |> put_resp_header("www-authenticate", "Bearer realm=\"Person access\"") + |> put_resp_content_type("text/html") |> send_resp(401, "invalid client_id") |> halt() end diff --git a/test/auth_web/controllers/apikey_controller_test.exs b/test/auth_web/controllers/apikey_controller_test.exs index c6726e68..c8030065 100644 --- a/test/auth_web/controllers/apikey_controller_test.exs +++ b/test/auth_web/controllers/apikey_controller_test.exs @@ -44,6 +44,16 @@ defmodule AuthWeb.ApikeyControllerTest do assert decrypted == person_id end + test "decode_decrypt/1 with invalid client_id" do + valid_key = AuthWeb.ApikeyController.encrypt_encode(1) + person_id = AuthWeb.ApikeyController.decode_decrypt(valid_key) + assert person_id == 1 + + invalid_key = String.slice(valid_key, 0..-2) + error = AuthWeb.ApikeyController.decode_decrypt(invalid_key) + assert error == 0 + end + property "Check a batch of int values can be decoded decode_decrypt/1" do check all(int <- integer()) do assert decode_decrypt(encrypt_encode(int)) == int diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 793595c9..7b1a16ff 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -52,14 +52,13 @@ defmodule AuthWeb.AuthControllerTest do # assert html_response(conn, 302) =~ "redirected" end - test "decode_decrypt/1 with invalid client_id" do - valid_key = AuthWeb.ApikeyController.encrypt_encode(1) - person_id = AuthWeb.ApikeyController.decode_decrypt(valid_key) - assert person_id == 1 - - invalid_key = String.slice(valid_key, 0..-2) - error = AuthWeb.ApikeyController.decode_decrypt(invalid_key) - assert error == 0 + test "google_handler/2 with invalid client_id", %{conn: conn} do + invalid_key = String.slice(AuthPlug.Token.client_id(), 0..-2) + conn = get(conn, "/auth/google/callback", + %{code: "234", state: "www.example.com" <> + "&client_id=" <> invalid_key }) + # assert html_response(conn, 200) =~ "google account" + assert html_response(conn, 401) =~ "invalid client_id" end From 178a6e217b97e852a9fcfc7c4dda5b98130ceb1f Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 12:32:05 +0100 Subject: [PATCH 116/189] fix typo in test --- lib/auth_web/controllers/auth_controller.ex | 4 ++-- test/auth_web/controllers/auth_controller_test.exs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 874a5258..0e1b4848 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -78,7 +78,7 @@ defmodule AuthWeb.AuthController do conn # |> put_resp_header("www-authenticate", "Bearer realm=\"Person access\"") |> put_resp_content_type("text/html") - |> send_resp(401, "invalid client_id") + |> send_resp(401, "invalid AUTH_API_KEY/client_id please check.") |> halt() end @@ -97,7 +97,7 @@ defmodule AuthWeb.AuthController do case not is_nil(client_id) do true -> # Lookup client_id in apikeys table person_id = AuthWeb.ApikeyController.decode_decrypt(client_id) - IO.inspect(person_id, label: "person_id") + # IO.inspect(person_id, label: "person_id") if person_id == 0 do # decode_decrypt fails with state 0 # IO.inspect(person_id, label: "person_id:88") 0 diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 7b1a16ff..5b5e9047 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -55,10 +55,10 @@ defmodule AuthWeb.AuthControllerTest do test "google_handler/2 with invalid client_id", %{conn: conn} do invalid_key = String.slice(AuthPlug.Token.client_id(), 0..-2) conn = get(conn, "/auth/google/callback", - %{code: "234", state: "www.example.com" <> + %{code: "234", state: "www.example.com/" <> "&client_id=" <> invalid_key }) # assert html_response(conn, 200) =~ "google account" - assert html_response(conn, 401) =~ "invalid client_id" + assert html_response(conn, 401) =~ "invalid" end From d87f2d79660c9ea89e74d804e2e99453a897525c Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 16:43:13 +0100 Subject: [PATCH 117/189] slight refactor into reuseable functions #42 --- lib/auth_web/controllers/auth_controller.ex | 30 ++++++++++++--------- lib/auth_web/controllers/page_controller.ex | 18 ++++++++++--- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 0e1b4848..d76a6236 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -96,25 +96,29 @@ defmodule AuthWeb.AuthController do IO.inspect(client_id, label: "client_id") case not is_nil(client_id) do true -> # Lookup client_id in apikeys table - person_id = AuthWeb.ApikeyController.decode_decrypt(client_id) - # IO.inspect(person_id, label: "person_id") - if person_id == 0 do # decode_decrypt fails with state 0 - # IO.inspect(person_id, label: "person_id:88") - 0 - else - apikeys = Auth.Apikey.list_apikeys_for_person(person_id) - # IO.inspect(apikeys) - Enum.filter(apikeys, fn(k) -> - k.client_id == client_id and state =~ k.url - end) |> List.first() |> Map.get(:client_secret) - # check for URL match! - end + get_client_secret(client_id, state) false -> # state without client_id is not valid 0 end end + def get_client_secret(client_id, state) do + person_id = AuthWeb.ApikeyController.decode_decrypt(client_id) + # IO.inspect(person_id, label: "person_id") + if person_id == 0 do # decode_decrypt fails with state 0 + # IO.inspect(person_id, label: "person_id:88") + 0 + else + apikeys = Auth.Apikey.list_apikeys_for_person(person_id) + # IO.inspect(apikeys) + Enum.filter(apikeys, fn(k) -> + k.client_id == client_id and state =~ k.url + end) |> List.first() |> Map.get(:client_secret) + + end + end + def add_jwt_url_param(person, state, client_secret) do diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index 3b9af187..b675e7e3 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -3,6 +3,7 @@ defmodule AuthWeb.PageController do def index(conn, _params) do state = get_referer(conn) + oauth_github_url = ElixirAuthGithub.login_url(%{scopes: ["user:email"], state: state}) oauth_google_url = ElixirAuthGoogle.generate_oauth_url(conn, state) @@ -34,14 +35,23 @@ defmodule AuthWeb.PageController do true -> query = URI.decode_query(conn.query_string) ref = Map.get(query, "referer") - client_id = Map.get(query, "client_id") + client_id = get_client_id_from_query(conn) ref |> append_client_id(client_id) - false -> # no referer, redirect back to this app. - # IO.inspect("false: no referer") - AuthPlug.Helpers.get_baseurl_from_conn(conn) <> "/profile" + false -> # no referer, redirect back to Auth app. + AuthPlug.Helpers.get_baseurl_from_conn(conn) + <> "/profile" <> AuthPlug.Token.client_id() end end |> URI.encode |> IO.inspect(label: "referer") end + + def get_client_id_from_query(conn) do + case conn.query_string =~ "client_id" do + true -> + Map.get(URI.decode_query(conn.query_string), "client_id") + false -> # no client_id, redirect back to this app. + 0 + end + end end From 9886ffc68fdc0fecb23497b5e8952364602f2f22 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 16:50:51 +0100 Subject: [PATCH 118/189] add client_id to state sent to 3rd party urls fixes #57 --- lib/auth_web/controllers/page_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index b675e7e3..2558fead 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -40,7 +40,7 @@ defmodule AuthWeb.PageController do false -> # no referer, redirect back to Auth app. AuthPlug.Helpers.get_baseurl_from_conn(conn) - <> "/profile" <> AuthPlug.Token.client_id() + <> "/profile?client_id" <> AuthPlug.Token.client_id() end end |> URI.encode |> IO.inspect(label: "referer") From 0f11599233989c3ae3a35eeaa2232cb205feb149 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 17:25:40 +0100 Subject: [PATCH 119/189] use auth_plug 1.0.0 with auth_client_id url prop https://github.com/dwyl/auth/issues/57 --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index f6ec38de..484d5ba5 100644 --- a/mix.exs +++ b/mix.exs @@ -61,7 +61,7 @@ defmodule Auth.Mixfile do # https://github.com/dwyl/elixir-auth-google {:elixir_auth_google, "~> 1.2.0"}, # https://github.com/dwyl/auth_plug - {:auth_plug, "~> 0.16.0"}, + {:auth_plug, "~> 1.0.0"}, # Field Validation and Encryption: github.com/dwyl/fields {:fields, "~> 2.5.0"}, diff --git a/mix.lock b/mix.lock index cf113532..1c6c897e 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ "argon2_elixir": {:hex, :argon2_elixir, "2.3.0", "e251bdafd69308e8c1263e111600e6d68bd44f23d2cccbe43fcb1a417a76bc8e", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "28ccb63bff213aecec1f7f3dde9648418b031f822499973281d8f494b9d5a3b3"}, - "auth_plug": {:hex, :auth_plug, "0.16.0", "a4af82b1ba74c15a7fb9d3bd60320389e2dd30a4eb5f2008ea8c767bcadb88c2", [:mix], [{:joken, "~> 2.2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "38a520a798647b79bb5ae160d353bb0b74437ef2da276cc88d9a602993e06da7"}, + "auth_plug": {:hex, :auth_plug, "1.0.0", "7e93fbc4a983b6af4e018fd48f966a3aa273f336850c99bb505e02872c02faae", [:mix], [{:joken, "~> 2.2.0", [hex: :joken, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9bdcdebaa30106c3a2f6cc363b6456dc3ab5cd51ca48dc93008778ac47975f0c"}, "certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm", "805abd97539caf89ec6d4732c91e62ba9da0cda51ac462380bbd28ee697a8c42"}, "comeonin": {:hex, :comeonin, "5.3.1", "7fe612b739c78c9c1a75186ef2d322ce4d25032d119823269d0aa1e2f1e20025", [:mix], [], "hexpm", "d6222483060c17f0977fad1b7401ef0c5863c985a64352755f366aee3799c245"}, "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm", "4a0850c9be22a43af9920a71ab17c051f5f7d45c209e40269a1938832510e4d9"}, From 333ae6c34adf967c5e4f5a806c5ced532652e382 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 17:26:20 +0100 Subject: [PATCH 120/189] client_id > auth_client_id #57 --- lib/auth_web/controllers/auth_controller.ex | 5 +++-- lib/auth_web/controllers/page_controller.ex | 14 +++++++------- test/auth_web/controllers/auth_controller_test.exs | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index d76a6236..83b536a2 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -19,6 +19,7 @@ defmodule AuthWeb.AuthController do `google_handler/2` handles the callback from Google Auth API redirect. """ def google_handler(conn, %{"code" => code, "state" => state}) do + IO.inspect(state, label: "state:22") {:ok, token} = ElixirAuthGoogle.get_token(code, conn) {:ok, profile} = ElixirAuthGoogle.get_user_profile(token.access_token) @@ -91,8 +92,8 @@ defmodule AuthWeb.AuthController do """ def get_client_secret_from_state(state) do query = URI.decode_query(state) - # IO.inspect(query, label: "query") - client_id = Map.get(query, "client_id") + IO.inspect(query, label: "query") + client_id = Map.get(query, "auth_client_id") IO.inspect(client_id, label: "client_id") case not is_nil(client_id) do true -> # Lookup client_id in apikeys table diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index 2558fead..dc122fb0 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -21,7 +21,7 @@ defmodule AuthWeb.PageController do end def append_client_id(ref, client_id) do - ref <> "?client_id=" <> client_id + ref <> "&client_id=" <> client_id end def get_referer(conn) do @@ -36,20 +36,20 @@ defmodule AuthWeb.PageController do query = URI.decode_query(conn.query_string) ref = Map.get(query, "referer") client_id = get_client_id_from_query(conn) - ref |> append_client_id(client_id) + ref |> URI.encode |> append_client_id(client_id) false -> # no referer, redirect back to Auth app. - AuthPlug.Helpers.get_baseurl_from_conn(conn) - <> "/profile?client_id" <> AuthPlug.Token.client_id() + AuthPlug.Helpers.get_baseurl_from_conn(conn) <> "/profile" + |> URI.encode + |> append_client_id(AuthPlug.Token.client_id()) end end - |> URI.encode |> IO.inspect(label: "referer") end def get_client_id_from_query(conn) do - case conn.query_string =~ "client_id" do + case conn.query_string =~ "auth_client_id" do true -> - Map.get(URI.decode_query(conn.query_string), "client_id") + Map.get(URI.decode_query(conn.query_string), "auth_client_id") false -> # no client_id, redirect back to this app. 0 end diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 5b5e9047..ca2abde2 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -12,7 +12,7 @@ defmodule AuthWeb.AuthControllerTest do test "google_handler/2 for google auth callback", %{conn: conn} do conn = get(conn, "/auth/google/callback", - %{code: "234", state: "http://localhost:4000/" <> + %{code: "234", state: "http://localhost:4000" <> "&client_id=" <> AuthPlug.Token.client_id() }) # assert html_response(conn, 200) =~ "nelson@gmail.com" From 425765641226cace1003751cf56f0293a3afdd7e Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 17:26:42 +0100 Subject: [PATCH 121/189] client_id > auth_client_id (tests) #57 --- test/auth_web/controllers/auth_controller_test.exs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index ca2abde2..05ce34d1 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -13,7 +13,7 @@ defmodule AuthWeb.AuthControllerTest do test "google_handler/2 for google auth callback", %{conn: conn} do conn = get(conn, "/auth/google/callback", %{code: "234", state: "http://localhost:4000" <> - "&client_id=" <> AuthPlug.Token.client_id() }) + "&auth_client_id=" <> AuthPlug.Token.client_id() }) # assert html_response(conn, 200) =~ "nelson@gmail.com" assert html_response(conn, 302) =~ "http://localhost" @@ -30,7 +30,7 @@ defmodule AuthWeb.AuthControllerTest do conn = get(conn, "/auth/google/callback", %{code: "234", state: AuthPlug.Helpers.get_baseurl_from_conn(conn) <> - "&client_id=" <> key.client_id }) + "&auth_client_id=" <> key.client_id }) # assert html_response(conn, 200) =~ "nelson@gmail.com" assert html_response(conn, 302) =~ "redirected" @@ -56,7 +56,7 @@ defmodule AuthWeb.AuthControllerTest do invalid_key = String.slice(AuthPlug.Token.client_id(), 0..-2) conn = get(conn, "/auth/google/callback", %{code: "234", state: "www.example.com/" <> - "&client_id=" <> invalid_key }) + "&auth_client_id=" <> invalid_key }) # assert html_response(conn, 200) =~ "google account" assert html_response(conn, 401) =~ "invalid" end From 2e60531edec42c048902834df63487a277a7e1e3 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 17:28:55 +0100 Subject: [PATCH 122/189] client_id > auth_client_id (moar tests) #57 --- test/auth_web/controllers/auth_controller_test.exs | 2 +- test/auth_web/controllers/page_controller_test.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 05ce34d1..ac0f5621 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -5,7 +5,7 @@ defmodule AuthWeb.AuthControllerTest do test "github_handler/2 github auth callback", %{conn: conn} do conn = get(conn, "/auth/github/callback", %{code: "123", state: "http://localhost:4000/" <> - "&client_id=" <> AuthPlug.Token.client_id() }) + "&auth_client_id=" <> AuthPlug.Token.client_id() }) # assert html_response(conn, 200) =~ "test@gmail.com" assert html_response(conn, 302) =~ "http://localhost" end diff --git a/test/auth_web/controllers/page_controller_test.exs b/test/auth_web/controllers/page_controller_test.exs index 1cd011ea..072cda87 100644 --- a/test/auth_web/controllers/page_controller_test.exs +++ b/test/auth_web/controllers/page_controller_test.exs @@ -23,7 +23,7 @@ defmodule AuthWeb.PageControllerTest do test "get_referer/1 query_string", %{conn: conn} do conn = conn |> get("/?referer=" <> URI.encode("http://localhost/admin") - <> "&client_id=" <> AuthPlug.Token.client_id() + <> "&auth_client_id=" <> AuthPlug.Token.client_id() ) assert conn.resp_body =~ "state=http://localhost/admin" From 6801da1b861651a354c236ef70328045ca840d4f Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 17:51:39 +0100 Subject: [PATCH 123/189] add debug statements for #57 --- lib/auth_web/controllers/auth_controller.ex | 3 ++- lib/auth_web/controllers/page_controller.ex | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 83b536a2..371c62e7 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -91,8 +91,9 @@ defmodule AuthWeb.AuthController do All other failure conditions return a 0 (zero) which results in a 401. """ def get_client_secret_from_state(state) do + IO.inspect(state, label: "state:94") query = URI.decode_query(state) - IO.inspect(query, label: "query") + IO.inspect(query, label: "query:96") client_id = Map.get(query, "auth_client_id") IO.inspect(client_id, label: "client_id") case not is_nil(client_id) do diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index dc122fb0..31f42f9c 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -21,7 +21,7 @@ defmodule AuthWeb.PageController do end def append_client_id(ref, client_id) do - ref <> "&client_id=" <> client_id + ref <> "?auth_client_id=" <> client_id end def get_referer(conn) do From 6a264a47ae2cf2f9f7548976ef70a48736c2393e Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 18:14:45 +0100 Subject: [PATCH 124/189] strip client_id from state before redirecting #57 --- lib/auth_web/controllers/auth_controller.ex | 28 +++++++++++++-------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 371c62e7..f3a2292f 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -58,15 +58,16 @@ defmodule AuthWeb.AuthController do true -> # redirect case get_client_secret_from_state(state) do 0 -> - # IO.inspect("client_secret is 0 (error)") + IO.inspect("client_secret is 0 (error)") unauthorized(conn) secret -> - # IO.inspect(secret, label: "secret") + IO.inspect(secret, label: "secret") conn + # |> AuthPlug.create_session(person, secret) |> redirect(external: add_jwt_url_param(person, state, secret)) end - false -> # display welcome page + false -> # display welcome page on Auth site: conn |> put_view(AuthWeb.PageView) |> AuthPlug.create_jwt_session(person) @@ -92,8 +93,12 @@ defmodule AuthWeb.AuthController do """ def get_client_secret_from_state(state) do IO.inspect(state, label: "state:94") - query = URI.decode_query(state) - IO.inspect(query, label: "query:96") + decoded = URI.decode(state) + IO.inspect(decoded, label: "decoded:96") + query = List.last(String.split(state, "?")) + IO.inspect(query, label: "query:98") + query = URI.decode_query(query) + IO.inspect(query, label: "query:100") client_id = Map.get(query, "auth_client_id") IO.inspect(client_id, label: "client_id") case not is_nil(client_id) do @@ -107,13 +112,13 @@ defmodule AuthWeb.AuthController do def get_client_secret(client_id, state) do person_id = AuthWeb.ApikeyController.decode_decrypt(client_id) - # IO.inspect(person_id, label: "person_id") + IO.inspect(person_id, label: "person_id:114") if person_id == 0 do # decode_decrypt fails with state 0 - # IO.inspect(person_id, label: "person_id:88") + IO.inspect(person_id, label: "person_id:116") 0 else apikeys = Auth.Apikey.list_apikeys_for_person(person_id) - # IO.inspect(apikeys) + IO.inspect(apikeys, label: "apikeys:120") Enum.filter(apikeys, fn(k) -> k.client_id == client_id and state =~ k.url end) |> List.first() |> Map.get(:client_secret) @@ -125,7 +130,8 @@ defmodule AuthWeb.AuthController do def add_jwt_url_param(person, state, client_secret) do - IO.inspect(state, label: "state") + IO.inspect(state, label: "state:133") + # IO.inspect(client_secret, label: "client_secret:134") data = %{ auth_provider: person.auth_provider, @@ -136,6 +142,8 @@ defmodule AuthWeb.AuthController do } jwt = AuthPlug.Token.generate_jwt!(data, client_secret) - URI.decode(state) <> "?jwt=" <> jwt + List.first(String.split(URI.decode(state), "?")) + <> "?jwt=" <> jwt + # |> IO.inspect(label: "state+jwt:146") end end From 2a4bcdce20b27d1b225b28f8dec03c5c4b70cf60 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 22:11:25 +0100 Subject: [PATCH 125/189] add tests for Edit API Key data #58 --- .../controllers/apikey_controller_test.exs | 37 ++++++++++++++----- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/test/auth_web/controllers/apikey_controller_test.exs b/test/auth_web/controllers/apikey_controller_test.exs index c8030065..02cc2d55 100644 --- a/test/auth_web/controllers/apikey_controller_test.exs +++ b/test/auth_web/controllers/apikey_controller_test.exs @@ -114,15 +114,34 @@ defmodule AuthWeb.ApikeyControllerTest do # assert html_response(conn, 200) =~ "New Apikey" # end end - # - # describe "edit apikey" do - # setup [:create_apikey] - # - # test "renders form for editing chosen apikey", %{conn: conn, apikey: apikey} do - # conn = get(conn, Routes.apikey_path(conn, :edit, apikey)) - # assert html_response(conn, 200) =~ "Edit Apikey" - # end - # end + + describe "edit apikey" do + test "renders form for editing chosen apikey", %{conn: conn} do + + person = Auth.Person.get_person_by_email(@email) + conn = AuthPlug.create_jwt_session(conn, person) + {:ok, key} = %{"name" => "test key", "url" => "http://localhost:4000"} + |> AuthWeb.ApikeyController.make_apikey(person.id) + |> Auth.Apikey.create_apikey() + + conn = get(conn, Routes.apikey_path(conn, :edit, key.id)) + assert html_response(conn, 200) =~ "Edit Apikey" + end + + test "attempt to edit a key I don't own > should 404", %{conn: conn} do + + person = Auth.Person.get_person_by_email(@email) + wrong_person = Auth.Person.create_person(%{email: "wrong@gmail.com"}) + conn = AuthPlug.create_jwt_session(conn, wrong_person) + + {:ok, key} = %{"name" => "test key", "url" => "http://localhost:4000"} + |> AuthWeb.ApikeyController.make_apikey(person.id) + |> Auth.Apikey.create_apikey() + + conn = get(conn, Routes.apikey_path(conn, :edit, key.id)) + assert html_response(conn, 404) =~ "not found" + end + end # # describe "update apikey" do # setup [:create_apikey] From 9969305c21f3b8414b9ac2da89083d46a95906c4 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 22:11:52 +0100 Subject: [PATCH 126/189] add Edit API Key implementation fixes #58 --- lib/auth_web/controllers/apikey_controller.ex | 16 +++++++++++----- lib/auth_web/controllers/auth_controller.ex | 10 +++++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/auth_web/controllers/apikey_controller.ex b/lib/auth_web/controllers/apikey_controller.ex index 44b85292..68c9f2a4 100644 --- a/lib/auth_web/controllers/apikey_controller.ex +++ b/lib/auth_web/controllers/apikey_controller.ex @@ -64,11 +64,17 @@ defmodule AuthWeb.ApikeyController do render(conn, "show.html", apikey: apikey) end - # def edit(conn, %{"id" => id}) do - # apikey = Ctx.get_apikey!(id) - # changeset = Ctx.change_apikey(apikey) - # render(conn, "edit.html", apikey: apikey, changeset: changeset) - # end + def edit(conn, %{"id" => id}) do + person_id = conn.assigns.decoded.id + apikey = Auth.Apikey.get_apikey!(id) + # IO.inspect(apikey, label: "apikey") + if apikey.person_id == person_id do + changeset = Auth.Apikey.change_apikey(apikey) + render(conn, "edit.html", apikey: apikey, changeset: changeset) + else + AuthWeb.AuthController.not_found(conn, "API KEY " <> id <> " not found." ) + end + end # def update(conn, %{"id" => id, "apikey" => apikey_params}) do # apikey = Ctx.get_apikey!(id) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index f3a2292f..b7b6a48f 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -75,7 +75,7 @@ defmodule AuthWeb.AuthController do end end - defp unauthorized(conn) do + def unauthorized(conn) do # IO.inspect(conn) conn # |> put_resp_header("www-authenticate", "Bearer realm=\"Person access\"") @@ -84,6 +84,14 @@ defmodule AuthWeb.AuthController do |> halt() end + # TODO: refactor this to render a template with a nice layout. + def not_found(conn, message) do + conn + |> put_resp_content_type("text/html") + |> send_resp(404, message) + |> halt() + end + @doc """ `get_client_secret_from_state/1` gets the client_id from state, From eceeea4b6ea12d6c01e591da1265a2f82a6cbeee Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 22:16:48 +0100 Subject: [PATCH 127/189] comment out excess debugging IO.inspect statements #42 --- lib/auth_web/controllers/auth_controller.ex | 29 +++++++++------------ 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index b7b6a48f..7aca4d09 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -19,7 +19,7 @@ defmodule AuthWeb.AuthController do `google_handler/2` handles the callback from Google Auth API redirect. """ def google_handler(conn, %{"code" => code, "state" => state}) do - IO.inspect(state, label: "state:22") + # IO.inspect(state, label: "state:22") {:ok, token} = ElixirAuthGoogle.get_token(code, conn) {:ok, profile} = ElixirAuthGoogle.get_user_profile(token.access_token) @@ -58,10 +58,10 @@ defmodule AuthWeb.AuthController do true -> # redirect case get_client_secret_from_state(state) do 0 -> - IO.inspect("client_secret is 0 (error)") + # IO.inspect("client_secret is 0 (error)") unauthorized(conn) secret -> - IO.inspect(secret, label: "secret") + # IO.inspect(secret, label: "secret") conn # |> AuthPlug.create_session(person, secret) |> redirect(external: add_jwt_url_param(person, state, secret)) @@ -100,15 +100,13 @@ defmodule AuthWeb.AuthController do All other failure conditions return a 0 (zero) which results in a 401. """ def get_client_secret_from_state(state) do - IO.inspect(state, label: "state:94") - decoded = URI.decode(state) - IO.inspect(decoded, label: "decoded:96") - query = List.last(String.split(state, "?")) - IO.inspect(query, label: "query:98") - query = URI.decode_query(query) - IO.inspect(query, label: "query:100") + # IO.inspect(state, label: "state:94") + # decoded = URI.decode(state) + # IO.inspect(decoded, label: "decoded:96") + query = URI.decode_query(List.last(String.split(state, "?"))) + # IO.inspect(query, label: "query:100") client_id = Map.get(query, "auth_client_id") - IO.inspect(client_id, label: "client_id") + # IO.inspect(client_id, label: "client_id") case not is_nil(client_id) do true -> # Lookup client_id in apikeys table get_client_secret(client_id, state) @@ -120,13 +118,13 @@ defmodule AuthWeb.AuthController do def get_client_secret(client_id, state) do person_id = AuthWeb.ApikeyController.decode_decrypt(client_id) - IO.inspect(person_id, label: "person_id:114") + # IO.inspect(person_id, label: "person_id:114") if person_id == 0 do # decode_decrypt fails with state 0 - IO.inspect(person_id, label: "person_id:116") + # IO.inspect(person_id, label: "person_id:116") 0 else apikeys = Auth.Apikey.list_apikeys_for_person(person_id) - IO.inspect(apikeys, label: "apikeys:120") + # IO.inspect(apikeys, label: "apikeys:120") Enum.filter(apikeys, fn(k) -> k.client_id == client_id and state =~ k.url end) |> List.first() |> Map.get(:client_secret) @@ -138,9 +136,8 @@ defmodule AuthWeb.AuthController do def add_jwt_url_param(person, state, client_secret) do - IO.inspect(state, label: "state:133") + # IO.inspect(state, label: "state:133") # IO.inspect(client_secret, label: "client_secret:134") - data = %{ auth_provider: person.auth_provider, givenName: person.givenName, From 49a8da2836f02cd102143624f269938a0cc77877 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 22:19:47 +0100 Subject: [PATCH 128/189] create tests for update/2 for #58 --- .../controllers/apikey_controller_test.exs | 57 ++++++++++++------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/test/auth_web/controllers/apikey_controller_test.exs b/test/auth_web/controllers/apikey_controller_test.exs index 02cc2d55..f8477cd0 100644 --- a/test/auth_web/controllers/apikey_controller_test.exs +++ b/test/auth_web/controllers/apikey_controller_test.exs @@ -5,6 +5,10 @@ defmodule AuthWeb.ApikeyControllerTest do # alias Auth.Apikey import AuthWeb.ApikeyController @email System.get_env("ADMIN_EMAIL") + @create_attrs %{description: "some description", name: "some name", url: "some url"} + @update_attrs %{client_secret: "updated client sec", description: "some updated desc", name: "updated name", url: "surl"} + @invalid_attrs %{client_secret: nil, description: nil, key_id: nil, name: nil, url: nil} + describe "Create a DWYL_API_KEY for a given person_id" do test "encrypt_encode/1 returns a base58 we can decrypt" do @@ -62,11 +66,6 @@ defmodule AuthWeb.ApikeyControllerTest do end - - @create_attrs %{description: "some description", name: "some name", url: "some url"} - # @update_attrs %{client_secret: "some updated client_secret", description: "some updated description", key_id: 43, name: "some updated name", url: "surl"} - # @invalid_attrs %{client_secret: nil, description: nil, key_id: nil, name: nil, url: nil} - # # def fixture(:apikey) do # {:ok, apikey} = Ctx.create_apikey(@create_attrs) # apikey @@ -142,23 +141,37 @@ defmodule AuthWeb.ApikeyControllerTest do assert html_response(conn, 404) =~ "not found" end end - # - # describe "update apikey" do - # setup [:create_apikey] - # - # test "redirects when data is valid", %{conn: conn, apikey: apikey} do - # conn = put(conn, Routes.apikey_path(conn, :update, apikey), apikey: @update_attrs) - # assert redirected_to(conn) == Routes.apikey_path(conn, :show, apikey) - # - # conn = get(conn, Routes.apikey_path(conn, :show, apikey)) - # assert html_response(conn, 200) =~ "some updated description" - # end - # - # test "renders errors when data is invalid", %{conn: conn, apikey: apikey} do - # conn = put(conn, Routes.apikey_path(conn, :update, apikey), apikey: @invalid_attrs) - # assert html_response(conn, 200) =~ "Edit Apikey" - # end - # end + + describe "update apikey" do + + test "redirects when data is valid", %{conn: conn} do + + person = Auth.Person.get_person_by_email(@email) + conn = AuthPlug.create_jwt_session(conn, person) + {:ok, key} = %{"name" => "test key", "url" => "http://localhost:4000"} + |> AuthWeb.ApikeyController.make_apikey(person.id) + |> Auth.Apikey.create_apikey() + + + conn = put(conn, Routes.apikey_path(conn, :update, key.id), apikey: @update_attrs) + assert redirected_to(conn) == Routes.apikey_path(conn, :show, key) + + conn = get(conn, Routes.apikey_path(conn, :show, key)) + assert html_response(conn, 200) =~ "some updated desc" + end + + test "renders errors when data is invalid", %{conn: conn} do + + person = Auth.Person.get_person_by_email(@email) + conn = AuthPlug.create_jwt_session(conn, person) + {:ok, key} = %{"name" => "test key", "url" => "http://localhost:4000"} + |> AuthWeb.ApikeyController.make_apikey(person.id) + |> Auth.Apikey.create_apikey() + + conn = put(conn, Routes.apikey_path(conn, :update, key), apikey: @invalid_attrs) + assert html_response(conn, 200) =~ "Edit Apikey" + end + end # # describe "delete apikey" do # setup [:create_apikey] From caeb731d9436cde59903b9b3816a8dbb98d3d8db Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 22:20:53 +0100 Subject: [PATCH 129/189] enable update_key/2 function #58 --- lib/auth/apikey.ex | 21 ++++++++++++++++++++- lib/auth/ctx.ex | 18 +----------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/lib/auth/apikey.ex b/lib/auth/apikey.ex index 52b59310..dbc0467c 100644 --- a/lib/auth/apikey.ex +++ b/lib/auth/apikey.ex @@ -21,7 +21,8 @@ defmodule Auth.Apikey do @doc false def changeset(apikey, attrs) do apikey - |> cast(attrs, [:client_id, :client_secret, :name, :description, :url, :person_id]) + |> cast(attrs, [:client_id, :client_secret, + :name, :description, :url, :person_id]) |> validate_required([:client_secret]) end @@ -61,4 +62,22 @@ defmodule Auth.Apikey do """ def get_apikey!(id), do: Repo.get!(__MODULE__, id) + @doc """ + Updates a apikey. + + ## Examples + + iex> update_apikey(apikey, %{field: new_value}) + {:ok, %Apikey{}} + + iex> update_apikey(apikey, %{field: bad_value}) + {:error, %Ecto.Changeset{}} + + """ + def update_apikey(%Apikey{} = apikey, attrs) do + apikey + |> changeset(attrs) + |> Repo.update() + end + end diff --git a/lib/auth/ctx.ex b/lib/auth/ctx.ex index b6746ed0..342583aa 100644 --- a/lib/auth/ctx.ex +++ b/lib/auth/ctx.ex @@ -55,23 +55,7 @@ # |> Repo.insert() # end # -# @doc """ -# Updates a apikey. -# -# ## Examples -# -# iex> update_apikey(apikey, %{field: new_value}) -# {:ok, %Apikey{}} -# -# iex> update_apikey(apikey, %{field: bad_value}) -# {:error, %Ecto.Changeset{}} -# -# """ -# def update_apikey(%Apikey{} = apikey, attrs) do -# apikey -# |> Apikey.changeset(attrs) -# |> Repo.update() -# end + # # @doc """ # Deletes a apikey. From 6cf8c3761aa36f12b0fa079edb71e307fe6d130a Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 22:21:14 +0100 Subject: [PATCH 130/189] update/2 for #58 --- lib/auth_web/controllers/apikey_controller.ex | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/auth_web/controllers/apikey_controller.ex b/lib/auth_web/controllers/apikey_controller.ex index 68c9f2a4..5344fe02 100644 --- a/lib/auth_web/controllers/apikey_controller.ex +++ b/lib/auth_web/controllers/apikey_controller.ex @@ -76,19 +76,24 @@ defmodule AuthWeb.ApikeyController do end end - # def update(conn, %{"id" => id, "apikey" => apikey_params}) do - # apikey = Ctx.get_apikey!(id) - # - # case Ctx.update_apikey(apikey, apikey_params) do - # {:ok, apikey} -> - # conn - # |> put_flash(:info, "Apikey updated successfully.") - # |> redirect(to: Routes.apikey_path(conn, :show, apikey)) - # - # {:error, %Ecto.Changeset{} = changeset} -> - # render(conn, "edit.html", apikey: apikey, changeset: changeset) - # end - # end + @doc """ + `update/2` updates a given API Key. Checks if the person attempting + to update the key is the "owner" of the key before updating. + """ + def update(conn, %{"id" => id, "apikey" => apikey_params}) do + apikey = Apikey.get_apikey!(id) + + + case Apikey.update_apikey(apikey, apikey_params) do + {:ok, apikey} -> + conn + |> put_flash(:info, "Apikey updated successfully.") + |> redirect(to: Routes.apikey_path(conn, :show, apikey)) + + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "edit.html", apikey: apikey, changeset: changeset) + end + end # # def delete(conn, %{"id" => id}) do # apikey = Ctx.get_apikey!(id) From 21fae611b4caff82ec62f4e375dc97cdb372efd1 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 22:42:03 +0100 Subject: [PATCH 131/189] add test for checking api key ownership in update/2 for #58 --- .../controllers/apikey_controller_test.exs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/auth_web/controllers/apikey_controller_test.exs b/test/auth_web/controllers/apikey_controller_test.exs index f8477cd0..5722d77a 100644 --- a/test/auth_web/controllers/apikey_controller_test.exs +++ b/test/auth_web/controllers/apikey_controller_test.exs @@ -171,6 +171,22 @@ defmodule AuthWeb.ApikeyControllerTest do conn = put(conn, Routes.apikey_path(conn, :update, key), apikey: @invalid_attrs) assert html_response(conn, 200) =~ "Edit Apikey" end + + test "attempt to UPDATE a key I don't own > should 404", %{conn: conn} do + + person = Auth.Person.get_person_by_email(@email) + # create session with wrong person: + wrong_person = Auth.Person.create_person(%{email: "wronger@gmail.com"}) + conn = AuthPlug.create_jwt_session(conn, wrong_person) + + {:ok, key} = %{"name" => "test key", + "url" => "http://localhost:4000", "person_id" => person.id} + |> AuthWeb.ApikeyController.make_apikey(person.id) + |> Auth.Apikey.create_apikey() + + conn = put(conn, Routes.apikey_path(conn, :update, key.id), apikey: @update_attrs) + assert html_response(conn, 404) =~ "not found" + end end # # describe "delete apikey" do From d4cc4d81431d56e849d9c0a9a944391acee06d9d Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 29 Apr 2020 22:42:37 +0100 Subject: [PATCH 132/189] modify update/2 (update api key) to check for ownership #58 --- lib/auth_web/controllers/apikey_controller.ex | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/auth_web/controllers/apikey_controller.ex b/lib/auth_web/controllers/apikey_controller.ex index 5344fe02..c56bc7ee 100644 --- a/lib/auth_web/controllers/apikey_controller.ex +++ b/lib/auth_web/controllers/apikey_controller.ex @@ -82,16 +82,20 @@ defmodule AuthWeb.ApikeyController do """ def update(conn, %{"id" => id, "apikey" => apikey_params}) do apikey = Apikey.get_apikey!(id) - - - case Apikey.update_apikey(apikey, apikey_params) do - {:ok, apikey} -> - conn - |> put_flash(:info, "Apikey updated successfully.") - |> redirect(to: Routes.apikey_path(conn, :show, apikey)) + person_id = conn.assigns.decoded.id + # check that the person attempting to update the key owns it! + if apikey.person_id == person_id do + case Apikey.update_apikey(apikey, apikey_params) do + {:ok, apikey} -> + conn + |> put_flash(:info, "Apikey updated successfully.") + |> redirect(to: Routes.apikey_path(conn, :show, apikey)) - {:error, %Ecto.Changeset{} = changeset} -> - render(conn, "edit.html", apikey: apikey, changeset: changeset) + {:error, %Ecto.Changeset{} = changeset} -> + render(conn, "edit.html", apikey: apikey, changeset: changeset) + end + else + AuthWeb.AuthController.not_found(conn, "API KEY " <> id <> " not found." ) end end # From 714bdd3ad1899f9cca05e49f76f4031b1592476d Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 30 Apr 2020 13:28:16 +0100 Subject: [PATCH 133/189] create nav.html.eex for #51 --- lib/auth_web/templates/layout/app.html.eex | 11 +++- lib/auth_web/templates/layout/nav.html.eex | 68 ++++++++++++++++++++++ 2 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 lib/auth_web/templates/layout/nav.html.eex diff --git a/lib/auth_web/templates/layout/app.html.eex b/lib/auth_web/templates/layout/app.html.eex index a4b98c61..f8717cae 100644 --- a/lib/auth_web/templates/layout/app.html.eex +++ b/lib/auth_web/templates/layout/app.html.eex @@ -5,11 +5,16 @@ <%= assigns[:page_title] || "Auth · Phoenix Framework" %> - "/> + <%= csrf_meta_tag() %> + - -
+ + + <%= render "nav.html", assigns %> + + +
<%= render @view_module, @view_template, assigns %> diff --git a/lib/auth_web/templates/layout/nav.html.eex b/lib/auth_web/templates/layout/nav.html.eex new file mode 100644 index 00000000..67a8e974 --- /dev/null +++ b/lib/auth_web/templates/layout/nav.html.eex @@ -0,0 +1,68 @@ + + + + + + + + + From ff14c866bc40dd40aae115ecc7a99e8210e72888 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 30 Apr 2020 14:52:50 +0100 Subject: [PATCH 134/189] tidy up index.html.eex (listing apikeys) #51 --- lib/auth_web/templates/apikey/index.html.eex | 51 ++++++++++++++++---- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/lib/auth_web/templates/apikey/index.html.eex b/lib/auth_web/templates/apikey/index.html.eex index 04021588..4c8d22c1 100644 --- a/lib/auth_web/templates/apikey/index.html.eex +++ b/lib/auth_web/templates/apikey/index.html.eex @@ -1,32 +1,63 @@ -

API Keys

+

Auth API Keys

+
- +
- + - - <%= for apikey <- @apikeys do %> - + <% end %>
DWYL_API_KEYAUTH_API_KEY Name Description Url
<%= apikey.client_id %><%= apikey.client_secret %> + <%= apikey.client_id %>/<%= apikey.client_secret %> + <%= apikey.name %> <%= apikey.description %> <%= apikey.url %> - <%= link "Show", to: Routes.apikey_path(@conn, :show, apikey) %> - <%= link "Edit", to: Routes.apikey_path(@conn, :edit, apikey) %> - <%= link "Delete", to: Routes.apikey_path(@conn, :delete, apikey), method: :delete, data: [confirm: "Are you sure?"] %> + + <%= link "Edit", to: Routes.apikey_path(@conn, :edit, apikey), + class: "pointer br2 ba b--dark-blue bg-blue white pa3 ml1 mv1 f4 + bg-animate hover-bg-dark-blue border-box no-underline" + %> + + <%= link "Delete", to: Routes.apikey_path(@conn, :delete, apikey), method: :delete, + class: "pointer br2 ba b--dark-red bg-red white pa3 ml1 mv1 f4 + bg-animate hover-bg-dark-red border-box no-underline", + data: + [confirm: "Are you sure you want to delete this API Key? + (This cannot be undone!)"] %> +
+

+ + +<%= link "New Apikey", to: Routes.apikey_path(@conn, :new), +class: "fr mr4 pointer br2 ba b--dark-green bg-green white pa3 ml1 mv1 f4 shadow-hover +bg-animate hover-bg-dark-green border-box no-underline" %> + -<%= link "New Apikey", to: Routes.apikey_path(@conn, :new) %> + From 5fe245edaf0d242be8883d9fc414c8143b39c686 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 30 Apr 2020 14:54:28 +0100 Subject: [PATCH 135/189] add delete/2 for #59 --- lib/auth/apikey.ex | 15 ++++ lib/auth/ctx.ex | 79 ------------------- lib/auth_web/controllers/apikey_controller.ex | 28 ++++--- 3 files changed, 30 insertions(+), 92 deletions(-) diff --git a/lib/auth/apikey.ex b/lib/auth/apikey.ex index dbc0467c..0d839468 100644 --- a/lib/auth/apikey.ex +++ b/lib/auth/apikey.ex @@ -80,4 +80,19 @@ defmodule Auth.Apikey do |> Repo.update() end + @doc """ + Deletes a apikey. + + ## Examples + + iex> delete_apikey(apikey) + {:ok, %Apikey{}} + + iex> delete_apikey(apikey) + {:error, %Ecto.Changeset{}} + + """ + def delete_apikey(%Apikey{} = apikey) do + Repo.delete(apikey) + end end diff --git a/lib/auth/ctx.ex b/lib/auth/ctx.ex index 342583aa..fa2be706 100644 --- a/lib/auth/ctx.ex +++ b/lib/auth/ctx.ex @@ -7,82 +7,3 @@ # alias Auth.Repo # # alias Auth.Ctx.Apikey -# -# @doc """ -# Returns the list of apikeys. -# -# ## Examples -# -# iex> list_apikeys() -# [%Apikey{}, ...] -# -# """ -# def list_apikeys do -# Repo.all(Apikey) -# end -# -# @doc """ -# Gets a single apikey. -# -# Raises `Ecto.NoResultsError` if the Apikey does not exist. -# -# ## Examples -# -# iex> get_apikey!(123) -# %Apikey{} -# -# iex> get_apikey!(456) -# ** (Ecto.NoResultsError) -# -# """ -# def get_apikey!(id), do: Repo.get!(Apikey, id) -# -# @doc """ -# Creates a apikey. -# -# ## Examples -# -# iex> create_apikey(%{field: value}) -# {:ok, %Apikey{}} -# -# iex> create_apikey(%{field: bad_value}) -# {:error, %Ecto.Changeset{}} -# -# """ -# def create_apikey(attrs \\ %{}) do -# %Apikey{} -# |> Apikey.changeset(attrs) -# |> Repo.insert() -# end -# - -# -# @doc """ -# Deletes a apikey. -# -# ## Examples -# -# iex> delete_apikey(apikey) -# {:ok, %Apikey{}} -# -# iex> delete_apikey(apikey) -# {:error, %Ecto.Changeset{}} -# -# """ -# def delete_apikey(%Apikey{} = apikey) do -# Repo.delete(apikey) -# end -# -# @doc """ -# Returns an `%Ecto.Changeset{}` for tracking apikey changes. -# -# ## Examples -# -# iex> change_apikey(apikey) -# %Ecto.Changeset{source: %Apikey{}} -# -# """ -# def change_apikey(%Apikey{} = apikey) do -# Apikey.changeset(apikey, %{}) -# end -# end diff --git a/lib/auth_web/controllers/apikey_controller.ex b/lib/auth_web/controllers/apikey_controller.ex index c56bc7ee..41d95852 100644 --- a/lib/auth_web/controllers/apikey_controller.ex +++ b/lib/auth_web/controllers/apikey_controller.ex @@ -65,10 +65,9 @@ defmodule AuthWeb.ApikeyController do end def edit(conn, %{"id" => id}) do - person_id = conn.assigns.decoded.id apikey = Auth.Apikey.get_apikey!(id) # IO.inspect(apikey, label: "apikey") - if apikey.person_id == person_id do + if apikey.person_id == conn.assigns.decoded.id do changeset = Auth.Apikey.change_apikey(apikey) render(conn, "edit.html", apikey: apikey, changeset: changeset) else @@ -82,9 +81,8 @@ defmodule AuthWeb.ApikeyController do """ def update(conn, %{"id" => id, "apikey" => apikey_params}) do apikey = Apikey.get_apikey!(id) - person_id = conn.assigns.decoded.id # check that the person attempting to update the key owns it! - if apikey.person_id == person_id do + if apikey.person_id == conn.assigns.decoded.id do case Apikey.update_apikey(apikey, apikey_params) do {:ok, apikey} -> conn @@ -98,13 +96,17 @@ defmodule AuthWeb.ApikeyController do AuthWeb.AuthController.not_found(conn, "API KEY " <> id <> " not found." ) end end - # - # def delete(conn, %{"id" => id}) do - # apikey = Ctx.get_apikey!(id) - # {:ok, _apikey} = Ctx.delete_apikey(apikey) - # - # conn - # |> put_flash(:info, "Apikey deleted successfully.") - # |> redirect(to: Routes.apikey_path(conn, :index)) - # end + + def delete(conn, %{"id" => id}) do + apikey = Apikey.get_apikey!(id) + # check that the person attempting to delete the key owns it! + if apikey.person_id == conn.assigns.decoded.id do + {:ok, _apikey} = Apikey.delete_apikey(apikey) + conn + |> put_flash(:info, "Apikey deleted successfully.") + |> redirect(to: Routes.apikey_path(conn, :index)) + else + AuthWeb.AuthController.not_found(conn, "API KEY " <> id <> " not found.") + end + end end From 0196ee12269f871c327e1b4b9921706787f1e4d6 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 30 Apr 2020 14:54:59 +0100 Subject: [PATCH 136/189] delete lib/auth/ctx.ex as no longer used --- lib/auth/ctx.ex | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 lib/auth/ctx.ex diff --git a/lib/auth/ctx.ex b/lib/auth/ctx.ex deleted file mode 100644 index fa2be706..00000000 --- a/lib/auth/ctx.ex +++ /dev/null @@ -1,9 +0,0 @@ -# defmodule Auth.Ctx do -# @moduledoc """ -# The Ctx context. -# """ -# -# import Ecto.Query, warn: false -# alias Auth.Repo -# -# alias Auth.Ctx.Apikey From 6955d4e576b0dd79e45d108df687bc6bdc40518b Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 30 Apr 2020 15:09:31 +0100 Subject: [PATCH 137/189] add tests for attempting to delete an api key not owned #59 --- lib/auth_web/templates/apikey/show.html.eex | 2 +- .../controllers/apikey_controller_test.exs | 76 +++++++++++-------- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/lib/auth_web/templates/apikey/show.html.eex b/lib/auth_web/templates/apikey/show.html.eex index 6f5c0ecd..9c51df9c 100644 --- a/lib/auth_web/templates/apikey/show.html.eex +++ b/lib/auth_web/templates/apikey/show.html.eex @@ -1,4 +1,4 @@ -

Your DWYL_API_KEY

+

Your AUTH_API_KEY

diff --git a/test/auth_web/controllers/apikey_controller_test.exs b/test/auth_web/controllers/apikey_controller_test.exs index 5722d77a..14b1c54f 100644 --- a/test/auth_web/controllers/apikey_controller_test.exs +++ b/test/auth_web/controllers/apikey_controller_test.exs @@ -3,17 +3,17 @@ defmodule AuthWeb.ApikeyControllerTest do use ExUnitProperties # alias Auth.Apikey - import AuthWeb.ApikeyController + # alias AuthWeb.ApikeyController, as: Ctrl @email System.get_env("ADMIN_EMAIL") @create_attrs %{description: "some description", name: "some name", url: "some url"} @update_attrs %{client_secret: "updated client sec", description: "some updated desc", name: "updated name", url: "surl"} @invalid_attrs %{client_secret: nil, description: nil, key_id: nil, name: nil, url: nil} - describe "Create a DWYL_API_KEY for a given person_id" do + describe "Create an AUTH_API_KEY for a given person_id" do test "encrypt_encode/1 returns a base58 we can decrypt" do person_id = 1 - key = encrypt_encode(person_id) + key = AuthWeb.ApikeyController.encrypt_encode(person_id) # |> IO.inspect(label: "key") decrypted = key @@ -29,22 +29,22 @@ defmodule AuthWeb.ApikeyControllerTest do test "decode_decrypt/1 reverses the operation of encrypt_encode/1" do person_id = 4869234521 - key = encrypt_encode(person_id) - id = decode_decrypt(key) + key = AuthWeb.ApikeyController.encrypt_encode(person_id) + id = AuthWeb.ApikeyController.decode_decrypt(key) assert person_id == id end - test "create_api_key/1 creates a DWYL_API_KEY" do + test "create_api_key/1 creates an AUTH_API_KEY" do person_id = 123456789 - key = create_api_key(person_id) + key = AuthWeb.ApikeyController.create_api_key(person_id) assert key =~ "/" end - test "decrypt_api_key/1 decrypts a DWYL_API_KEY" do + test "decrypt_api_key/1 decrypts an AUTH_API_KEY" do person_id = 1234 - key = create_api_key(person_id) # |> IO.inspect() + key = AuthWeb.ApikeyController.create_api_key(person_id) # |> IO.inspect() # IO.inspect(String.length(key), label: "String.length(key)") - decrypted = decrypt_api_key(key) # |> IO.inspect() + decrypted = AuthWeb.ApikeyController.decrypt_api_key(key) # |> IO.inspect() assert decrypted == person_id end @@ -60,7 +60,8 @@ defmodule AuthWeb.ApikeyControllerTest do property "Check a batch of int values can be decoded decode_decrypt/1" do check all(int <- integer()) do - assert decode_decrypt(encrypt_encode(int)) == int + assert AuthWeb.ApikeyController.decode_decrypt( + AuthWeb.ApikeyController.encrypt_encode(int)) == int end end end @@ -76,7 +77,7 @@ defmodule AuthWeb.ApikeyControllerTest do person = Auth.Person.get_person_by_email(@email) conn = AuthPlug.create_jwt_session(conn, %{email: @email, id: person.id}) conn = get(conn, Routes.apikey_path(conn, :index)) - assert html_response(conn, 200) =~ "DWYL_API_KEY" + assert html_response(conn, 200) =~ "Auth API Keys" end end @@ -102,7 +103,7 @@ defmodule AuthWeb.ApikeyControllerTest do assert redirected_to(conn) == Routes.apikey_path(conn, :show, id) conn = get(conn, Routes.apikey_path(conn, :show, id)) - assert html_response(conn, 200) =~ "Your DWYL_API_KEY" + assert html_response(conn, 200) =~ "Your AUTH_API_KEY" end # test "renders errors when data is invalid", %{conn: conn} do @@ -188,21 +189,36 @@ defmodule AuthWeb.ApikeyControllerTest do assert html_response(conn, 404) =~ "not found" end end - # - # describe "delete apikey" do - # setup [:create_apikey] - # - # test "deletes chosen apikey", %{conn: conn, apikey: apikey} do - # conn = delete(conn, Routes.apikey_path(conn, :delete, apikey)) - # assert redirected_to(conn) == Routes.apikey_path(conn, :index) - # assert_error_sent 404, fn -> - # get(conn, Routes.apikey_path(conn, :show, apikey)) - # end - # end - # end - # - # defp create_apikey(_) do - # apikey = fixture(:apikey) - # {:ok, apikey: apikey} - # end + + describe "delete apikey" do + + test "deletes chosen apikey", %{conn: conn} do + + person = Auth.Person.get_person_by_email(@email) + conn = AuthPlug.create_jwt_session(conn, person) + {:ok, key} = %{"name" => "test key", "url" => "http://localhost:4000"} + |> AuthWeb.ApikeyController.make_apikey(person.id) + |> Auth.Apikey.create_apikey() + + conn = delete(conn, Routes.apikey_path(conn, :delete, key)) + assert redirected_to(conn) == Routes.apikey_path(conn, :index) + + assert_error_sent 404, fn -> + get(conn, Routes.apikey_path(conn, :show, key)) + end + end + + test "cannot delete a key belonging to someone else! 404", %{conn: conn} do + wrong_person = Auth.Person.create_person(%{email: "wrongin@gmail.com"}) + conn = AuthPlug.create_jwt_session(conn, wrong_person) + + person = Auth.Person.get_person_by_email(@email) + {:ok, key} = %{"name" => "test key", "url" => "http://localhost:4000"} + |> AuthWeb.ApikeyController.make_apikey(person.id) + |> Auth.Apikey.create_apikey() + + conn = delete(conn, Routes.apikey_path(conn, :delete, key)) + assert html_response(conn, 404) =~ "not found" + end + end end From 8e71db67da523493affb4e6236f2163ae39453ce Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 30 Apr 2020 15:36:32 +0100 Subject: [PATCH 138/189] re-style layout for edit form https://github.com/dwyl/auth/issues/51#issuecomment-621893590 --- lib/auth_web/templates/apikey/edit.html.eex | 10 ++++++++- lib/auth_web/templates/apikey/form.html.eex | 23 +++++++++++--------- lib/auth_web/templates/apikey/index.html.eex | 8 +++---- lib/auth_web/templates/layout/nav.html.eex | 5 +++-- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/lib/auth_web/templates/apikey/edit.html.eex b/lib/auth_web/templates/apikey/edit.html.eex index 1d9b06c7..c1ff9165 100644 --- a/lib/auth_web/templates/apikey/edit.html.eex +++ b/lib/auth_web/templates/apikey/edit.html.eex @@ -1,5 +1,13 @@ +

+

Edit Apikey

<%= render "form.html", Map.put(assigns, :action, Routes.apikey_path(@conn, :update, @apikey)) %> -<%= link "Back", to: Routes.apikey_path(@conn, :index) %> +
+<%= link "< Back", to: Routes.apikey_path(@conn, :index), +class: "pointer br2 ba b--orange bg-gold white pa3 ml1 mv1 f4 +shadow-hover bg-animate hover-bg-orange border-box no-underline" + %> + +
diff --git a/lib/auth_web/templates/apikey/form.html.eex b/lib/auth_web/templates/apikey/form.html.eex index 8561aec0..be0864b7 100644 --- a/lib/auth_web/templates/apikey/form.html.eex +++ b/lib/auth_web/templates/apikey/form.html.eex @@ -5,19 +5,22 @@
<% end %> - <%= label f, :name %> - <%= text_input f, :name %> + <%= label f, :name%> + <%= text_input f, :name, class: "db w-100 mt2 pa2 ba b--dark-grey"%> <%= error_tag f, :name %> - - <%= label f, :description %> - <%= textarea f, :description %> +
+ <%= label f, :description, class: "mt3" %> + <%= textarea f, :description, class: "db w-100 mt2 pa2 ba b--dark-grey" %> <%= error_tag f, :description %> - - <%= label f, :url %> - <%= text_input f, :url %> +
+ <%= label f, :url, class: "pt3" %> + <%= text_input f, :url, class: "db w-100 mt2 pa2 ba b--dark-grey" %> <%= error_tag f, :url %> -
- <%= submit "Save" %> +
+ <%= submit "Save", + class: "pointer fr br2 ba b--dark-green bg-green white pa3 ml1 mv1 f4 + shadow-hover bg-animate hover-bg-dark-green border-box no-underline" + %>
<% end %> diff --git a/lib/auth_web/templates/apikey/index.html.eex b/lib/auth_web/templates/apikey/index.html.eex index 4c8d22c1..286d7fb7 100644 --- a/lib/auth_web/templates/apikey/index.html.eex +++ b/lib/auth_web/templates/apikey/index.html.eex @@ -1,9 +1,9 @@ -

Auth API Keys

+

Auth API Keys


- +
- + @@ -47,7 +47,7 @@ <%= link "New Apikey", to: Routes.apikey_path(@conn, :new), -class: "fr mr4 pointer br2 ba b--dark-green bg-green white pa3 ml1 mv1 f4 shadow-hover +class: "center mr5 pointer br2 ba b--dark-green bg-green white pa3 ml1 mv1 f2 shadow-hover bg-animate hover-bg-dark-green border-box no-underline" %> diff --git a/lib/auth_web/templates/layout/nav.html.eex b/lib/auth_web/templates/layout/nav.html.eex index 67a8e974..dce5099e 100644 --- a/lib/auth_web/templates/layout/nav.html.eex +++ b/lib/auth_web/templates/layout/nav.html.eex @@ -22,11 +22,12 @@
  • Contact
  • - --> -
  • + +
  • From 38944242f96a5cad112a7736549ee1e30c88638a Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 30 Apr 2020 15:49:53 +0100 Subject: [PATCH 139/189] enable view button/link in index template #51 --- lib/auth_web/templates/apikey/index.html.eex | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/auth_web/templates/apikey/index.html.eex b/lib/auth_web/templates/apikey/index.html.eex index 286d7fb7..b9c543b1 100644 --- a/lib/auth_web/templates/apikey/index.html.eex +++ b/lib/auth_web/templates/apikey/index.html.eex @@ -21,14 +21,14 @@
  • + + <% end %> + +
    AUTH_API_KEY Name Description<%= apikey.url %> - + <%= link "Edit", to: Routes.apikey_path(@conn, :edit, apikey), - class: "pointer br2 ba b--dark-blue bg-blue white pa3 ml1 mv1 f4 - bg-animate hover-bg-dark-blue border-box no-underline" + class: "pointer br2 ba b--orange bg-gold white pa3 ml1 mv1 f4 + bg-animate hover-bg-orange border-box no-underline" %> <%= link "Delete", to: Routes.apikey_path(@conn, :delete, apikey), method: :delete, @@ -47,8 +47,8 @@ <%= link "New Apikey", to: Routes.apikey_path(@conn, :new), -class: "center mr5 pointer br2 ba b--dark-green bg-green white pa3 ml1 mv1 f2 shadow-hover -bg-animate hover-bg-dark-green border-box no-underline" %> +class: "center mr5 pointer br2 ba b--dark-green bg-green white pa3 ml1 mv1 f3 +shadow-hover bg-animate hover-bg-dark-green border-box no-underline" %> From 6712d49d3f3ed30fc571f5ed502a05b54916057d Mon Sep 17 00:00:00 2001 From: nelsonic Date: Fri, 1 May 2020 23:37:15 +0100 Subject: [PATCH 148/189] add state (referer + JWT) prop to login/register form for #63 --- lib/auth_web/controllers/page_controller.ex | 3 ++- lib/auth_web/templates/page/index.html.eex | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index 04b14580..bf1e4223 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -16,7 +16,8 @@ defmodule AuthWeb.PageController do render(conn, "index.html", oauth_github_url: oauth_github_url, oauth_google_url: oauth_google_url, - changeset: changeset + changeset: changeset, + state: state ) end diff --git a/lib/auth_web/templates/page/index.html.eex b/lib/auth_web/templates/page/index.html.eex index 899836cb..abdaf3ad 100644 --- a/lib/auth_web/templates/page/index.html.eex +++ b/lib/auth_web/templates/page/index.html.eex @@ -48,11 +48,13 @@

    Or use your email address:

    <%= form_for @changeset, @action, fn f -> %> - <%= text_input f, :email, class: + <%= email_input f, :email, class: "db w-100 mt2 pa2 ba b--dark-grey f3", placeholder: "your@email.com"%> <%= error_tag f, :email %>
    + <%= hidden_input f, :state, id: "state", value: @state %> + <%= submit "Login / Register", class: "pointer br2 ba dwyl-bg-mint white pa3 ml1 mv1 f3 From 75af93885261a5989a9b43f8df1269f04cc04193 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Fri, 1 May 2020 23:50:14 +0100 Subject: [PATCH 149/189] add test for login_register_handler/2 #63 --- test/auth_web/controllers/auth_controller_test.exs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index ac0f5621..db2782fd 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -61,5 +61,12 @@ defmodule AuthWeb.AuthControllerTest do assert html_response(conn, 401) =~ "invalid" end - + test "login_register_handler/2", %{conn: conn} do + invalid_key = String.slice(AuthPlug.Token.client_id(), 0..-2) + conn = get(conn, "/auth/google/callback", + %{code: "234", state: "www.example.com/" <> + "&auth_client_id=" <> invalid_key }) + # assert html_response(conn, 200) =~ "google account" + assert html_response(conn, 401) =~ "invalid" + end end From f0da368de17572ae9494ff3513ada85c76455c3b Mon Sep 17 00:00:00 2001 From: nelsonic Date: Fri, 1 May 2020 23:53:40 +0100 Subject: [PATCH 150/189] post(conn, "/people/register" ... #63 --- test/auth_web/controllers/auth_controller_test.exs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index db2782fd..002697d0 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -62,11 +62,11 @@ defmodule AuthWeb.AuthControllerTest do end test "login_register_handler/2", %{conn: conn} do - invalid_key = String.slice(AuthPlug.Token.client_id(), 0..-2) - conn = get(conn, "/auth/google/callback", - %{code: "234", state: "www.example.com/" <> - "&auth_client_id=" <> invalid_key }) + conn = post(conn, "/people/register", + %{email: "jimmy@dwyl.com", state: "www.example.com/" <> + "&auth_client_id=" <> AuthPlug.Token.client_id() }) + IO.inspect(conn) # assert html_response(conn, 200) =~ "google account" - assert html_response(conn, 401) =~ "invalid" + # assert html_response(conn, 401) =~ "invalid" end end From 250e8bc23d1e817634bf6bc2d346947cc25c03d9 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Sat, 2 May 2020 07:54:54 +0100 Subject: [PATCH 151/189] mobile styling for email login form https://github.com/dwyl/auth/issues/63#issuecomment-622736454 --- lib/auth_web/templates/layout/app.html.eex | 2 +- lib/auth_web/templates/page/index.html.eex | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/auth_web/templates/layout/app.html.eex b/lib/auth_web/templates/layout/app.html.eex index 9e63268d..6fa51288 100644 --- a/lib/auth_web/templates/layout/app.html.eex +++ b/lib/auth_web/templates/layout/app.html.eex @@ -13,7 +13,7 @@ <%= render "nav.html", assigns %> -
    +
    <%= render @view_module, @view_template, assigns %> diff --git a/lib/auth_web/templates/page/index.html.eex b/lib/auth_web/templates/page/index.html.eex index abdaf3ad..4a2334f0 100644 --- a/lib/auth_web/templates/page/index.html.eex +++ b/lib/auth_web/templates/page/index.html.eex @@ -1,8 +1,8 @@ -
    +

    Please Sign in to Continue

    -

    Use your GitHub or Google Account:

    +

    Use GitHub or Google:

    -
    +
    -

    Or use your email address:

    +

    Or your email address:

    <%= form_for @changeset, @action, fn f -> %> <%= email_input f, :email, class: - "db w-100 mt2 pa2 ba b--dark-grey f3", + "db w-100 mt2 pa3 ba b--dark-grey f3", placeholder: "your@email.com"%> <%= error_tag f, :email %>
    @@ -57,14 +57,17 @@ <%= submit "Login / Register", - class: "pointer br2 ba dwyl-bg-mint white pa3 ml1 mv1 f3 - shadow-hover bg-animate hover-bg-dark-green border-box no-underline db", + class: "w-100 pointer ba border-box dwyl-bg-mint white pa3 ml1 mv1 f3 + shadow-hover bg-animate hover-dwyl-teal-darkest no-underline db", style: "margin:0 auto; border-color: #318d7b;" %> <% end %> From 76d1944df948feeb83c48dcec24ac46c25fafa67 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Sat, 2 May 2020 08:38:01 +0100 Subject: [PATCH 152/189] reorg controllers for clarity --- lib/auth_web/controllers/auth_controller.ex | 78 +++++++++++++++++++-- lib/auth_web/controllers/page_controller.ex | 56 +-------------- 2 files changed, 75 insertions(+), 59 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index a19fedac..01c834e3 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -2,6 +2,59 @@ defmodule AuthWeb.AuthController do use AuthWeb, :controller alias Auth.Person + def index(conn, params) do + email = Map.get(params, "email") + state = get_referer(conn) + + oauth_github_url = ElixirAuthGithub.login_url(%{scopes: ["user:email"], + state: state}) + oauth_google_url = ElixirAuthGoogle.generate_oauth_url(conn, state) + + conn + |> assign(:action, Routes.auth_path(conn, :login_register_handler)) + |> render("index.html", + oauth_github_url: oauth_github_url, + oauth_google_url: oauth_google_url, + changeset: Auth.Person.login_register_changeset(%{email: email}), + state: state + ) + end + + def append_client_id(ref, client_id) do + ref <> "?auth_client_id=" <> client_id + end + + def get_referer(conn) do + # https://stackoverflow.com/questions/37176911/get-http-referrer + case List.keyfind(conn.req_headers, "referer", 0) do + {"referer", referer} -> + referer + + nil -> # referer not in headers, check URL query: + case conn.query_string =~ "referer" do + true -> + query = URI.decode_query(conn.query_string) + ref = Map.get(query, "referer") + client_id = get_client_id_from_query(conn) + ref |> URI.encode |> append_client_id(client_id) + + false -> # no referer, redirect back to Auth app. + AuthPlug.Helpers.get_baseurl_from_conn(conn) <> "/profile" + |> URI.encode + |> append_client_id(AuthPlug.Token.client_id()) + end + end + end + + def get_client_id_from_query(conn) do + case conn.query_string =~ "auth_client_id" do + true -> + Map.get(URI.decode_query(conn.query_string), "auth_client_id") + false -> # no client_id, redirect back to this app. + 0 + end + end + @doc """ `github_auth/2` handles the callback from GitHub Auth API redirect. """ @@ -30,13 +83,30 @@ defmodule AuthWeb.AuthController do handler(conn, person, state) end + + @doc """ + `login_register_handler/2` is a hybrid of traditional registration and login. + If the person has already registered, we treat it as a login attempt and + present them with a password field/form to complete. + If the person does *not* exist (or has not yet verified their email address), + we show them a welcome screen informing them that a verification email + was sent to their address. When they click it they will see a password (reset) + form where they can define a new password for their account. + """ def login_register_handler(conn, params) do IO.inspect(params, label: "params") + email = Map.get(params, "email") + state = Map.get(params, "state") - conn - |> put_resp_content_type("text/html") - |> send_resp(200, "login_register_handler") - |> halt() + # email is blank or invalid: + if is_nil(email) or not Fields.Validate.email(email) do + index(conn, params) + else + conn + |> put_resp_content_type("text/html") + |> send_resp(200, "login_register_handler") + |> halt() + end end diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index bf1e4223..fcda1af0 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -1,65 +1,11 @@ defmodule AuthWeb.PageController do use AuthWeb, :controller - def index(conn, _params) do - state = get_referer(conn) - - oauth_github_url = ElixirAuthGithub.login_url(%{scopes: ["user:email"], state: state}) - oauth_google_url = ElixirAuthGoogle.generate_oauth_url(conn, state) - - changeset = Auth.Person.login_register_changeset() - # Map.put(assigns, :action, - # Routes.apikey_path(@conn, :create)) - conn = assign(conn, :action, - Routes.auth_path(conn, :login_register_handler)) - - render(conn, "index.html", - oauth_github_url: oauth_github_url, - oauth_google_url: oauth_google_url, - changeset: changeset, - state: state - ) - end - # https://github.com/dwyl/auth/issues/46 def admin(conn, _params) do conn |> put_view(AuthWeb.PageView) |> render(:welcome) end - - def append_client_id(ref, client_id) do - ref <> "?auth_client_id=" <> client_id - end - - def get_referer(conn) do - # https://stackoverflow.com/questions/37176911/get-http-referrer - case List.keyfind(conn.req_headers, "referer", 0) do - {"referer", referer} -> - referer - - nil -> # referer not in headers, check URL query: - case conn.query_string =~ "referer" do - true -> - query = URI.decode_query(conn.query_string) - ref = Map.get(query, "referer") - client_id = get_client_id_from_query(conn) - ref |> URI.encode |> append_client_id(client_id) - - false -> # no referer, redirect back to Auth app. - AuthPlug.Helpers.get_baseurl_from_conn(conn) <> "/profile" - |> URI.encode - |> append_client_id(AuthPlug.Token.client_id()) - end - end - end - - def get_client_id_from_query(conn) do - case conn.query_string =~ "auth_client_id" do - true -> - Map.get(URI.decode_query(conn.query_string), "auth_client_id") - false -> # no client_id, redirect back to this app. - 0 - end - end + end From 9c7a03b9c2a6f614f076b266dbac7eb436e962a9 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Sat, 2 May 2020 08:38:32 +0100 Subject: [PATCH 153/189] create auth templates #42 --- lib/auth_web/templates/apikey/edit.html.eex | 3 +- lib/auth_web/templates/auth/index.html.eex | 77 ++++++++++++++++++++ lib/auth_web/templates/auth/welcome.html.eex | 11 +++ lib/auth_web/templates/page/index.html.eex | 14 ++-- 4 files changed, 97 insertions(+), 8 deletions(-) create mode 100644 lib/auth_web/templates/auth/index.html.eex create mode 100644 lib/auth_web/templates/auth/welcome.html.eex diff --git a/lib/auth_web/templates/apikey/edit.html.eex b/lib/auth_web/templates/apikey/edit.html.eex index c1ff9165..2509a843 100644 --- a/lib/auth_web/templates/apikey/edit.html.eex +++ b/lib/auth_web/templates/apikey/edit.html.eex @@ -2,7 +2,8 @@

    Edit Apikey

    -<%= render "form.html", Map.put(assigns, :action, Routes.apikey_path(@conn, :update, @apikey)) %> +<%= render "form.html", + Map.put(assigns, :action, Routes.apikey_path(@conn, :update, @apikey)) %>
    <%= link "< Back", to: Routes.apikey_path(@conn, :index), diff --git a/lib/auth_web/templates/auth/index.html.eex b/lib/auth_web/templates/auth/index.html.eex new file mode 100644 index 00000000..4a2334f0 --- /dev/null +++ b/lib/auth_web/templates/auth/index.html.eex @@ -0,0 +1,77 @@ +
    +

    Please Sign in to Continue

    +

    Use GitHub or Google:

    + +
    + + +
    + + + +
    +
    + Sign in with GitHub +
    +
    + + +
    + + + + + + +
    +
    + Sign in with Google +
    +
    +
    + +
    +

    Or your email address:

    + + <%= form_for @changeset, @action, fn f -> %> + <%= email_input f, :email, class: + "db w-100 mt2 pa3 ba b--dark-grey f3", + placeholder: "your@email.com"%> + <%= error_tag f, :email %> +
    + <%= hidden_input f, :state, id: "state", value: @state %> + + + <%= submit "Login / Register", + class: "w-100 pointer ba border-box dwyl-bg-mint white pa3 ml1 mv1 f3 + shadow-hover bg-animate hover-dwyl-teal-darkest no-underline db", + style: "margin:0 auto; border-color: #318d7b;" + %> + <% end %> + + +
    +
    + + diff --git a/lib/auth_web/templates/auth/welcome.html.eex b/lib/auth_web/templates/auth/welcome.html.eex new file mode 100644 index 00000000..a94da05d --- /dev/null +++ b/lib/auth_web/templates/auth/welcome.html.eex @@ -0,0 +1,11 @@ +
    +

    Welcome <%= @conn.assigns.person.givenName %>! + +

    +

    You are signed in + with your <%= @conn.assigns.person.auth_provider %> account
    + +

    +

    + +API Keys diff --git a/lib/auth_web/templates/page/index.html.eex b/lib/auth_web/templates/page/index.html.eex index 4a2334f0..d0064e53 100644 --- a/lib/auth_web/templates/page/index.html.eex +++ b/lib/auth_web/templates/page/index.html.eex @@ -62,13 +62,13 @@ style: "margin:0 auto; border-color: #318d7b;" %> <% end %> -
    From 6386a2f78ea4940d750605b93270e87419eb512e Mon Sep 17 00:00:00 2001 From: nelsonic Date: Sat, 2 May 2020 08:50:19 +0100 Subject: [PATCH 154/189] reorg to remove page_controller.ex (which does not make senses if we dont have pages!) --- lib/auth/person.ex | 4 +- lib/auth_web/controllers/auth_controller.ex | 7 +- lib/auth_web/controllers/page_controller.ex | 8 +- lib/auth_web/router.ex | 4 +- lib/auth_web/templates/auth/welcome.html.eex | 2 +- lib/auth_web/templates/page/admin.html.eex | 1 - lib/auth_web/templates/page/index.html.eex | 77 ------------------- lib/auth_web/templates/page/welcome.html.eex | 11 --- lib/auth_web/views/auth_view.ex | 3 + .../controllers/auth_controller_test.exs | 55 ++++++++++++- .../controllers/page_controller_test.exs | 60 --------------- 11 files changed, 68 insertions(+), 164 deletions(-) delete mode 100644 lib/auth_web/templates/page/admin.html.eex delete mode 100644 lib/auth_web/templates/page/index.html.eex delete mode 100644 lib/auth_web/templates/page/welcome.html.eex create mode 100644 lib/auth_web/views/auth_view.ex diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 5a866ba3..415d007d 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -64,9 +64,9 @@ defmodule Auth.Person do end end - def login_register_changeset() do + def login_register_changeset(attrs) do %Person{} - |> cast(%{}, [:email]) + |> cast(attrs, [:email]) end @doc """ diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 01c834e3..c10c12bb 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -2,6 +2,12 @@ defmodule AuthWeb.AuthController do use AuthWeb, :controller alias Auth.Person + # https://github.com/dwyl/auth/issues/46 + def admin(conn, _params) do + conn + |> render(:welcome) + end + def index(conn, params) do email = Map.get(params, "email") state = get_referer(conn) @@ -148,7 +154,6 @@ defmodule AuthWeb.AuthController do false -> # display welcome page on Auth site: conn - |> put_view(AuthWeb.PageView) |> AuthPlug.create_jwt_session(person) |> render(:welcome, person: person) end diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex index fcda1af0..0810677b 100644 --- a/lib/auth_web/controllers/page_controller.ex +++ b/lib/auth_web/controllers/page_controller.ex @@ -1,11 +1,5 @@ defmodule AuthWeb.PageController do use AuthWeb, :controller - # https://github.com/dwyl/auth/issues/46 - def admin(conn, _params) do - conn - |> put_view(AuthWeb.PageView) - |> render(:welcome) - end - + end diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index cefb64e2..f5f3d22f 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -16,7 +16,7 @@ defmodule AuthWeb.Router do scope "/", AuthWeb do pipe_through :browser - get "/", PageController, :index + get "/", AuthController, :index get "/auth/github/callback", AuthController, :github_handler get "/auth/google/callback", AuthController, :google_handler post "/people/register", AuthController, :login_register_handler @@ -31,7 +31,7 @@ defmodule AuthWeb.Router do pipe_through :browser pipe_through :auth - get "/profile", PageController, :admin + get "/profile", AuthController, :admin resources "/settings/apikeys", ApikeyController end diff --git a/lib/auth_web/templates/auth/welcome.html.eex b/lib/auth_web/templates/auth/welcome.html.eex index a94da05d..5d3fdb94 100644 --- a/lib/auth_web/templates/auth/welcome.html.eex +++ b/lib/auth_web/templates/auth/welcome.html.eex @@ -1,4 +1,4 @@ -
    +

    Welcome <%= @conn.assigns.person.givenName %>!

    diff --git a/lib/auth_web/templates/page/admin.html.eex b/lib/auth_web/templates/page/admin.html.eex deleted file mode 100644 index d40c9396..00000000 --- a/lib/auth_web/templates/page/admin.html.eex +++ /dev/null @@ -1 +0,0 @@ -Login diff --git a/lib/auth_web/templates/page/index.html.eex b/lib/auth_web/templates/page/index.html.eex deleted file mode 100644 index d0064e53..00000000 --- a/lib/auth_web/templates/page/index.html.eex +++ /dev/null @@ -1,77 +0,0 @@ -
    -

    Please Sign in to Continue

    -

    Use GitHub or Google:

    - -
    - - -
    - - - -
    -
    - Sign in with GitHub -
    -
    - - -
    - - - - - - -
    -
    - Sign in with Google -
    -
    -
    - -
    -

    Or your email address:

    - - <%= form_for @changeset, @action, fn f -> %> - <%= email_input f, :email, class: - "db w-100 mt2 pa3 ba b--dark-grey f3", - placeholder: "your@email.com"%> - <%= error_tag f, :email %> -
    - <%= hidden_input f, :state, id: "state", value: @state %> - - - <%= submit "Login / Register", - class: "w-100 pointer ba border-box dwyl-bg-mint white pa3 ml1 mv1 f3 - shadow-hover bg-animate hover-dwyl-teal-darkest no-underline db", - style: "margin:0 auto; border-color: #318d7b;" - %> - <% end %> - - -
    -
    - - diff --git a/lib/auth_web/templates/page/welcome.html.eex b/lib/auth_web/templates/page/welcome.html.eex deleted file mode 100644 index a94da05d..00000000 --- a/lib/auth_web/templates/page/welcome.html.eex +++ /dev/null @@ -1,11 +0,0 @@ -
    -

    Welcome <%= @conn.assigns.person.givenName %>! - -

    -

    You are signed in - with your <%= @conn.assigns.person.auth_provider %> account
    - -

    -

    - -API Keys diff --git a/lib/auth_web/views/auth_view.ex b/lib/auth_web/views/auth_view.ex new file mode 100644 index 00000000..48476518 --- /dev/null +++ b/lib/auth_web/views/auth_view.ex @@ -0,0 +1,3 @@ +defmodule AuthWeb.AuthView do + use AuthWeb, :view +end diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 002697d0..4642bedc 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -2,6 +2,48 @@ defmodule AuthWeb.AuthControllerTest do use AuthWeb.ConnCase # @email System.get_env("ADMIN_EMAIL") + test "GET /", %{conn: conn} do + conn = get(conn, "/") + assert html_response(conn, 200) =~ "Sign in" + end + + test "GET /profile (without valid session should redirect)", %{conn: conn} do + conn = get(conn, "/profile") + # assert html_response(conn, 301) =~ "Login" + assert conn.status == 302 + end + + test "admin/2 show welcome page", %{conn: conn} do + data = %{ + email: "nelson@gmail.com", + givenName: "McTestin", + picture: "https://youtu.be/naoknj1ebqI", + auth_provider: "google" + } + person = Auth.Person.create_person(data) # |> IO.inspect(label: "person") + conn = AuthPlug.create_jwt_session(conn, Map.merge(data, %{id: person.id})) + conn = get(conn, "/profile", %{}) + assert html_response(conn, 200) =~ "google account" + # assert html_response(conn, 302) =~ "redirected" + end + + test "get_referer/1", %{conn: conn} do + conn = conn + |> put_req_header("referer", "http://localhost/admin") + |> get("/") + + assert conn.resp_body =~ "state=http://localhost/admin" + end + + test "get_referer/1 query_string", %{conn: conn} do + conn = conn + |> get("/?referer=" <> URI.encode("http://localhost/admin") + <> "&auth_client_id=" <> AuthPlug.Token.client_id() + ) + + assert conn.resp_body =~ "state=http://localhost/admin" + end + test "github_handler/2 github auth callback", %{conn: conn} do conn = get(conn, "/auth/github/callback", %{code: "123", state: "http://localhost:4000/" <> @@ -61,12 +103,21 @@ defmodule AuthWeb.AuthControllerTest do assert html_response(conn, 401) =~ "invalid" end + test "login_register_handler/2 with invalid email", %{conn: conn} do + conn = post(conn, "/people/register", + %{email: "invalid", state: "www.example.com/" <> + "&auth_client_id=" <> AuthPlug.Token.client_id() }) + IO.inspect(conn) + # assert html_response(conn, 200) =~ "email" + # assert html_response(conn, 401) =~ "invalid" + end + test "login_register_handler/2", %{conn: conn} do conn = post(conn, "/people/register", %{email: "jimmy@dwyl.com", state: "www.example.com/" <> "&auth_client_id=" <> AuthPlug.Token.client_id() }) - IO.inspect(conn) - # assert html_response(conn, 200) =~ "google account" + # IO.inspect(conn) + # assert html_response(conn, 200) =~ "email" # assert html_response(conn, 401) =~ "invalid" end end diff --git a/test/auth_web/controllers/page_controller_test.exs b/test/auth_web/controllers/page_controller_test.exs index b6aa2bde..46016d66 100644 --- a/test/auth_web/controllers/page_controller_test.exs +++ b/test/auth_web/controllers/page_controller_test.exs @@ -1,63 +1,3 @@ defmodule AuthWeb.PageControllerTest do use AuthWeb.ConnCase - - test "GET /", %{conn: conn} do - conn = get(conn, "/") - assert html_response(conn, 200) =~ "Sign in" - end - - test "GET /profile", %{conn: conn} do - conn = get(conn, "/profile") - # assert html_response(conn, 301) =~ "Login" - assert conn.status == 302 - end - - test "get_referer/1", %{conn: conn} do - conn = conn - |> put_req_header("referer", "http://localhost/admin") - |> get("/") - - assert conn.resp_body =~ "state=http://localhost/admin" - end - - test "get_referer/1 query_string", %{conn: conn} do - conn = conn - |> get("/?referer=" <> URI.encode("http://localhost/admin") - <> "&auth_client_id=" <> AuthPlug.Token.client_id() - ) - - assert conn.resp_body =~ "state=http://localhost/admin" - end - - test "google_handler/2 show welcome (state=nil) > handler/3", %{conn: conn} do - # IO.inspect(System.get_env("AUTH_API_KEY"), label: "AUTH_API_KEY") - # IO.inspect(AuthPlug.Token.client_id(), label: "AuthPlug.Token.client_id()") - # data = %{ - # email: "nelson@gmail.com", - # givenName: "McTestin", - # picture: "https://youtu.be/naoknj1ebqI", - # auth_provider: "google" - # } - # person = Auth.Person.create_person(data) |> IO.inspect(label: "person") - # conn = AuthPlug.create_jwt_session(conn, Map.merge(data, %{id: person.id})) - # |> IO.inspect(label: "conn") - conn = get(conn, "/auth/google/callback", %{code: "234", state: nil}) - - assert html_response(conn, 200) =~ "google account" - # assert html_response(conn, 302) =~ "redirected" - end - - test "admin/2 show welcome page", %{conn: conn} do - data = %{ - email: "nelson@gmail.com", - givenName: "McTestin", - picture: "https://youtu.be/naoknj1ebqI", - auth_provider: "google" - } - person = Auth.Person.create_person(data) # |> IO.inspect(label: "person") - conn = AuthPlug.create_jwt_session(conn, Map.merge(data, %{id: person.id})) - conn = get(conn, "/profile", %{}) - assert html_response(conn, 200) =~ "google account" - # assert html_response(conn, 302) =~ "redirected" - end end From 4d676eadbea478a515ef0b4f6b31879430b0c43e Mon Sep 17 00:00:00 2001 From: nelsonic Date: Sat, 2 May 2020 08:52:16 +0100 Subject: [PATCH 155/189] remove all references to "page" from project as irrelevant #42 --- lib/auth_web/controllers/page_controller.ex | 5 ----- lib/auth_web/views/page_view.ex | 3 --- test/auth_web/controllers/page_controller_test.exs | 3 --- 3 files changed, 11 deletions(-) delete mode 100644 lib/auth_web/controllers/page_controller.ex delete mode 100644 lib/auth_web/views/page_view.ex delete mode 100644 test/auth_web/controllers/page_controller_test.exs diff --git a/lib/auth_web/controllers/page_controller.ex b/lib/auth_web/controllers/page_controller.ex deleted file mode 100644 index 0810677b..00000000 --- a/lib/auth_web/controllers/page_controller.ex +++ /dev/null @@ -1,5 +0,0 @@ -defmodule AuthWeb.PageController do - use AuthWeb, :controller - - -end diff --git a/lib/auth_web/views/page_view.ex b/lib/auth_web/views/page_view.ex deleted file mode 100644 index f9a7c32a..00000000 --- a/lib/auth_web/views/page_view.ex +++ /dev/null @@ -1,3 +0,0 @@ -defmodule AuthWeb.PageView do - use AuthWeb, :view -end diff --git a/test/auth_web/controllers/page_controller_test.exs b/test/auth_web/controllers/page_controller_test.exs deleted file mode 100644 index 46016d66..00000000 --- a/test/auth_web/controllers/page_controller_test.exs +++ /dev/null @@ -1,3 +0,0 @@ -defmodule AuthWeb.PageControllerTest do - use AuthWeb.ConnCase -end From 90731ace9306a2017a525c529510467a85edaa1d Mon Sep 17 00:00:00 2001 From: nelsonic Date: Sat, 2 May 2020 09:11:32 +0100 Subject: [PATCH 156/189] tidy up welcome template see: https://github.com/dwyl/auth/pull/43#issuecomment-622845002 --- assets/css/app.css | 9 ++++++++- lib/auth_web/templates/auth/index.html.eex | 9 --------- lib/auth_web/templates/auth/welcome.html.eex | 20 +++++++++++++------- lib/auth_web/templates/layout/app.html.eex | 2 +- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/assets/css/app.css b/assets/css/app.css index fec0b3fc..c3cf4bc6 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -1,3 +1,10 @@ /* This file is for your main application css. */ -@import "./phoenix.css"; +/* @import "./phoenix.css"; */ + +.dwyl-bg-mint { + background-color: #339999; +} +.hover-dwyl-teal-darkest:hover, .hover-dwyl-teal-darkest:focus { + background-color: #318d7b; +} diff --git a/lib/auth_web/templates/auth/index.html.eex b/lib/auth_web/templates/auth/index.html.eex index 4a2334f0..17eddd7e 100644 --- a/lib/auth_web/templates/auth/index.html.eex +++ b/lib/auth_web/templates/auth/index.html.eex @@ -62,15 +62,6 @@ style: "margin:0 auto; border-color: #318d7b;" %> <% end %> - -
    diff --git a/lib/auth_web/templates/auth/welcome.html.eex b/lib/auth_web/templates/auth/welcome.html.eex index 5d3fdb94..ec621175 100644 --- a/lib/auth_web/templates/auth/welcome.html.eex +++ b/lib/auth_web/templates/auth/welcome.html.eex @@ -1,11 +1,17 @@ -
    +

    Welcome <%= @conn.assigns.person.givenName %>! -

    -

    You are signed in - with your <%= @conn.assigns.person.auth_provider %> account
    - + +

    You are signed in + with your <%= String.capitalize(@conn.assigns.person.auth_provider) %> account.

    -

    -API Keys +
    + + Manage API Keys + +
    diff --git a/lib/auth_web/templates/layout/app.html.eex b/lib/auth_web/templates/layout/app.html.eex index 6fa51288..91ffa65d 100644 --- a/lib/auth_web/templates/layout/app.html.eex +++ b/lib/auth_web/templates/layout/app.html.eex @@ -5,7 +5,7 @@ <%= assigns[:page_title] || "Auth · Phoenix Framework" %> - + "/> <%= csrf_meta_tag() %> From 9a30bbf95c4cd93f149628473bd744c43dccc197 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Sat, 2 May 2020 09:17:54 +0100 Subject: [PATCH 157/189] rework styles in apikeys/index template #42 --- lib/auth_web/templates/apikey/index.html.eex | 2 ++ lib/auth_web/templates/auth/welcome.html.eex | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/auth_web/templates/apikey/index.html.eex b/lib/auth_web/templates/apikey/index.html.eex index b9c543b1..e8a64f48 100644 --- a/lib/auth_web/templates/apikey/index.html.eex +++ b/lib/auth_web/templates/apikey/index.html.eex @@ -1,3 +1,4 @@ +

    Auth API Keys


    @@ -61,3 +62,4 @@ td { padding: 10px; } +
    diff --git a/lib/auth_web/templates/auth/welcome.html.eex b/lib/auth_web/templates/auth/welcome.html.eex index ec621175..3b055518 100644 --- a/lib/auth_web/templates/auth/welcome.html.eex +++ b/lib/auth_web/templates/auth/welcome.html.eex @@ -10,8 +10,7 @@
    + shadow-hover bg-animate hover-dwyl-teal-darkest no-underline"> Manage API Keys
    From 7032b8b308fc41f6c50955ad42d8b4e1884856f6 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Sat, 2 May 2020 09:21:06 +0100 Subject: [PATCH 158/189] fix failing tests due to copy change (capitalise auth_provider) #42 --- test/auth_web/controllers/auth_controller_test.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 4642bedc..0483ab81 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -23,7 +23,7 @@ defmodule AuthWeb.AuthControllerTest do person = Auth.Person.create_person(data) # |> IO.inspect(label: "person") conn = AuthPlug.create_jwt_session(conn, Map.merge(data, %{id: person.id})) conn = get(conn, "/profile", %{}) - assert html_response(conn, 200) =~ "google account" + assert html_response(conn, 200) =~ "Google account" # assert html_response(conn, 302) =~ "redirected" end @@ -90,7 +90,7 @@ defmodule AuthWeb.AuthControllerTest do conn = get(conn, "/auth/google/callback", %{code: "234", state: nil}) - assert html_response(conn, 200) =~ "google account" + assert html_response(conn, 200) =~ "Google account" # assert html_response(conn, 302) =~ "redirected" end From 110f83394a82475629e44f0cb35be233815068d0 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Sun, 3 May 2020 22:37:19 +0100 Subject: [PATCH 159/189] [WiP] debugging sending verification email #62 / #63 --- lib/auth/person.ex | 13 +- lib/auth_web/controllers/auth_controller.ex | 68 +++++++++-- lib/auth_web/templates/apikey/index.html.eex | 119 ++++++++++--------- lib/auth_web/templates/auth/index.html.eex | 3 +- 4 files changed, 126 insertions(+), 77 deletions(-) diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 415d007d..40e4eafd 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -152,17 +152,14 @@ defmodule Auth.Person do end # @doc """ - # Changeset function used for email/password registration - # Define email hash and password hash + # Changeset function used for email registration. # """ - # def changeset_registration(profile, attrs) do - # profile - # |> cast(attrs, [:email, :password]) - # |> validate_required([:email, :password]) - # |> validate_length(:password, min: 6, max: 100) + # def changeset_registration(attrs) do + # %Person{} + # |> cast(attrs, [:email]) + # |> validate_required([:email]) # |> unique_constraint(:email) # |> put_email_hash() - # |> put_pass_hash() # end defp put_email_hash(changeset) do diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index c10c12bb..d8d86e6e 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -9,8 +9,33 @@ defmodule AuthWeb.AuthController do end def index(conn, params) do - email = Map.get(params, "email") - state = get_referer(conn) + params_person = Map.get(params, "person") + email = if not is_nil(params_person) + and not is_nil(Map.get(params_person, "email")) do + Map.get(Map.get(params, "person"), "email") + else + nil + end + + # TODO: add friendly error message when email is invalid + # IO.inspect(Fields.Validate.email(email), label: "Fields.Validate.email(email)") + # errors = if not is_nil(email) and not Fields.Validate.email(email) do + # [email: "email address is invalid"] + # else + # [] + # end + # + # IO.inspect(email, label: "email") + # IO.inspect(errors, label: "errors") + + + state = if not is_nil(params_person) + and not is_nil(Map.get(params_person, "state")) do + Map.get(params_person, "state") + else + get_referer(conn) # get from headers + end + oauth_github_url = ElixirAuthGithub.login_url(%{scopes: ["user:email"], state: state}) @@ -22,7 +47,8 @@ defmodule AuthWeb.AuthController do oauth_github_url: oauth_github_url, oauth_google_url: oauth_google_url, changeset: Auth.Person.login_register_changeset(%{email: email}), - state: state + state: state, + # errors: errors ) end @@ -53,6 +79,7 @@ defmodule AuthWeb.AuthController do end def get_client_id_from_query(conn) do + IO.inspect(conn.query_string, label: "conn.query_string") case conn.query_string =~ "auth_client_id" do true -> Map.get(URI.decode_query(conn.query_string), "auth_client_id") @@ -100,14 +127,32 @@ defmodule AuthWeb.AuthController do form where they can define a new password for their account. """ def login_register_handler(conn, params) do - IO.inspect(params, label: "params") - email = Map.get(params, "email") - state = Map.get(params, "state") - + IO.inspect(params, label: "params:130") + params_person = Map.get(params, "person") + email = Map.get(params_person, "email") + state = Map.get(params_person, "state") + IO.inspect(email, label: "email") # email is blank or invalid: if is_nil(email) or not Fields.Validate.email(email) do - index(conn, params) + conn # re-render the login/register form: + |> index(params) else + # check if the email exists in the people table: + person = case Auth.Person.get_person_by_email(email) do + person -> + person + nil -> + person = Auth.Person.create_person(%{email: email}) + IO.inspect(person, label: "person:146") + Auth.Email.sendemail(%{ + email: email, + link: make_verify_link(conn, person, state) + }) |> IO.inspect(label: "sendemail") + + person + end + IO.inspect(person) + # respond conn |> put_resp_content_type("text/html") |> send_resp(200, "login_register_handler") @@ -115,6 +160,13 @@ defmodule AuthWeb.AuthController do end end + def make_verify_link(conn, person, state) do + AuthPlug.Helpers.get_baseurl_from_conn(conn) + <> "/person/verify" + <> AuthWeb.ApikeyController.encrypt_encode(person.id) + <> "?" <> state + end + # def email_handler(conn, params) do # diff --git a/lib/auth_web/templates/apikey/index.html.eex b/lib/auth_web/templates/apikey/index.html.eex index e8a64f48..5b9450b8 100644 --- a/lib/auth_web/templates/apikey/index.html.eex +++ b/lib/auth_web/templates/apikey/index.html.eex @@ -1,65 +1,66 @@
    -

    Auth API Keys

    -
    +

    Auth API Keys

    +
    - - - - - - - - - - -<%= for apikey <- @apikeys do %> - - - - - +
    AUTH_API_KEYNameDescriptionUrl
    - <%= apikey.client_id %>/<%= apikey.client_secret %> - <%= apikey.name %><%= apikey.description %><%= apikey.url %>
    + + + + + + + + + + <%= for apikey <- @apikeys do %> + + + + + - - -<% end %> - -
    AUTH_API_KEYNameDescriptionUrl
    + <%= apikey.client_id %>/<%= apikey.client_secret %> + <%= apikey.name %><%= apikey.description %><%= apikey.url %> - <%= link "View", to: Routes.apikey_path(@conn, :show, apikey), - class: "pointer br2 ba b--dark-blue bg-blue white pa3 ml1 mv1 f4 - bg-animate hover-bg-dark-blue border-box no-underline" - %> - - <%= link "Edit", to: Routes.apikey_path(@conn, :edit, apikey), - class: "pointer br2 ba b--orange bg-gold white pa3 ml1 mv1 f4 - bg-animate hover-bg-orange border-box no-underline" - %> - - <%= link "Delete", to: Routes.apikey_path(@conn, :delete, apikey), method: :delete, - class: "pointer br2 ba b--dark-red bg-red white pa3 ml1 mv1 f4 - bg-animate hover-bg-dark-red border-box no-underline", - data: - [confirm: "Are you sure you want to delete this API Key? - (This cannot be undone!)"] %> - -
    -

    +
    + <%= link "View", to: Routes.apikey_path(@conn, :show, apikey), + class: "pointer br2 ba b--dark-blue bg-blue white pa3 ml1 mv1 f4 + bg-animate hover-bg-dark-blue border-box no-underline" + %> + + <%= link "Edit", to: Routes.apikey_path(@conn, :edit, apikey), + class: "pointer br2 ba b--orange bg-gold white pa3 ml1 mv1 f4 + bg-animate hover-bg-orange border-box no-underline" + %> + + <%= link "Delete", to: Routes.apikey_path(@conn, :delete, apikey), + method: :delete, + class: "pointer br2 ba b--dark-red bg-red white pa3 ml1 mv1 f4 + bg-animate hover-bg-dark-red border-box no-underline", + data: + [confirm: "Are you sure you want to delete this API Key? + (This cannot be undone!)"] %> + +
    +

    - -<%= link "New Apikey", to: Routes.apikey_path(@conn, :new), -class: "center mr5 pointer br2 ba b--dark-green bg-green white pa3 ml1 mv1 f3 -shadow-hover bg-animate hover-bg-dark-green border-box no-underline" %> - + + <%= link "New Apikey", to: Routes.apikey_path(@conn, :new), + class: "center mr5 pointer br2 ba b--dark-green bg-green white pa3 ml1 mv1 f3 + shadow-hover bg-animate hover-bg-dark-green border-box no-underline" %> + - +
    diff --git a/lib/auth_web/templates/auth/index.html.eex b/lib/auth_web/templates/auth/index.html.eex index 17eddd7e..2397ae07 100644 --- a/lib/auth_web/templates/auth/index.html.eex +++ b/lib/auth_web/templates/auth/index.html.eex @@ -48,8 +48,7 @@

    Or your email address:

    <%= form_for @changeset, @action, fn f -> %> - <%= email_input f, :email, class: - "db w-100 mt2 pa3 ba b--dark-grey f3", + <%= email_input f, :email, class: "db w-100 mt2 pa3 ba b--dark-grey f3", placeholder: "your@email.com"%> <%= error_tag f, :email %>
    From 16050c4e8ed1a8c77c47e057ca853371bbde6674 Mon Sep 17 00:00:00 2001 From: Nelson Date: Mon, 4 May 2020 15:28:07 +0100 Subject: [PATCH 160/189] Update lib/auth_web/controllers/auth_controller.ex Co-authored-by: Simon --- lib/auth_web/controllers/auth_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index c10c12bb..9f03ad57 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -9,7 +9,7 @@ defmodule AuthWeb.AuthController do end def index(conn, params) do - email = Map.get(params, "email") + email = params["email"] state = get_referer(conn) oauth_github_url = ElixirAuthGithub.login_url(%{scopes: ["user:email"], From 7946b729a3ab77bdb22827b03ac640813b538a73 Mon Sep 17 00:00:00 2001 From: Nelson Date: Mon, 4 May 2020 15:28:44 +0100 Subject: [PATCH 161/189] comment out IO.inspect in lib/auth_web/controllers/auth_controller.ex Co-authored-by: Simon --- lib/auth_web/controllers/auth_controller.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 9f03ad57..fafd071b 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -100,7 +100,7 @@ defmodule AuthWeb.AuthController do form where they can define a new password for their account. """ def login_register_handler(conn, params) do - IO.inspect(params, label: "params") + # IO.inspect(params, label: "params") email = Map.get(params, "email") state = Map.get(params, "state") From 97c8b4c2ea6e8c88091b7e4109ee22c35fcfbe6f Mon Sep 17 00:00:00 2001 From: nelsonic Date: Mon, 4 May 2020 21:16:12 +0100 Subject: [PATCH 162/189] [WiP] create verify_email/2 for #63 --- lib/auth/person.ex | 19 ++++- lib/auth_web/controllers/auth_controller.ex | 74 +++++++++++++------ lib/auth_web/router.ex | 3 +- lib/auth_web/templates/auth/index.html.eex | 2 +- .../templates/auth/password-prompt.html.eex | 21 ++++++ 5 files changed, 94 insertions(+), 25 deletions(-) create mode 100644 lib/auth_web/templates/auth/password-prompt.html.eex diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 40e4eafd..4d77912d 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -69,6 +69,11 @@ defmodule Auth.Person do |> cast(attrs, [:email]) end + def password_prompt_changeset(attrs) do + %Person{} + |> cast(attrs, [:email, :password]) + end + @doc """ `transform_github_profile_data_to_person/1` transforms the profile data received from invoking `ElixirAuthGithub.github_auth/1` @@ -167,9 +172,19 @@ defmodule Auth.Person do # |> IO.inspect(label: "changeset with :email_hash") end + def get_status_verified do + status = Auth.Status.upsert_status("verified") + status.id + end + def put_email_status_verified(changeset) do - status_verified = Auth.Status.upsert_status("verified") - put_change(changeset, :status, status_verified.id) + # IO.inspect(changeset, label: "changeset") + provider = changeset.changes.auth_provider + if provider == "google" or provider == "github" do + put_change(changeset, :status, get_status_verified()) + else + changeset + end end # defp put_pass_hash(changeset) do diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index d8d86e6e..ad3480a3 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -134,37 +134,69 @@ defmodule AuthWeb.AuthController do IO.inspect(email, label: "email") # email is blank or invalid: if is_nil(email) or not Fields.Validate.email(email) do - conn # re-render the login/register form: + conn # email invalid, re-render the login/register form: |> index(params) else + IO.puts("email is NOT nil: " <> email) + person = Auth.Person.get_person_by_email(email) + IO.inspect(person, label: "person:142") # check if the email exists in the people table: - person = case Auth.Person.get_person_by_email(email) do - person -> - person - nil -> - person = Auth.Person.create_person(%{email: email}) - IO.inspect(person, label: "person:146") - Auth.Email.sendemail(%{ - email: email, - link: make_verify_link(conn, person, state) - }) |> IO.inspect(label: "sendemail") - - person + person = if is_nil(person) do + person = Auth.Person.create_person(%{ + email: email, + auth_provider: "email" + }) + # IO.inspect(person, label: "person:146") + Auth.Email.sendemail(%{ email: email, template: "verify", + link: make_verify_link(conn, person, state), + subject: "Please Verify Your Email Address" + }) + + person + else + person + end + IO.inspect(person, label: "person:156") + if not is_nil(person.status) and person.status == 1 do # verified + conn + |> assign(:action, Routes.auth_path(conn, :login_register_handler)) + |> render("password-prompt.html", + changeset: Auth.Person.password_prompt_changeset(%{email: email}), + state: state, + person_id: AuthWeb.ApikeyController.encrypt_encode(person.id) # hide + ) + else + # respond + conn + |> put_resp_content_type("text/html") + |> send_resp(200, "login_register_handler " <> email) + |> halt() end - IO.inspect(person) - # respond - conn - |> put_resp_content_type("text/html") - |> send_resp(200, "login_register_handler") - |> halt() end end def make_verify_link(conn, person, state) do AuthPlug.Helpers.get_baseurl_from_conn(conn) - <> "/person/verify" + <> "/auth/verify?id=" <> AuthWeb.ApikeyController.encrypt_encode(person.id) - <> "?" <> state + <> "&referer=" <> state + end + + def verify_email(conn, params) do + IO.inspect(params, label: "params:196") + referer = params["referer"] + IO.inspect(referer, label: "referer:198") + person_id = AuthWeb.ApikeyController.decode_decrypt(params["id"]) + IO.inspect(person_id, label: "person_id:190") + + auth_client_id = get_client_id_from_query(conn) + IO.inspect(auth_client_id, label: "auth_client_id:200") + # ref = get_referer(conn) + # IO.inspect(ref, label: "referer:188") + conn + |> put_resp_content_type("text/html") + |> send_resp(200, "verify_email") + |> halt() end diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index f5f3d22f..d800b2d6 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -19,7 +19,8 @@ defmodule AuthWeb.Router do get "/", AuthController, :index get "/auth/github/callback", AuthController, :github_handler get "/auth/google/callback", AuthController, :google_handler - post "/people/register", AuthController, :login_register_handler + get "/auth/verify", AuthController, :verify_email + post "/auth/register", AuthController, :login_register_handler end diff --git a/lib/auth_web/templates/auth/index.html.eex b/lib/auth_web/templates/auth/index.html.eex index 2397ae07..7541fcb8 100644 --- a/lib/auth_web/templates/auth/index.html.eex +++ b/lib/auth_web/templates/auth/index.html.eex @@ -64,4 +64,4 @@
    - + diff --git a/lib/auth_web/templates/auth/password-prompt.html.eex b/lib/auth_web/templates/auth/password-prompt.html.eex new file mode 100644 index 00000000..fdf5a7e3 --- /dev/null +++ b/lib/auth_web/templates/auth/password-prompt.html.eex @@ -0,0 +1,21 @@ +
    +
    +

    Please Type Your Password:

    + + <%= form_for @changeset, @action, fn f -> %> + <%= password_input f, :password, + class: "db w-100 mt2 pa3 ba b--dark-grey f3"%> + <%= error_tag f, :password %> + + +
    + <%= hidden_input f, :state, id: "state", value: @state %> + + <%= submit "Login", + class: "w-100 pointer ba border-box dwyl-bg-mint white pa3 ml1 mv1 f3 + shadow-hover bg-animate hover-dwyl-teal-darkest no-underline db", + style: "margin:0 auto; border-color: #318d7b;" + %> + <% end %> +
    +
    From 201bc503e84e6ebb68ad4619fb4e73a31fc5717a Mon Sep 17 00:00:00 2001 From: nelsonic Date: Mon, 4 May 2020 21:55:39 +0100 Subject: [PATCH 163/189] fix tests I broke by reworking API for #63 --- lib/auth_web/controllers/auth_controller.ex | 2 +- test/auth/person_test.exs | 2 +- .../controllers/apikey_controller_test.exs | 15 ++++++++++++--- .../controllers/auth_controller_test.exs | 19 +++++++++++-------- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index ad3480a3..41a8d287 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -188,7 +188,7 @@ defmodule AuthWeb.AuthController do IO.inspect(referer, label: "referer:198") person_id = AuthWeb.ApikeyController.decode_decrypt(params["id"]) IO.inspect(person_id, label: "person_id:190") - + auth_client_id = get_client_id_from_query(conn) IO.inspect(auth_client_id, label: "auth_client_id:200") # ref = get_referer(conn) diff --git a/test/auth/person_test.exs b/test/auth/person_test.exs index 5d4ffb0b..697aeb62 100644 --- a/test/auth/person_test.exs +++ b/test/auth/person_test.exs @@ -3,7 +3,7 @@ defmodule Auth.PersonTest do alias Auth.{Person} test "create_person/1" do - alex = %{email: "alex@gmail.com"} + alex = %{email: "alex@gmail.com", auth_provider: "email"} person = Person.create_person(alex) assert person.id > 1 diff --git a/test/auth_web/controllers/apikey_controller_test.exs b/test/auth_web/controllers/apikey_controller_test.exs index 14b1c54f..7edaaec8 100644 --- a/test/auth_web/controllers/apikey_controller_test.exs +++ b/test/auth_web/controllers/apikey_controller_test.exs @@ -131,7 +131,10 @@ defmodule AuthWeb.ApikeyControllerTest do test "attempt to edit a key I don't own > should 404", %{conn: conn} do person = Auth.Person.get_person_by_email(@email) - wrong_person = Auth.Person.create_person(%{email: "wrong@gmail.com"}) + wrong_person = Auth.Person.create_person(%{ + email: "wrong@gmail.com", + auth_provider: "email" + }) conn = AuthPlug.create_jwt_session(conn, wrong_person) {:ok, key} = %{"name" => "test key", "url" => "http://localhost:4000"} @@ -177,7 +180,10 @@ defmodule AuthWeb.ApikeyControllerTest do person = Auth.Person.get_person_by_email(@email) # create session with wrong person: - wrong_person = Auth.Person.create_person(%{email: "wronger@gmail.com"}) + wrong_person = Auth.Person.create_person(%{ + email: "wronger@gmail.com", + auth_provider: "email" + }) conn = AuthPlug.create_jwt_session(conn, wrong_person) {:ok, key} = %{"name" => "test key", @@ -209,7 +215,10 @@ defmodule AuthWeb.ApikeyControllerTest do end test "cannot delete a key belonging to someone else! 404", %{conn: conn} do - wrong_person = Auth.Person.create_person(%{email: "wrongin@gmail.com"}) + wrong_person = Auth.Person.create_person(%{ + email: "wrongin@gmail.com", + auth_provider: "email" + }) conn = AuthPlug.create_jwt_session(conn, wrong_person) person = Auth.Person.get_person_by_email(@email) diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 0483ab81..e7614874 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -104,20 +104,23 @@ defmodule AuthWeb.AuthControllerTest do end test "login_register_handler/2 with invalid email", %{conn: conn} do - conn = post(conn, "/people/register", + conn = post(conn, "/auth/register", %{"person" => %{email: "invalid", state: "www.example.com/" <> - "&auth_client_id=" <> AuthPlug.Token.client_id() }) - IO.inspect(conn) - # assert html_response(conn, 200) =~ "email" + "&auth_client_id=" <> AuthPlug.Token.client_id() } + }) + # IO.inspect(conn) + assert html_response(conn, 200) =~ "email" # re-render the index # assert html_response(conn, 401) =~ "invalid" end - test "login_register_handler/2", %{conn: conn} do - conn = post(conn, "/people/register", + test "login_register_handler/2 with valid email", %{conn: conn} do + conn = post(conn, "/auth/register", %{"person" => %{email: "jimmy@dwyl.com", state: "www.example.com/" <> - "&auth_client_id=" <> AuthPlug.Token.client_id() }) + "&auth_client_id=" <> AuthPlug.Token.client_id() } + }) + # TODO: show password form! # IO.inspect(conn) - # assert html_response(conn, 200) =~ "email" + assert html_response(conn, 200) =~ "login_register_handler" # assert html_response(conn, 401) =~ "invalid" end end From b892d49b2f190d529327076158fddf702ff0b21e Mon Sep 17 00:00:00 2001 From: nelsonic Date: Mon, 4 May 2020 23:49:23 +0100 Subject: [PATCH 164/189] simplify upsert_person (considerably!) https://github.com/dwyl/auth/issues/53 --- lib/auth/person.ex | 32 +++++++++++++++++-- lib/auth_web/controllers/auth_controller.ex | 14 ++++---- .../controllers/auth_controller_test.exs | 5 +-- 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 4d77912d..1d522198 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -32,6 +32,7 @@ defmodule Auth.Person do def changeset(person, attrs) do person |> cast(attrs, [ + :id, :username, :email, :givenName, @@ -106,7 +107,7 @@ defmodule Auth.Person do end def create_github_person(profile) do - transform_github_profile_data_to_person(profile) |> create_person() + transform_github_profile_data_to_person(profile) |> upsert_person() end @doc """ @@ -141,7 +142,7 @@ defmodule Auth.Person do } """ def transform_google_profile_data_to_person(profile) do - # IO.inspect(profile, label: "profile") + # IO.inspect(profile, label: "profile:145") Map.merge(profile, %{ familyName: profile.family_name, givenName: profile.given_name, @@ -152,7 +153,7 @@ defmodule Auth.Person do def create_google_person(profile) do transform_google_profile_data_to_person(profile) - |> create_person() + |> upsert_person() # |> IO.inspect(label: "create_person:") end @@ -168,6 +169,7 @@ defmodule Auth.Person do # end defp put_email_hash(changeset) do + put_change(changeset, :email_hash, changeset.changes.email) # |> IO.inspect(label: "changeset with :email_hash") end @@ -187,6 +189,15 @@ defmodule Auth.Person do end end + def verify_person_by_id(id) do + %{id: id, status: get_status_verified()} |> upsert_person() + end + + def get_person_by_id(id) do + __MODULE__ + |> Repo.get_by(id: id) + end + # defp put_pass_hash(changeset) do # case changeset do # %Ecto.Changeset{valid?: true, changes: %{password: pass}} -> @@ -204,4 +215,19 @@ defmodule Auth.Person do __MODULE__ |> Repo.get_by(email_hash: email) end + + def upsert_person(person) do + # IO.inspect(person.email, label: "person.email:220") + case get_person_by_email(person.email) do + nil -> + create_person(person) + + ep -> # existing person + merged = Map.merge(AuthPlug.Helpers.strip_struct_metadata(ep), person) + {:ok, person} = changeset(%Person{id: ep.id}, merged) + |> Repo.update() + + person + end + end end diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 41a8d287..9551f5e8 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -139,7 +139,7 @@ defmodule AuthWeb.AuthController do else IO.puts("email is NOT nil: " <> email) person = Auth.Person.get_person_by_email(email) - IO.inspect(person, label: "person:142") + # IO.inspect(person, label: "person:142") # check if the email exists in the people table: person = if is_nil(person) do person = Auth.Person.create_person(%{ @@ -156,7 +156,7 @@ defmodule AuthWeb.AuthController do else person end - IO.inspect(person, label: "person:156") + # IO.inspect(person, label: "person:156") if not is_nil(person.status) and person.status == 1 do # verified conn |> assign(:action, Routes.auth_path(conn, :login_register_handler)) @@ -183,16 +183,18 @@ defmodule AuthWeb.AuthController do end def verify_email(conn, params) do - IO.inspect(params, label: "params:196") + IO.inspect(params, label: "params:186") referer = params["referer"] - IO.inspect(referer, label: "referer:198") + IO.inspect(referer, label: "referer:188") person_id = AuthWeb.ApikeyController.decode_decrypt(params["id"]) IO.inspect(person_id, label: "person_id:190") + person = Auth.Person.verify_person_by_id(person_id) - auth_client_id = get_client_id_from_query(conn) - IO.inspect(auth_client_id, label: "auth_client_id:200") + client_secret = get_client_secret_from_state(referer) + IO.inspect(client_secret, label: "client_secret:193") # ref = get_referer(conn) # IO.inspect(ref, label: "referer:188") + conn |> put_resp_content_type("text/html") |> send_resp(200, "verify_email") diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index e7614874..9133bf1c 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -85,8 +85,9 @@ defmodule AuthWeb.AuthControllerTest do picture: "https://youtu.be/naoknj1ebqI", auth_provider: "google" } - person = Auth.Person.create_person(data) # |> IO.inspect(label: "person") - conn = AuthPlug.create_jwt_session(conn, Map.merge(data, person)) + person = Auth.Person.upsert_person(data) # |> IO.inspect(label: "person") + # IO.inspect(person, label: "google_handler/2 test person:89") + conn = AuthPlug.create_jwt_session(conn, person) conn = get(conn, "/auth/google/callback", %{code: "234", state: nil}) From 8621747c20b6faa71687e4e86d132c9355375907 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Mon, 4 May 2020 23:55:15 +0100 Subject: [PATCH 165/189] remove excess IO.inspect --- lib/auth/person.ex | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 1d522198..dd7589f0 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -217,7 +217,6 @@ defmodule Auth.Person do end def upsert_person(person) do - # IO.inspect(person.email, label: "person.email:220") case get_person_by_email(person.email) do nil -> create_person(person) From ec8ed358c5f60e0d14caa9f2107c4e20c71d6a0e Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 5 May 2020 13:58:19 +0100 Subject: [PATCH 166/189] tidy up verify_email/2 function for #63 --- lib/auth/person.ex | 8 +++++--- lib/auth_web/controllers/auth_controller.ex | 13 ++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/auth/person.ex b/lib/auth/person.ex index dd7589f0..4ecfafe2 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -42,7 +42,8 @@ defmodule Auth.Person do :locale, :picture, :username, - :auth_provider + :auth_provider, + :status ]) |> validate_required([:email]) |> put_email_hash() @@ -190,7 +191,8 @@ defmodule Auth.Person do end def verify_person_by_id(id) do - %{id: id, status: get_status_verified()} |> upsert_person() + person = get_person_by_id(id) + %{email: person.email, status: get_status_verified()} |> upsert_person() end def get_person_by_id(id) do @@ -226,7 +228,7 @@ defmodule Auth.Person do {:ok, person} = changeset(%Person{id: ep.id}, merged) |> Repo.update() - person + person # |> IO.inspect(label: "updated person:230") end end end diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 9551f5e8..a29cce6f 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -190,15 +190,18 @@ defmodule AuthWeb.AuthController do IO.inspect(person_id, label: "person_id:190") person = Auth.Person.verify_person_by_id(person_id) - client_secret = get_client_secret_from_state(referer) - IO.inspect(client_secret, label: "client_secret:193") + secret = get_client_secret_from_state(referer) + IO.inspect(secret, label: "secret:196") # ref = get_referer(conn) # IO.inspect(ref, label: "referer:188") conn - |> put_resp_content_type("text/html") - |> send_resp(200, "verify_email") - |> halt() + # |> AuthPlug.create_session(person, secret) + |> redirect(external: add_jwt_url_param(person, referer, secret)) + # conn + # |> put_resp_content_type("text/html") + # |> send_resp(200, "verify_email") + # |> halt() end From 7d8203dac9c9af3f2c0432e504f6e295431294d8 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 5 May 2020 21:11:48 +0100 Subject: [PATCH 167/189] [WiP] inching closer to a password creation form for #63 --- assets/css/app.css | 16 +++++ lib/auth/person.ex | 2 +- lib/auth_web/controllers/auth_controller.ex | 71 ++++++++++--------- lib/auth_web/router.ex | 2 + .../templates/auth/password-prompt.html.eex | 11 +-- lib/auth_web/templates/layout/app.html.eex | 3 +- 6 files changed, 65 insertions(+), 40 deletions(-) diff --git a/assets/css/app.css b/assets/css/app.css index c3cf4bc6..5cafbbc9 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -8,3 +8,19 @@ .hover-dwyl-teal-darkest:hover, .hover-dwyl-teal-darkest:focus { background-color: #318d7b; } + +.alert { + padding: 15px; + margin-bottom: 20px; + border: 1px solid transparent; + border-radius: 4px; +} +.alert-info { + color: #31708f; + background-color: #d9edf7; + border-color: #bce8f1; +} + +.alert:empty { + display: none; +} diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 4ecfafe2..82256ce2 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -71,7 +71,7 @@ defmodule Auth.Person do |> cast(attrs, [:email]) end - def password_prompt_changeset(attrs) do + def password_new_changeset(attrs) do %Person{} |> cast(attrs, [:email, :password]) end diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index a29cce6f..67265b00 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -47,7 +47,7 @@ defmodule AuthWeb.AuthController do oauth_github_url: oauth_github_url, oauth_google_url: oauth_google_url, changeset: Auth.Person.login_register_changeset(%{email: email}), - state: state, + state: state # errors: errors ) end @@ -127,11 +127,11 @@ defmodule AuthWeb.AuthController do form where they can define a new password for their account. """ def login_register_handler(conn, params) do - IO.inspect(params, label: "params:130") + # IO.inspect(params, label: "params:130") params_person = Map.get(params, "person") email = Map.get(params_person, "email") state = Map.get(params_person, "state") - IO.inspect(email, label: "email") + # IO.inspect(email, label: "email") # email is blank or invalid: if is_nil(email) or not Fields.Validate.email(email) do conn # email invalid, re-render the login/register form: @@ -156,15 +156,19 @@ defmodule AuthWeb.AuthController do else person end - # IO.inspect(person, label: "person:156") - if not is_nil(person.status) and person.status == 1 do # verified - conn - |> assign(:action, Routes.auth_path(conn, :login_register_handler)) - |> render("password-prompt.html", - changeset: Auth.Person.password_prompt_changeset(%{email: email}), - state: state, - person_id: AuthWeb.ApikeyController.encrypt_encode(person.id) # hide - ) + IO.inspect(person, label: "person:159") + if is_nil(person.status) do # person has not verified their email address + # display message in UI instructing them to check their email and click + # & prompt to define a password. + message = """ + You have registered with your email: #{email}. + Please check your email and click the link to verify your address. + """ + conn # redirect with info & params: stackoverflow.com/questions/48733360 + |> put_flash(:info, message) + |> redirect(to: Routes.auth_path(conn, :password_input, + state: state, email: email, person_id: person.id)) + else # respond conn @@ -182,41 +186,42 @@ defmodule AuthWeb.AuthController do <> "&referer=" <> state end + def password_input(conn, params) do + IO.inspect(params, label: "params:197") + conn + |> assign(:action, Routes.auth_path(conn, :password_create)) + |> render("password-prompt.html", + changeset: Auth.Person.password_new_changeset(%{email: params["email"]}), + state: params["state"], # so we can redirect after creatig a password + person_id: AuthWeb.ApikeyController.encrypt_encode(params["person_id"]) + ) + end + + def password_create(conn, params) do + IO.inspect(params, label: "params:201") + + conn + |> put_resp_content_type("text/html") + |> send_resp(200, "password_create") + |> halt() + end + + def verify_email(conn, params) do - IO.inspect(params, label: "params:186") referer = params["referer"] - IO.inspect(referer, label: "referer:188") - person_id = AuthWeb.ApikeyController.decode_decrypt(params["id"]) - IO.inspect(person_id, label: "person_id:190") - person = Auth.Person.verify_person_by_id(person_id) - + person = Auth.Person.verify_person_by_id(params["id"]) secret = get_client_secret_from_state(referer) - IO.inspect(secret, label: "secret:196") - # ref = get_referer(conn) - # IO.inspect(ref, label: "referer:188") - conn - # |> AuthPlug.create_session(person, secret) |> redirect(external: add_jwt_url_param(person, referer, secret)) - # conn - # |> put_resp_content_type("text/html") - # |> send_resp(200, "verify_email") - # |> halt() end - # def email_handler(conn, params) do - # - # # GOTO: https://toranbillups.com/blog/archive/2018/11/18/implementing-basic-authentication/ - # end - @doc """ `handler/3` responds to successful auth requests. if the state is defined, redirect to it. """ def handler(conn, person, state) do - # IO.inspect(person, label: "handler/3 > person") # Send welcome email: Auth.Email.sendemail(%{ email: person.email, diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index d800b2d6..8a71a33c 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -21,6 +21,8 @@ defmodule AuthWeb.Router do get "/auth/google/callback", AuthController, :google_handler get "/auth/verify", AuthController, :verify_email post "/auth/register", AuthController, :login_register_handler + get "/auth/password/new", AuthController, :password_input + post "/auth/password/create", AuthController, :password_create end diff --git a/lib/auth_web/templates/auth/password-prompt.html.eex b/lib/auth_web/templates/auth/password-prompt.html.eex index fdf5a7e3..f901f1fe 100644 --- a/lib/auth_web/templates/auth/password-prompt.html.eex +++ b/lib/auth_web/templates/auth/password-prompt.html.eex @@ -1,6 +1,6 @@ -
    -
    -

    Please Type Your Password:

    +
    +
    +

    Please Create a New Password:

    <%= form_for @changeset, @action, fn f -> %> <%= password_input f, :password, @@ -10,9 +10,10 @@
    <%= hidden_input f, :state, id: "state", value: @state %> + <%= hidden_input f, :person_id, id: "person_id", value: @person_id %> - <%= submit "Login", - class: "w-100 pointer ba border-box dwyl-bg-mint white pa3 ml1 mv1 f3 + <%= submit "Save Password", + class: "pointer ba border-box dwyl-bg-mint white pa3 ml1 mv1 f3 shadow-hover bg-animate hover-dwyl-teal-darkest no-underline db", style: "margin:0 auto; border-color: #318d7b;" %> diff --git a/lib/auth_web/templates/layout/app.html.eex b/lib/auth_web/templates/layout/app.html.eex index 91ffa65d..1a6b8843 100644 --- a/lib/auth_web/templates/layout/app.html.eex +++ b/lib/auth_web/templates/layout/app.html.eex @@ -14,7 +14,8 @@ <%= render "nav.html", assigns %>
    - + <%= render @view_module, @view_template, assigns %>
    From e57f14b08a2cfdabfb4399b1327c74bb95227588 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Tue, 5 May 2020 21:23:51 +0100 Subject: [PATCH 168/189] improve styling of put_flash alert box for #63 --- assets/css/app.css | 1 + lib/auth_web/controllers/auth_controller.ex | 3 ++- lib/auth_web/templates/auth/password-prompt.html.eex | 2 +- lib/auth_web/templates/layout/app.html.eex | 3 ++- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/assets/css/app.css b/assets/css/app.css index 5cafbbc9..4fc391f9 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -14,6 +14,7 @@ margin-bottom: 20px; border: 1px solid transparent; border-radius: 4px; + white-space: pre-line; } .alert-info { color: #31708f; diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 67265b00..5f45c0e7 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -162,7 +162,8 @@ defmodule AuthWeb.AuthController do # & prompt to define a password. message = """ You have registered with your email: #{email}. - Please check your email and click the link to verify your address. + We have sent you an email with a link to confirm your address. + Please check your email inbox for our message and click the link. """ conn # redirect with info & params: stackoverflow.com/questions/48733360 |> put_flash(:info, message) diff --git a/lib/auth_web/templates/auth/password-prompt.html.eex b/lib/auth_web/templates/auth/password-prompt.html.eex index f901f1fe..67a37946 100644 --- a/lib/auth_web/templates/auth/password-prompt.html.eex +++ b/lib/auth_web/templates/auth/password-prompt.html.eex @@ -1,4 +1,4 @@ -
    +

    Please Create a New Password:

    diff --git a/lib/auth_web/templates/layout/app.html.eex b/lib/auth_web/templates/layout/app.html.eex index 1a6b8843..aad1f95a 100644 --- a/lib/auth_web/templates/layout/app.html.eex +++ b/lib/auth_web/templates/layout/app.html.eex @@ -14,7 +14,8 @@ <%= render "nav.html", assigns %>
    - <%= render @view_module, @view_template, assigns %> From 79507172fd15639d0f9d889771e3c8f55ffb3d13 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 6 May 2020 22:19:10 +0100 Subject: [PATCH 169/189] add Register/Login flow diagram https://github.com/dwyl/auth/issues/63#issuecomment-624876214 --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 36dc9d53..0a779cca 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,15 @@ https://github.com/dwyl/elixir-auth-google
    +### Email + Password Registration / Login + +This diagram depicts the flow: + +registration-login-email-password-flow-diagram + +[Edit this diagram](https://docs.google.com/presentation/d/1PUKzbRQOEgHaOmaEheU7T3AHQhRT8mhGuqVKotEJkM0/edit#slide=id.g7745f61495_0_0) + + ### Email @@ -255,7 +264,7 @@ _This_ repo/project is for people who _do_ want to think about auth, want to _know_ where sensitive data is stored and want to be able to extend the code if they choose to. -### Phoenix Has a Session System, Does this Use It? +### Phoenix Has a Session System, Does this _Use_ It? Phoenix has a built-in mechanism for sessions: http://www.phoenixframework.org/docs/sessions From 85f470bda4b6fb14e45751a7fda70332c84563bf Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 6 May 2020 22:26:20 +0100 Subject: [PATCH 170/189] update register test to redirect to password promot for #63 --- test/auth_web/controllers/auth_controller_test.exs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 9133bf1c..12efd34d 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -121,7 +121,8 @@ defmodule AuthWeb.AuthControllerTest do }) # TODO: show password form! # IO.inspect(conn) - assert html_response(conn, 200) =~ "login_register_handler" + assert html_response(conn, 302) =~ "redirected" + # assert html_response(conn, 200) =~ "login_register_handler" # assert html_response(conn, 401) =~ "invalid" end end From 07cc7e305bbbf05d5eea5be1cdf81f3e5f2dd7a4 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 6 May 2020 22:30:08 +0100 Subject: [PATCH 171/189] redirect new person registration to password_input form #63 --- README.md | 2 +- assets/css/app.css | 33 +++++++++++++++++-- lib/auth_web/controllers/auth_controller.ex | 12 ++++--- lib/auth_web/templates/auth/index.html.eex | 6 ++-- .../templates/auth/password-prompt.html.eex | 2 +- lib/auth_web/templates/layout/app.html.eex | 5 ++- 6 files changed, 46 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 0a779cca..c0b83831 100644 --- a/README.md +++ b/README.md @@ -216,7 +216,7 @@ https://github.com/dwyl/elixir-auth-google ### Email + Password Registration / Login -This diagram depicts the flow: +This diagram depicts the flow: registration-login-email-password-flow-diagram diff --git a/assets/css/app.css b/assets/css/app.css index 4fc391f9..5af4fd2b 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -2,6 +2,10 @@ /* @import "./phoenix.css"; */ +.dwyl-bg-teal { + background-color: #4BC0A9; +} + .dwyl-bg-mint { background-color: #339999; } @@ -9,19 +13,44 @@ background-color: #318d7b; } +.border-dwyl-teal { + border-color: #4BC0A9; +} + +.border-dwyl-teal-darkest { + border-color: #318d7b; +} + +/* Alerts and form errors */ .alert { padding: 15px; margin-bottom: 20px; border: 1px solid transparent; border-radius: 4px; - white-space: pre-line; } .alert-info { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } - +.alert-warning { + color: #8a6d3b; + background-color: #fcf8e3; + border-color: #faebcc; +} +.alert-danger { + color: #a94442; + background-color: #f2dede; + border-color: #ebccd1; +} +.alert p { + margin-bottom: 0; +} .alert:empty { display: none; } +.help-block { + color: #a94442; + display: block; + margin: -1rem 0 2rem; +} diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 5f45c0e7..156d7c80 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -159,16 +159,17 @@ defmodule AuthWeb.AuthController do IO.inspect(person, label: "person:159") if is_nil(person.status) do # person has not verified their email address # display message in UI instructing them to check their email and click - # & prompt to define a password. + # verify their email & prompt to define a password. message = """ You have registered with your email: #{email}. We have sent you an email with a link to confirm your address. - Please check your email inbox for our message and click the link. + Please check your email inbox for our message, + open it and click the link. """ conn # redirect with info & params: stackoverflow.com/questions/48733360 |> put_flash(:info, message) |> redirect(to: Routes.auth_path(conn, :password_input, - state: state, email: email, person_id: person.id)) + state: state, email: email, email: person.email)) else # respond @@ -200,6 +201,8 @@ defmodule AuthWeb.AuthController do def password_create(conn, params) do IO.inspect(params, label: "params:201") + params_person = params["person"] + person_id = AuthWeb.ApikeyController.decode_decrypt(params_person["person_id"]) conn |> put_resp_content_type("text/html") @@ -319,7 +322,8 @@ defmodule AuthWeb.AuthController do givenName: person.givenName, id: person.id, picture: person.picture, - status: person.status + status: person.status, + email: person.email } jwt = AuthPlug.Token.generate_jwt!(data, client_secret) diff --git a/lib/auth_web/templates/auth/index.html.eex b/lib/auth_web/templates/auth/index.html.eex index 7541fcb8..52d421c9 100644 --- a/lib/auth_web/templates/auth/index.html.eex +++ b/lib/auth_web/templates/auth/index.html.eex @@ -56,9 +56,9 @@ <%= submit "Login / Register", - class: "w-100 pointer ba border-box dwyl-bg-mint white pa3 ml1 mv1 f3 - shadow-hover bg-animate hover-dwyl-teal-darkest no-underline db", - style: "margin:0 auto; border-color: #318d7b;" + class: "w-100 pointer ba dwyl-bg-teal f3 white border-dwyl-teal + pa3 shadow-hover bg-animate hover-dwyl-teal-darkest no-underline db", + style: "margin:0 auto;" %> <% end %>
    diff --git a/lib/auth_web/templates/auth/password-prompt.html.eex b/lib/auth_web/templates/auth/password-prompt.html.eex index 67a37946..e7a46e99 100644 --- a/lib/auth_web/templates/auth/password-prompt.html.eex +++ b/lib/auth_web/templates/auth/password-prompt.html.eex @@ -1,4 +1,4 @@ -
    +

    Please Create a New Password:

    diff --git a/lib/auth_web/templates/layout/app.html.eex b/lib/auth_web/templates/layout/app.html.eex index aad1f95a..bc881218 100644 --- a/lib/auth_web/templates/layout/app.html.eex +++ b/lib/auth_web/templates/layout/app.html.eex @@ -13,9 +13,8 @@ <%= render "nav.html", assigns %> -
    -

    +

    <%= render @view_module, @view_template, assigns %> From a84c6ffeb07370f7193499c52ca09d98d30556f8 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 6 May 2020 22:47:34 +0100 Subject: [PATCH 172/189] create decrypt_email/1 function to decrypt email #63 --- lib/auth/person.ex | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 82256ce2..93eff7d7 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -231,4 +231,19 @@ defmodule Auth.Person do person # |> IO.inspect(label: "updated person:230") end end + + + @doc""" + `decrypt_email/1` accepts a `cyphertext` and attempts to Base58.decode + followed by AES.decrypt it. If decode or decrypt fails, return 0 (zero). + """ + def decrypt_email(cyphertext) do + try do + cyphertext |> Base58.decode |> Fields.AES.decrypt() + rescue + ArgumentError -> + # IO.puts("AES.decrypt() unable to decrypt client_id") + 0 + end + end end From df827f72c15b3ca4dc6d7f545b4d06304362c29d Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 6 May 2020 22:48:28 +0100 Subject: [PATCH 173/189] use encrypted email in password template #63 --- lib/auth_web/templates/auth/password-prompt.html.eex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/auth_web/templates/auth/password-prompt.html.eex b/lib/auth_web/templates/auth/password-prompt.html.eex index e7a46e99..c585ae32 100644 --- a/lib/auth_web/templates/auth/password-prompt.html.eex +++ b/lib/auth_web/templates/auth/password-prompt.html.eex @@ -10,7 +10,7 @@
    <%= hidden_input f, :state, id: "state", value: @state %> - <%= hidden_input f, :person_id, id: "person_id", value: @person_id %> + <%= hidden_input f, :email, id: "email", value: @email %> <%= submit "Save Password", class: "pointer ba border-box dwyl-bg-mint white pa3 ml1 mv1 f3 From a6802020202c19e4146a5a3b9dd1c2a5be6b79a1 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 6 May 2020 22:49:55 +0100 Subject: [PATCH 174/189] rename param for encrypt_encode/1 to make it more generic --- lib/auth_web/controllers/apikey_controller.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/auth_web/controllers/apikey_controller.ex b/lib/auth_web/controllers/apikey_controller.ex index b0584d33..055d37e7 100644 --- a/lib/auth_web/controllers/apikey_controller.ex +++ b/lib/auth_web/controllers/apikey_controller.ex @@ -13,8 +13,8 @@ defmodule AuthWeb.ApikeyController do render(conn, "new.html", changeset: changeset) end - def encrypt_encode(person_id) do - Fields.AES.encrypt(person_id) |> Base58.encode + def encrypt_encode(plaintext) do + Fields.AES.encrypt(plaintext) |> Base58.encode end def create_api_key(person_id) do From f0e7f2a081500237cf0f4886a7ccf7a18944eeee Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 6 May 2020 23:00:45 +0100 Subject: [PATCH 175/189] add put_pass_hash/1 to changeset pipeline #63 --- lib/auth/person.ex | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 93eff7d7..c598da41 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -47,6 +47,7 @@ defmodule Auth.Person do ]) |> validate_required([:email]) |> put_email_hash() + |> put_pass_hash() end def create_person(person) do @@ -170,7 +171,6 @@ defmodule Auth.Person do # end defp put_email_hash(changeset) do - put_change(changeset, :email_hash, changeset.changes.email) # |> IO.inspect(label: "changeset with :email_hash") end @@ -200,15 +200,15 @@ defmodule Auth.Person do |> Repo.get_by(id: id) end - # defp put_pass_hash(changeset) do - # case changeset do - # %Ecto.Changeset{valid?: true, changes: %{password: pass}} -> - # put_change(changeset, :password_hash, pass) - # - # _ -> - # changeset - # end - # end + defp put_pass_hash(changeset) do + case changeset do + %Ecto.Changeset{valid?: true, changes: %{password: pass}} -> + put_change(changeset, :password_hash, pass) + + _ -> + changeset + end + end @doc """ `get_person_by_email/1` returns the person based on email address. From ed8c031aaf62749189025df00d44ca5e45832c68 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 6 May 2020 23:20:47 +0100 Subject: [PATCH 176/189] upsert_person in password_create/2 for #63 --- lib/auth/person.ex | 10 +++++++++- lib/auth_web/controllers/apikey_controller.ex | 2 +- lib/auth_web/controllers/auth_controller.ex | 9 +++++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/auth/person.ex b/lib/auth/person.ex index c598da41..5da8c44f 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -37,6 +37,7 @@ defmodule Auth.Person do :email, :givenName, :familyName, + :password, :password_hash, :key_id, :locale, @@ -171,6 +172,7 @@ defmodule Auth.Person do # end defp put_email_hash(changeset) do + IO.inspect(changeset, label: "changeset") put_change(changeset, :email_hash, changeset.changes.email) # |> IO.inspect(label: "changeset with :email_hash") end @@ -201,6 +203,7 @@ defmodule Auth.Person do end defp put_pass_hash(changeset) do + IO.inspect(changeset, label: "changeset put_pass_hash:205") case changeset do %Ecto.Changeset{valid?: true, changes: %{password: pass}} -> put_change(changeset, :password_hash, pass) @@ -218,7 +221,11 @@ defmodule Auth.Person do |> Repo.get_by(email_hash: email) end + @doc """ + `upsert_person/1` inserts or updates a person record. + """ def upsert_person(person) do + IO.inspect(person, label: "person:226") case get_person_by_email(person.email) do nil -> create_person(person) @@ -226,6 +233,7 @@ defmodule Auth.Person do ep -> # existing person merged = Map.merge(AuthPlug.Helpers.strip_struct_metadata(ep), person) {:ok, person} = changeset(%Person{id: ep.id}, merged) + |> IO.inspect(label: "changeset transformed:234") |> Repo.update() person # |> IO.inspect(label: "updated person:230") @@ -233,7 +241,7 @@ defmodule Auth.Person do end - @doc""" + @doc """ `decrypt_email/1` accepts a `cyphertext` and attempts to Base58.decode followed by AES.decrypt it. If decode or decrypt fails, return 0 (zero). """ diff --git a/lib/auth_web/controllers/apikey_controller.ex b/lib/auth_web/controllers/apikey_controller.ex index 055d37e7..eea624c3 100644 --- a/lib/auth_web/controllers/apikey_controller.ex +++ b/lib/auth_web/controllers/apikey_controller.ex @@ -21,7 +21,7 @@ defmodule AuthWeb.ApikeyController do encrypt_encode(person_id) <> "/" <> encrypt_encode(person_id) end - @doc""" + @doc """ `decode_decrypt/1` accepts a `key` and attempts to Base58.decode followed by AES.decrypt it. If decode or decrypt fails, return 0 (zero). """ diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 156d7c80..13d22a25 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -195,14 +195,19 @@ defmodule AuthWeb.AuthController do |> render("password-prompt.html", changeset: Auth.Person.password_new_changeset(%{email: params["email"]}), state: params["state"], # so we can redirect after creatig a password - person_id: AuthWeb.ApikeyController.encrypt_encode(params["person_id"]) + email: AuthWeb.ApikeyController.encrypt_encode(params["email"]) ) end def password_create(conn, params) do IO.inspect(params, label: "params:201") params_person = params["person"] - person_id = AuthWeb.ApikeyController.decode_decrypt(params_person["person_id"]) + email = Auth.Person.decrypt_email(params_person["email"]) + IO.inspect(email, label: "email:206") + password = params_person["password"] + IO.inspect(password, label: "password:208") + person = Auth.Person.upsert_person(%{email: email, password: password}) + IO.inspect(person, label: "person") conn |> put_resp_content_type("text/html") From 9bd084b384a6ca109b5f8fb9e79f9e72a6ed96e7 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 6 May 2020 23:22:57 +0100 Subject: [PATCH 177/189] remove excess IO.inspect in Person --- lib/auth/person.ex | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/auth/person.ex b/lib/auth/person.ex index 5da8c44f..ab043d06 100644 --- a/lib/auth/person.ex +++ b/lib/auth/person.ex @@ -172,9 +172,7 @@ defmodule Auth.Person do # end defp put_email_hash(changeset) do - IO.inspect(changeset, label: "changeset") put_change(changeset, :email_hash, changeset.changes.email) - # |> IO.inspect(label: "changeset with :email_hash") end def get_status_verified do @@ -183,7 +181,6 @@ defmodule Auth.Person do end def put_email_status_verified(changeset) do - # IO.inspect(changeset, label: "changeset") provider = changeset.changes.auth_provider if provider == "google" or provider == "github" do put_change(changeset, :status, get_status_verified()) @@ -203,7 +200,6 @@ defmodule Auth.Person do end defp put_pass_hash(changeset) do - IO.inspect(changeset, label: "changeset put_pass_hash:205") case changeset do %Ecto.Changeset{valid?: true, changes: %{password: pass}} -> put_change(changeset, :password_hash, pass) @@ -225,7 +221,6 @@ defmodule Auth.Person do `upsert_person/1` inserts or updates a person record. """ def upsert_person(person) do - IO.inspect(person, label: "person:226") case get_person_by_email(person.email) do nil -> create_person(person) @@ -233,7 +228,7 @@ defmodule Auth.Person do ep -> # existing person merged = Map.merge(AuthPlug.Helpers.strip_struct_metadata(ep), person) {:ok, person} = changeset(%Person{id: ep.id}, merged) - |> IO.inspect(label: "changeset transformed:234") + # |> IO.inspect(label: "changeset transformed:234") |> Repo.update() person # |> IO.inspect(label: "updated person:230") From a5df6ad83d4d32030eb34a70e41feb3870371a75 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 6 May 2020 23:56:55 +0100 Subject: [PATCH 178/189] registration with email+password working. https://github.com/dwyl/auth/issues/63#issuecomment-624932939 --- lib/auth_web/controllers/auth_controller.ex | 123 ++++++++++---------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 13d22a25..5412cacf 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -93,10 +93,10 @@ defmodule AuthWeb.AuthController do """ def github_handler(conn, %{"code" => code, "state" => state}) do {:ok, profile} = ElixirAuthGithub.github_auth(code) - # IO.inspect(profile, label: "github profile") + IO.inspect(profile, label: "github profile:96") # save profile to people: person = Person.create_github_person(profile) - # IO.inspect(person, label: "github profile > person") + IO.inspect(person, label: "github profile > person:99") # render or redirect: handler(conn, person, state) end @@ -117,6 +117,61 @@ defmodule AuthWeb.AuthController do end + + @doc """ + `handler/3` responds to successful auth requests. + if the state is defined, redirect to it. + """ + def handler(conn, person, state) do + # Send welcome email: + Auth.Email.sendemail(%{ + email: person.email, + name: person.givenName, + template: "welcome" + }) + redirect_or_render(conn, person, state) + end + + def redirect_or_render(conn, person, state) do + # check if valid state (HTTP referer) is defined: + case not is_nil(state) do + true -> # redirect + case get_client_secret_from_state(state) do + 0 -> + # IO.inspect("client_secret is 0 (error)") + unauthorized(conn) + secret -> + # IO.inspect(secret, label: "secret") + conn + # |> AuthPlug.create_session(person, secret) + |> redirect(external: add_jwt_url_param(person, state, secret)) + end + + false -> # display welcome page on Auth site: + conn + |> AuthPlug.create_jwt_session(person) + |> render(:welcome, person: person) + end + end + + + def unauthorized(conn) do + # IO.inspect(conn) + conn + # |> put_resp_header("www-authenticate", "Bearer realm=\"Person access\"") + |> put_resp_content_type("text/html") + |> send_resp(401, "invalid AUTH_API_KEY/client_id please check.") + |> halt() + end + + # TODO: refactor this to render a template with a nice layout. + def not_found(conn, message) do + conn + |> put_resp_content_type("text/html") + |> send_resp(404, message) + |> halt() + end + @doc """ `login_register_handler/2` is a hybrid of traditional registration and login. If the person has already registered, we treat it as a login attempt and @@ -208,11 +263,10 @@ defmodule AuthWeb.AuthController do IO.inspect(password, label: "password:208") person = Auth.Person.upsert_person(%{email: email, password: password}) IO.inspect(person, label: "person") - - conn - |> put_resp_content_type("text/html") - |> send_resp(200, "password_create") - |> halt() + # + state = params_person["state"] + IO.inspect(state, label: "state:268") + redirect_or_render(conn, person, state) end @@ -225,61 +279,6 @@ defmodule AuthWeb.AuthController do end - - @doc """ - `handler/3` responds to successful auth requests. - if the state is defined, redirect to it. - """ - def handler(conn, person, state) do - # Send welcome email: - Auth.Email.sendemail(%{ - email: person.email, - name: person.givenName, - template: "welcome" - }) - # |> IO.inspect(label: "email") - - # IO.inspect(state, label: "state handler/3:53") - - # check if valid state (HTTP referer) is defined: - case not is_nil(state) do - true -> # redirect - case get_client_secret_from_state(state) do - 0 -> - # IO.inspect("client_secret is 0 (error)") - unauthorized(conn) - secret -> - # IO.inspect(secret, label: "secret") - conn - # |> AuthPlug.create_session(person, secret) - |> redirect(external: add_jwt_url_param(person, state, secret)) - end - - false -> # display welcome page on Auth site: - conn - |> AuthPlug.create_jwt_session(person) - |> render(:welcome, person: person) - end - end - - def unauthorized(conn) do - # IO.inspect(conn) - conn - # |> put_resp_header("www-authenticate", "Bearer realm=\"Person access\"") - |> put_resp_content_type("text/html") - |> send_resp(401, "invalid AUTH_API_KEY/client_id please check.") - |> halt() - end - - # TODO: refactor this to render a template with a nice layout. - def not_found(conn, message) do - conn - |> put_resp_content_type("text/html") - |> send_resp(404, message) - |> halt() - end - - @doc """ `get_client_secret_from_state/1` gets the client_id from state, attempts to decode_decrypt it and then look it up in apikeys From c4781f540dcd4a679e28a04707a26cc61fd7c238 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Wed, 6 May 2020 23:58:40 +0100 Subject: [PATCH 179/189] simplify (tidy up) password_create/2 handler for #63 --- lib/auth_web/controllers/auth_controller.ex | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 5412cacf..3e0fe884 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -255,18 +255,11 @@ defmodule AuthWeb.AuthController do end def password_create(conn, params) do - IO.inspect(params, label: "params:201") - params_person = params["person"] - email = Auth.Person.decrypt_email(params_person["email"]) - IO.inspect(email, label: "email:206") - password = params_person["password"] - IO.inspect(password, label: "password:208") - person = Auth.Person.upsert_person(%{email: email, password: password}) - IO.inspect(person, label: "person") - # - state = params_person["state"] - IO.inspect(state, label: "state:268") - redirect_or_render(conn, person, state) + # IO.inspect(params, label: "params:201") + p = params["person"] + email = Auth.Person.decrypt_email(p["email"]) + person = Auth.Person.upsert_person(%{email: email, password: p["password"]}) + redirect_or_render(conn, person, p["state"]) end From a34f966bce711521543df5ab9d4d51266cb91905 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 7 May 2020 07:55:45 +0100 Subject: [PATCH 180/189] rename templates for clarity #63 --- lib/auth_web/controllers/auth_controller.ex | 2 +- ...ompt.html.eex => password_create.html.eex} | 0 .../templates/auth/password_prompt.html.eex | 22 +++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) rename lib/auth_web/templates/auth/{password-prompt.html.eex => password_create.html.eex} (100%) create mode 100644 lib/auth_web/templates/auth/password_prompt.html.eex diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 3e0fe884..76371d4e 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -247,7 +247,7 @@ defmodule AuthWeb.AuthController do IO.inspect(params, label: "params:197") conn |> assign(:action, Routes.auth_path(conn, :password_create)) - |> render("password-prompt.html", + |> render("password_create.html", changeset: Auth.Person.password_new_changeset(%{email: params["email"]}), state: params["state"], # so we can redirect after creatig a password email: AuthWeb.ApikeyController.encrypt_encode(params["email"]) diff --git a/lib/auth_web/templates/auth/password-prompt.html.eex b/lib/auth_web/templates/auth/password_create.html.eex similarity index 100% rename from lib/auth_web/templates/auth/password-prompt.html.eex rename to lib/auth_web/templates/auth/password_create.html.eex diff --git a/lib/auth_web/templates/auth/password_prompt.html.eex b/lib/auth_web/templates/auth/password_prompt.html.eex new file mode 100644 index 00000000..c585ae32 --- /dev/null +++ b/lib/auth_web/templates/auth/password_prompt.html.eex @@ -0,0 +1,22 @@ +
    +
    +

    Please Create a New Password:

    + + <%= form_for @changeset, @action, fn f -> %> + <%= password_input f, :password, + class: "db w-100 mt2 pa3 ba b--dark-grey f3"%> + <%= error_tag f, :password %> + + +
    + <%= hidden_input f, :state, id: "state", value: @state %> + <%= hidden_input f, :email, id: "email", value: @email %> + + <%= submit "Save Password", + class: "pointer ba border-box dwyl-bg-mint white pa3 ml1 mv1 f3 + shadow-hover bg-animate hover-dwyl-teal-darkest no-underline db", + style: "margin:0 auto; border-color: #318d7b;" + %> + <% end %> +
    +
    From 268bd939a7f55ae08b9eb4cd59eb714f9f333499 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 7 May 2020 22:21:15 +0100 Subject: [PATCH 181/189] reorg reg/login control flow to use "cond do" (switch) statement #63 --- lib/auth_web/controllers/auth_controller.ex | 90 ++++++++++++++++----- 1 file changed, 68 insertions(+), 22 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 76371d4e..dac6b3be 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -211,31 +211,51 @@ defmodule AuthWeb.AuthController do else person end - IO.inspect(person, label: "person:159") - if is_nil(person.status) do # person has not verified their email address - # display message in UI instructing them to check their email and click - # verify their email & prompt to define a password. - message = """ - You have registered with your email: #{email}. - We have sent you an email with a link to confirm your address. - Please check your email inbox for our message, - open it and click the link. - """ - conn # redirect with info & params: stackoverflow.com/questions/48733360 - |> put_flash(:info, message) - |> redirect(to: Routes.auth_path(conn, :password_input, - state: state, email: email, email: person.email)) - else - # respond - conn - |> put_resp_content_type("text/html") - |> send_resp(200, "login_register_handler " <> email) - |> halt() + cond do + is_nil(person.status) and is_nil(person.password_hash) -> + # person has not verified their email address or created a password + message = """ + You registered with the email address: #{email}. An email was sent + to you with a link to confirm your address. Please check your email + inbox for our message, open it and click the link. + """ + render_password_form(conn, email, message, state, "password_create") + + person.status > 0 and is_nil(person.password_hash) -> + # has verified but not yet defined a password + render_password_form(conn, email, "", state, "password_create") + + is_nil(person.status) and not is_nil(person.password_hash) -> + # person has not yet verified their email but has defined a password + message = """ + You registered with the email address: #{email}. An email was sent + to you with a link to confirm your address. Please check your email + inbox for our message, open it and click the link. + You can still login using the password you saved. + """ + render_password_form(conn, email, message, state, "password_prompt") + + person.status > 0 and not is_nil(person.password_hash) -> + # render password prompt without any put_flash message + render_password_form(conn, email, "", state, "password_prompt") + end end end + def render_password_form(conn, email, message, state, template) do + conn + |> put_flash(:info, message) + |> assign(:action, Routes.auth_path(conn, :password_create)) + |> render(template <> ".html", + changeset: Auth.Person.password_new_changeset(%{email: email}), + state: state, # so we can redirect after creatig a password + email: AuthWeb.ApikeyController.encrypt_encode(email) + ) + end + + def make_verify_link(conn, person, state) do AuthPlug.Helpers.get_baseurl_from_conn(conn) <> "/auth/verify?id=" @@ -254,18 +274,44 @@ defmodule AuthWeb.AuthController do ) end + @doc """ + `password_create/2` is called when a new person is registering with email + and is defining a password for the first time. + Note: at present we are not enforcing any rules for password strength/length. + Thinking of doing these checks as progressive enhancement in Browser. + see: + """ def password_create(conn, params) do - # IO.inspect(params, label: "params:201") + IO.inspect(params, label: "password_create > params:271") p = params["person"] email = Auth.Person.decrypt_email(p["email"]) person = Auth.Person.upsert_person(%{email: email, password: p["password"]}) redirect_or_render(conn, person, p["state"]) end + # def passwprd_prompt(conn, params) do + # + # end + + + def password_verify(conn, params) do + IO.inspect(params, label: "param") + # respond + conn + |> put_resp_content_type("text/html") + |> send_resp(200, "password_verify") + |> halt() + + end + + + def verify_email(conn, params) do + IO.inspect(params, label: "verify_email params:297") referer = params["referer"] - person = Auth.Person.verify_person_by_id(params["id"]) + id = AuthWeb.ApikeyController.decode_decrypt(params["id"]) + person = Auth.Person.verify_person_by_id(id) secret = get_client_secret_from_state(referer) conn |> redirect(external: add_jwt_url_param(person, referer, secret)) From ba9426f0caea0770471d61ded516656d1c2761d2 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 7 May 2020 22:22:57 +0100 Subject: [PATCH 182/189] require min password length 8 characters in creation #63 --- lib/auth_web/templates/auth/password_create.html.eex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/auth_web/templates/auth/password_create.html.eex b/lib/auth_web/templates/auth/password_create.html.eex index c585ae32..abf9fbee 100644 --- a/lib/auth_web/templates/auth/password_create.html.eex +++ b/lib/auth_web/templates/auth/password_create.html.eex @@ -4,10 +4,10 @@ <%= form_for @changeset, @action, fn f -> %> <%= password_input f, :password, - class: "db w-100 mt2 pa3 ba b--dark-grey f3"%> + class: "db w-100 mt2 pa3 ba b--dark-grey f3", + minlength: "8"%> <%= error_tag f, :password %> -
    <%= hidden_input f, :state, id: "state", value: @state %> <%= hidden_input f, :email, id: "email", value: @email %> From d9251ce99bda46d4ae0e36e7936cae292c3290c7 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 7 May 2020 23:04:30 +0100 Subject: [PATCH 183/189] use Argon2.verify_pass/2 to verify password_hash for #63 --- lib/auth_web/controllers/auth_controller.ex | 28 +++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index dac6b3be..ee47ed81 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -247,7 +247,7 @@ defmodule AuthWeb.AuthController do def render_password_form(conn, email, message, state, template) do conn |> put_flash(:info, message) - |> assign(:action, Routes.auth_path(conn, :password_create)) + |> assign(:action, Routes.auth_path(conn, String.to_atom(template))) |> render(template <> ".html", changeset: Auth.Person.password_new_changeset(%{email: email}), state: state, # so we can redirect after creatig a password @@ -289,24 +289,26 @@ defmodule AuthWeb.AuthController do redirect_or_render(conn, person, p["state"]) end - # def passwprd_prompt(conn, params) do - # - # end + def password_prompt(conn, params) do # verify the password + IO.inspect(params, label: "password_prompt params:294") + p = params["person"] + email = Auth.Person.decrypt_email(p["email"]) + person = Auth.Person.get_person_by_email(email) - def password_verify(conn, params) do - IO.inspect(params, label: "param") - # respond - conn - |> put_resp_content_type("text/html") - |> send_resp(200, "password_verify") - |> halt() + case Argon2.verify_pass(p["password"], person.password_hash) do + true -> + redirect_or_render(conn, person, p["state"]) + false -> + msg = """ + That password is incorrect. + """ + render_password_form(conn, email, msg, p["state"], "password_prompt") + end end - - def verify_email(conn, params) do IO.inspect(params, label: "verify_email params:297") referer = params["referer"] From 894d0b082d3936fde0ce7d36418b05a76ea96bda Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 7 May 2020 23:05:21 +0100 Subject: [PATCH 184/189] update copy of submit button in password_prompt.html template #63 --- lib/auth_web/router.ex | 1 + lib/auth_web/templates/auth/password_prompt.html.eex | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index 8a71a33c..2accc307 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -23,6 +23,7 @@ defmodule AuthWeb.Router do post "/auth/register", AuthController, :login_register_handler get "/auth/password/new", AuthController, :password_input post "/auth/password/create", AuthController, :password_create + post "/auth/password/verify", AuthController, :password_prompt end diff --git a/lib/auth_web/templates/auth/password_prompt.html.eex b/lib/auth_web/templates/auth/password_prompt.html.eex index c585ae32..c4dab1b7 100644 --- a/lib/auth_web/templates/auth/password_prompt.html.eex +++ b/lib/auth_web/templates/auth/password_prompt.html.eex @@ -1,6 +1,6 @@
    -

    Please Create a New Password:

    +

    Input Your Password:

    <%= form_for @changeset, @action, fn f -> %> <%= password_input f, :password, @@ -12,7 +12,7 @@ <%= hidden_input f, :state, id: "state", value: @state %> <%= hidden_input f, :email, id: "email", value: @email %> - <%= submit "Save Password", + <%= submit "Log in", class: "pointer ba border-box dwyl-bg-mint white pa3 ml1 mv1 f3 shadow-hover bg-animate hover-dwyl-teal-darkest no-underline db", style: "margin:0 auto; border-color: #318d7b;" From 5b03bd55906828ad2966f441ab7c5b72473cd694 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 7 May 2020 23:13:41 +0100 Subject: [PATCH 185/189] create test for verify_person_by_id/1 #63 --- test/auth/person_test.exs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/auth/person_test.exs b/test/auth/person_test.exs index 697aeb62..9bd73049 100644 --- a/test/auth/person_test.exs +++ b/test/auth/person_test.exs @@ -11,4 +11,15 @@ defmodule Auth.PersonTest do person2 = Person.create_person(alex) assert person2.id == person.id end + + test "verify_person_by_id/1" do + alice = %{email: "alice@gmail.com", auth_provider: "email"} + person = Person.create_person(alice) + assert is_nil(person.status) + + Person.verify_person_by_id(person.id) + updated_person = Person.get_person_by_id(person.id) + # IO.inspect(updated_person, label: "updated_person") + assert updated_person.status == 1 + end end From 07f98ca98d4a5e7f2f9f425ffad0f2997e7128de Mon Sep 17 00:00:00 2001 From: nelsonic Date: Thu, 7 May 2020 23:19:25 +0100 Subject: [PATCH 186/189] update test copy and status 302 > 200 as no longer redirecting during registraiton --- lib/auth_web/controllers/auth_controller.ex | 6 +++--- test/auth_web/controllers/auth_controller_test.exs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index ee47ed81..903125d6 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -183,9 +183,9 @@ defmodule AuthWeb.AuthController do """ def login_register_handler(conn, params) do # IO.inspect(params, label: "params:130") - params_person = Map.get(params, "person") - email = Map.get(params_person, "email") - state = Map.get(params_person, "state") + p = params["person"] + email = p["email"] + state = p["state"] # IO.inspect(email, label: "email") # email is blank or invalid: if is_nil(email) or not Fields.Validate.email(email) do diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 12efd34d..46eea40b 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -121,8 +121,8 @@ defmodule AuthWeb.AuthControllerTest do }) # TODO: show password form! # IO.inspect(conn) - assert html_response(conn, 302) =~ "redirected" - # assert html_response(conn, 200) =~ "login_register_handler" + # assert html_response(conn, 302) =~ "redirected" + assert html_response(conn, 200) =~ "New Password" # assert html_response(conn, 401) =~ "invalid" end end From 1ef7b41213f2827486ad33166889fe77f4716440 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Fri, 8 May 2020 08:35:48 +0100 Subject: [PATCH 187/189] create tests for login_register_handler/2 (all cases) see diagram: https://github.com/dwyl/auth/issues/63#issuecomment-624876214 --- lib/auth_web/controllers/auth_controller.ex | 18 ++--- lib/auth_web/router.ex | 4 +- test/auth/person_test.exs | 2 + .../controllers/auth_controller_test.exs | 73 ++++++++++++++++++- 4 files changed, 84 insertions(+), 13 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 903125d6..6e3b2bfc 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -17,17 +17,13 @@ defmodule AuthWeb.AuthController do nil end - # TODO: add friendly error message when email is invalid + # TODO: add friendly error message when email address is invalid # IO.inspect(Fields.Validate.email(email), label: "Fields.Validate.email(email)") # errors = if not is_nil(email) and not Fields.Validate.email(email) do # [email: "email address is invalid"] # else # [] # end - # - # IO.inspect(email, label: "email") - # IO.inspect(errors, label: "errors") - state = if not is_nil(params_person) and not is_nil(Map.get(params_person, "state")) do @@ -116,8 +112,6 @@ defmodule AuthWeb.AuthController do handler(conn, person, state) end - - @doc """ `handler/3` responds to successful auth requests. if the state is defined, redirect to it. @@ -132,6 +126,13 @@ defmodule AuthWeb.AuthController do redirect_or_render(conn, person, state) end + @doc """ + `redirect_or_render/3` does what it's name suggests, + redirects if the `state` (HTTP referer) is defined + or renders the default `:welcome` template. + If the `auth_client_id` is undefined or invalid, + render the `unauthorized/1` 401. + """ def redirect_or_render(conn, person, state) do # check if valid state (HTTP referer) is defined: case not is_nil(state) do @@ -154,11 +155,10 @@ defmodule AuthWeb.AuthController do end end - + # TODO: create a human-friendy response def unauthorized(conn) do # IO.inspect(conn) conn - # |> put_resp_header("www-authenticate", "Bearer realm=\"Person access\"") |> put_resp_content_type("text/html") |> send_resp(401, "invalid AUTH_API_KEY/client_id please check.") |> halt() diff --git a/lib/auth_web/router.ex b/lib/auth_web/router.ex index 2accc307..f2a351ca 100644 --- a/lib/auth_web/router.ex +++ b/lib/auth_web/router.ex @@ -20,8 +20,8 @@ defmodule AuthWeb.Router do get "/auth/github/callback", AuthController, :github_handler get "/auth/google/callback", AuthController, :google_handler get "/auth/verify", AuthController, :verify_email - post "/auth/register", AuthController, :login_register_handler - get "/auth/password/new", AuthController, :password_input + post "/auth/loginregister", AuthController, :login_register_handler + # get "/auth/password/new", AuthController, :password_input post "/auth/password/create", AuthController, :password_create post "/auth/password/verify", AuthController, :password_prompt end diff --git a/test/auth/person_test.exs b/test/auth/person_test.exs index 9bd73049..178f3748 100644 --- a/test/auth/person_test.exs +++ b/test/auth/person_test.exs @@ -22,4 +22,6 @@ defmodule Auth.PersonTest do # IO.inspect(updated_person, label: "updated_person") assert updated_person.status == 1 end + + end diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 46eea40b..3a3f6286 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -105,7 +105,7 @@ defmodule AuthWeb.AuthControllerTest do end test "login_register_handler/2 with invalid email", %{conn: conn} do - conn = post(conn, "/auth/register", %{"person" => + conn = post(conn, "/auth/loginregister", %{"person" => %{email: "invalid", state: "www.example.com/" <> "&auth_client_id=" <> AuthPlug.Token.client_id() } }) @@ -115,7 +115,7 @@ defmodule AuthWeb.AuthControllerTest do end test "login_register_handler/2 with valid email", %{conn: conn} do - conn = post(conn, "/auth/register", %{"person" => + conn = post(conn, "/auth/loginregister", %{"person" => %{email: "jimmy@dwyl.com", state: "www.example.com/" <> "&auth_client_id=" <> AuthPlug.Token.client_id() } }) @@ -125,4 +125,73 @@ defmodule AuthWeb.AuthControllerTest do assert html_response(conn, 200) =~ "New Password" # assert html_response(conn, 401) =~ "invalid" end + + test "login_register_handler/2 UNVERIFIED and NO PASSWORD", %{conn: conn} do + data = %{ + email: "alice@gmail.com", + auth_provider: "email" + } + person = Auth.Person.upsert_person(data) # |> IO.inspect(label: "person") + + conn = post(conn, "/auth/loginregister", %{"person" => + %{email: person.email, state: "www.example.com/" <> + "&auth_client_id=" <> AuthPlug.Token.client_id() } + }) + # IO.inspect(conn.resp_body, label: "conn.resp_body") + # expect to see put_flash informing person to click verify email: + assert html_response(conn, 200) =~ "email was sent" + # instruct them to create a New Password (registration): + assert conn.resp_body =~ "Please Create a New Password" + end + + test "login_register_handler/2 has VERIFIED but NO PASSWORD", %{conn: conn} do + data = %{ + email: "alan@gmail.com", + auth_provider: "email", + status: 1 + } + person = Auth.Person.upsert_person(data) # |> IO.inspect(label: "person") + + conn = post(conn, "/auth/loginregister", %{"person" => + %{email: person.email, state: "www.example.com/" <> + "&auth_client_id=" <> AuthPlug.Token.client_id() } + }) + # instruct them to create a New Password (registration): + assert conn.resp_body =~ "Please Create a New Password" + end + + test "login_register_handler/2 UNVERIFIED person with PWD", %{conn: conn} do + data = %{ + email: "alex@gmail.com", + auth_provider: "email", + password: "thiswillbehashed" + } + person = Auth.Person.upsert_person(data) # |> IO.inspect(label: "person") + + conn = post(conn, "/auth/loginregister", %{"person" => + %{email: person.email, state: "www.example.com/" <> + "&auth_client_id=" <> AuthPlug.Token.client_id() } + }) + # expect to see put_flash informing person to click verify email: + assert html_response(conn, 200) =~ "email was sent" + # they can/should still login using the password they defined: + assert conn.resp_body =~ "Input Your Password" + end + + test "login_register_handler/2 has VERIFIED and PASSWORD", %{conn: conn} do + data = %{ + email: "ana@gmail.com", + auth_provider: "email", + status: 1, + password: "thiswillbehashed" + } + person = Auth.Person.upsert_person(data) # |> IO.inspect(label: "person") + + conn = post(conn, "/auth/loginregister", %{"person" => + %{email: person.email, state: "www.example.com/" <> + "&auth_client_id=" <> AuthPlug.Token.client_id() } + }) + # person can login with their existing password: + assert conn.resp_body =~ "Input Your Password" + end end From f1830fb251bbc40c3053fd5f5a46ab3543336c59 Mon Sep 17 00:00:00 2001 From: nelsonic Date: Fri, 8 May 2020 17:40:12 +0100 Subject: [PATCH 188/189] create test for verify_email/2 #63 --- lib/auth_web/controllers/auth_controller.ex | 55 +++++++++++-------- priv/repo/seeds.exs | 4 +- .../controllers/auth_controller_test.exs | 39 +++++++++++-- 3 files changed, 67 insertions(+), 31 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 6e3b2bfc..1a2e8aaf 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -134,6 +134,7 @@ defmodule AuthWeb.AuthController do render the `unauthorized/1` 401. """ def redirect_or_render(conn, person, state) do + IO.inspect(state, label: "state:137") # check if valid state (HTTP referer) is defined: case not is_nil(state) do true -> # redirect @@ -215,6 +216,7 @@ defmodule AuthWeb.AuthController do cond do is_nil(person.status) and is_nil(person.password_hash) -> # person has not verified their email address or created a password + # TODO: pull out these messages into a translateable file. message = """ You registered with the email address: #{email}. An email was sent to you with a link to confirm your address. Please check your email @@ -255,7 +257,14 @@ defmodule AuthWeb.AuthController do ) end - + @doc """ + `make_verify_link/3` creates a verfication link that gets included + in the email we send to people to verify their email address. + The person.id is encrypted and base58 encoded to avoid anyone verifying + a different person's email. (not that anyone would do that, right? ;-) + We include the original state (HTTP referer) so that the request can be + redirected back to the desired page on successful verification. + """ def make_verify_link(conn, person, state) do AuthPlug.Helpers.get_baseurl_from_conn(conn) <> "/auth/verify?id=" @@ -263,16 +272,16 @@ defmodule AuthWeb.AuthController do <> "&referer=" <> state end - def password_input(conn, params) do - IO.inspect(params, label: "params:197") - conn - |> assign(:action, Routes.auth_path(conn, :password_create)) - |> render("password_create.html", - changeset: Auth.Person.password_new_changeset(%{email: params["email"]}), - state: params["state"], # so we can redirect after creatig a password - email: AuthWeb.ApikeyController.encrypt_encode(params["email"]) - ) - end + # def password_input(conn, params) do + # IO.inspect(params, label: "params:197") + # conn + # |> assign(:action, Routes.auth_path(conn, :password_create)) + # |> render("password_create.html", + # changeset: Auth.Person.password_new_changeset(%{email: params["email"]}), + # state: params["state"], # so we can redirect after creatig a password + # email: AuthWeb.ApikeyController.encrypt_encode(params["email"]) + # ) + # end @doc """ `password_create/2` is called when a new person is registering with email @@ -289,7 +298,12 @@ defmodule AuthWeb.AuthController do redirect_or_render(conn, person, p["state"]) end - + @doc """ + `password_prompt/2` handles all requests to verify a password for a person. + If the pasword is verified (using Argon2.verify_pass), redirect to their + desired page. If the password is invalid reset & re-render the form. + TODO: + """ def password_prompt(conn, params) do # verify the password IO.inspect(params, label: "password_prompt params:294") p = params["person"] @@ -310,13 +324,10 @@ defmodule AuthWeb.AuthController do def verify_email(conn, params) do - IO.inspect(params, label: "verify_email params:297") - referer = params["referer"] + # IO.inspect(params, label: "verify_email params:297") id = AuthWeb.ApikeyController.decode_decrypt(params["id"]) person = Auth.Person.verify_person_by_id(id) - secret = get_client_secret_from_state(referer) - conn - |> redirect(external: add_jwt_url_param(person, referer, secret)) + redirect_or_render(conn, person, params["referer"]) end @@ -327,11 +338,8 @@ defmodule AuthWeb.AuthController do All other failure conditions return a 0 (zero) which results in a 401. """ def get_client_secret_from_state(state) do - # IO.inspect(state, label: "state:94") - # decoded = URI.decode(state) - # IO.inspect(decoded, label: "decoded:96") query = URI.decode_query(List.last(String.split(state, "?"))) - # IO.inspect(query, label: "query:100") + # IO.inspect(query, label: "query:345") client_id = Map.get(query, "auth_client_id") # IO.inspect(client_id, label: "client_id") case not is_nil(client_id) do @@ -345,13 +353,12 @@ defmodule AuthWeb.AuthController do def get_client_secret(client_id, state) do person_id = AuthWeb.ApikeyController.decode_decrypt(client_id) - # IO.inspect(person_id, label: "person_id:114") + if person_id == 0 do # decode_decrypt fails with state 0 - # IO.inspect(person_id, label: "person_id:116") 0 else apikeys = Auth.Apikey.list_apikeys_for_person(person_id) - # IO.inspect(apikeys, label: "apikeys:120") + Enum.filter(apikeys, fn(k) -> k.client_id == client_id and state =~ k.url end) |> List.first() |> Map.get(:client_secret) diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs index d9ed5c4a..caaef2aa 100644 --- a/priv/repo/seeds.exs +++ b/priv/repo/seeds.exs @@ -38,9 +38,9 @@ defmodule Auth.Seeds do def create_apikey_for_admin(person) do {:ok, key} = %{ - "name" => "system admin key", + "name" => "system admin key", "description" => "Created by /priv/repo/seeds.exs during setup.", - "url" => "http://localhost:4000" + "url" => "www.example.com" # the default host in %Plug.Conn } |> AuthWeb.ApikeyController.make_apikey(person.id) # |> IO.inspect(label: "apikey_params") diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index 3a3f6286..f1148c8a 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -45,20 +45,22 @@ defmodule AuthWeb.AuthControllerTest do end test "github_handler/2 github auth callback", %{conn: conn} do + baseurl = AuthPlug.Helpers.get_baseurl_from_conn(conn) conn = get(conn, "/auth/github/callback", - %{code: "123", state: "http://localhost:4000/" <> + %{code: "123", state: baseurl <> "&auth_client_id=" <> AuthPlug.Token.client_id() }) # assert html_response(conn, 200) =~ "test@gmail.com" - assert html_response(conn, 302) =~ "http://localhost" + assert html_response(conn, 302) =~ baseurl end test "google_handler/2 for google auth callback", %{conn: conn} do + baseurl = AuthPlug.Helpers.get_baseurl_from_conn(conn) conn = get(conn, "/auth/google/callback", - %{code: "234", state: "http://localhost:4000" <> - "&auth_client_id=" <> AuthPlug.Token.client_id() }) + %{code: "234", state: baseurl <> + "?auth_client_id=" <> AuthPlug.Token.client_id() }) # assert html_response(conn, 200) =~ "nelson@gmail.com" - assert html_response(conn, 302) =~ "http://localhost" + assert html_response(conn, 302) =~ baseurl end test "google_handler/2 show welcome page", %{conn: conn} do @@ -194,4 +196,31 @@ defmodule AuthWeb.AuthControllerTest do # person can login with their existing password: assert conn.resp_body =~ "Input Your Password" end + + test "password_create/2 create a new password", %{conn: conn} do + %{ email: "anabela@mail.com", auth_provider: "email" } + |> Auth.Person.upsert_person() + + params = %{ "person" => %{ + "email" => AuthWeb.ApikeyController.encrypt_encode("anabela@mail.com"), + "password" => "thiswillbehashed" + }} + + conn = post(conn, "/auth/password/create", params) + assert html_response(conn, 200) =~ "Welcome" + end + + test "verify_email/2 verify an email address", %{conn: conn} do + person = %{ email: "anabela@mail.com", auth_provider: "email" } + |> Auth.Person.upsert_person() + # IO.inspect(conn) + state = AuthPlug.Helpers.get_baseurl_from_conn(conn) + <> "/profile?auth_client_id=" <> AuthPlug.Token.client_id() + link = AuthWeb.AuthController.make_verify_link(conn, person, state) + # IO.inspect(link, label: "link") + link = "/auth/verify" <> List.last(String.split(link, "/auth/verify")) + # IO.inspect(link, label: "link") + conn = get(conn, link, %{}) + assert html_response(conn, 302) =~ "redirected" + end end From a3dff03c6a2cbdcd9c86e8b170ba9add41565f4f Mon Sep 17 00:00:00 2001 From: nelsonic Date: Fri, 8 May 2020 18:25:50 +0100 Subject: [PATCH 189/189] create tests for password_prompt/2 for #63 >> 100%!! --- lib/auth_web/controllers/auth_controller.ex | 6 ++- .../controllers/auth_controller_test.exs | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/lib/auth_web/controllers/auth_controller.ex b/lib/auth_web/controllers/auth_controller.ex index 1a2e8aaf..de756ab5 100644 --- a/lib/auth_web/controllers/auth_controller.ex +++ b/lib/auth_web/controllers/auth_controller.ex @@ -291,7 +291,7 @@ defmodule AuthWeb.AuthController do see: """ def password_create(conn, params) do - IO.inspect(params, label: "password_create > params:271") + # IO.inspect(params, label: "password_create > params:271") p = params["person"] email = Auth.Person.decrypt_email(p["email"]) person = Auth.Person.upsert_person(%{email: email, password: p["password"]}) @@ -305,10 +305,12 @@ defmodule AuthWeb.AuthController do TODO: """ def password_prompt(conn, params) do # verify the password - IO.inspect(params, label: "password_prompt params:294") + # IO.inspect(params, label: "password_prompt params:294") p = params["person"] email = Auth.Person.decrypt_email(p["email"]) + # IO.inspect(email, label: "email:311") person = Auth.Person.get_person_by_email(email) + # IO.inspect(person, label: "person:312") case Argon2.verify_pass(p["password"], person.password_hash) do true -> diff --git a/test/auth_web/controllers/auth_controller_test.exs b/test/auth_web/controllers/auth_controller_test.exs index f1148c8a..a90d330d 100644 --- a/test/auth_web/controllers/auth_controller_test.exs +++ b/test/auth_web/controllers/auth_controller_test.exs @@ -223,4 +223,45 @@ defmodule AuthWeb.AuthControllerTest do conn = get(conn, link, %{}) assert html_response(conn, 302) =~ "redirected" end + + test "password_prompt/2 verify VALID password", %{conn: conn} do + data = %{ + email: "ana@mail.com", + auth_provider: "email", + status: 1, + password: "thiswillbehashed" + } + Auth.Person.upsert_person(data) + state = AuthPlug.Helpers.get_baseurl_from_conn(conn) + <> "/profile?auth_client_id=" <> AuthPlug.Token.client_id() + + params = %{ "person" => %{ + "email" => AuthWeb.ApikeyController.encrypt_encode(data.email), + "password" => "thiswillbehashed", + "state" => state + }} + conn = post(conn, "/auth/password/verify", params) + # IO.inspect(conn, label: "conn") + assert html_response(conn, 302) =~ "redirected" + end + + test "password_prompt/2 verify INVALID password", %{conn: conn} do + data = %{ + email: "ana@mail.com", + auth_provider: "email", + status: 1, + password: "thiswillbehashed" + } + Auth.Person.upsert_person(data) + state = AuthPlug.Helpers.get_baseurl_from_conn(conn) + <> "/profile?auth_client_id=" <> AuthPlug.Token.client_id() + + params = %{ "person" => %{ + "email" => AuthWeb.ApikeyController.encrypt_encode(data.email), + "password" => "fail", + "state" => state + }} + conn = post(conn, "/auth/password/verify", params) + assert html_response(conn, 200) =~ "password is incorrect" + end end