diff --git a/.formatter.exs b/.formatter.exs
index aeba3486..544be4a4 100644
--- a/.formatter.exs
+++ b/.formatter.exs
@@ -1,5 +1,5 @@
[
- inputs: ["mix.exs", ".formatter.exs", ".credo.exs", "{config,lib,test,priv}/**/*.{ex,exs}"],
+ inputs: ["mix.exs", ".formatter.exs", ".credo.exs", "{config,lib,test,rel,priv}/**/*.{ex,exs}"],
import_deps: [:ecto, :phoenix],
line_length: 180
]
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 79daf1b6..5cb73ef8 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -12,26 +12,26 @@ The goal of the Code of Conduct is to specify a baseline standard of behavior so
These are the values Accent developers should aspire to:
- * Be friendly and welcoming
- * Be patient
- * Remember that people have varying communication styles and that not everyone is using their native language. (Meaning and tone can be lost in translation.)
- * Be thoughtful
- * Productive communication requires effort. Think about how your words will be interpreted.
- * Remember that sometimes it is best to refrain entirely from commenting.
- * Be respectful
- * In particular, respect differences of opinion. It is important that we resolve disagreements and differing views constructively.
- * Avoid destructive behavior
- * Derailing: stay on topic; if you want to talk about something else, start a new conversation.
- * Unconstructive criticism: don't merely decry the current state of affairs; offer (or at least solicit) suggestions as to how things may be improved.
- * Snarking (pithy, unproductive, sniping comments).
+- Be friendly and welcoming
+- Be patient
+ - Remember that people have varying communication styles and that not everyone is using their native language. (Meaning and tone can be lost in translation.)
+- Be thoughtful
+ - Productive communication requires effort. Think about how your words will be interpreted.
+ - Remember that sometimes it is best to refrain entirely from commenting.
+- Be respectful
+ - In particular, respect differences of opinion. It is important that we resolve disagreements and differing views constructively.
+- Avoid destructive behavior
+ - Derailing: stay on topic; if you want to talk about something else, start a new conversation.
+ - Unconstructive criticism: don't merely decry the current state of affairs; offer (or at least solicit) suggestions as to how things may be improved.
+ - Snarking (pithy, unproductive, sniping comments).
The following actions are explicitly forbidden:
- * Insulting, demeaning, hateful, or threatening remarks.
- * Discrimination based on age, disability, gender, nationality, race, religion, sexuality, or similar personal characteristic.
- * Bullying or systematic harassment.
- * Unwelcome sexual advances.
- * Incitement to any of these.
+- Insulting, demeaning, hateful, or threatening remarks.
+- Discrimination based on age, disability, gender, nationality, race, religion, sexuality, or similar personal characteristic.
+- Bullying or systematic harassment.
+- Unwelcome sexual advances.
+- Incitement to any of these.
## Acknowledgements
diff --git a/README.md b/README.md
index 2fc3c44b..d5725024 100644
--- a/README.md
+++ b/README.md
@@ -14,23 +14,23 @@
Accent provides a powerful abstraction around the process maintaining translations in a web/native app.
-* **History**. Full history control and actions rollback. _Who_ did _what_, _when_.
-* **UI**. Simple yet powerful UI to enable translator and developer to be productive.
-* **CLI**. [Command line tool](https://github.com/mirego/accent-cli) to easily add Accent to your developer flow.
-* **Collaboration**. Centralize your discussions around translations.
-* **GraphQL**. The API that powers the UI is open and documented. It’s easy to build a plugin/cli/library around Accent.
+- **History**. Full history control and actions rollback. _Who_ did _what_, _when_.
+- **UI**. Simple yet powerful UI to enable translator and developer to be productive.
+- **CLI**. [Command line tool](https://github.com/mirego/accent-cli) to easily add Accent to your developer flow.
+- **Collaboration**. Centralize your discussions around translations.
+- **GraphQL**. The API that powers the UI is open and documented. It’s easy to build a plugin/cli/library around Accent.
## Contents
-| Section | Description |
-|---------------------------------------------------------|---------------------------------------------------------------------------|
-| [🚧 Requirements](#-requirements) | Dependencies required to run Accent’ stack |
-| [🎛 Mix commands](#-executing-mix-commands) | How to execute mix task with the Twelve-Factor pattern |
-| [🏎 Quickstart](#-quickstart) | Steps to run the project, from API to webapp, with or without Docker |
-| [🌳 Environment variables](#-environment-variables) | Required and optional env var used |
-| [✅ Tests](#-tests) | How to run the extensive tests suite |
-| [🚀 Heroku](#-heroku) | Easy deployment setup with Heroku |
-| [🌎 Contribute](#-contribute) | How to contribute to this repo |
+| Section | Description |
+| --------------------------------------------------- | -------------------------------------------------------------------- |
+| [🚧 Requirements](#-requirements) | Dependencies required to run Accent’ stack |
+| [🎛 Mix commands](#-executing-mix-commands) | How to execute mix task with the Twelve-Factor pattern |
+| [🏎 Quickstart](#-quickstart) | Steps to run the project, from API to webapp, with or without Docker |
+| [🌳 Environment variables](#-environment-variables) | Required and optional env var used |
+| [✅ Tests](#-tests) | How to run the extensive tests suite |
+| [🚀 Heroku](#-heroku) | Easy deployment setup with Heroku |
+| [🌎 Contribute](#-contribute) | How to contribute to this repo |
## 🚧 Requirements
@@ -71,7 +71,7 @@ _This is the full development setup. To simply run the app, see the *Docker* ins
7. Start Phoenix endpoint with `mix phx.server`
8. Start Ember server with `npm run start --prefix webapp`
-*That’s it!*
+_That’s it!_
### Makefile
@@ -87,43 +87,58 @@ _When running the production env, you need to provide a valid GOOGLE_API_CLIENT_
2. Run `make dev-start-postgresql` to start an instance of Postgresql. The instance will run on port 5432 with the `postgres` user. You can change those values in the `docker-compose.yml` file.
3. Run `make dev-start-application` to start the app! The release hook of the release will execute migrations and seeds before starting the webserver on port 4000 (again you can change the settings in `docker-compose.yml`)
-*That’s it! You now have a working Accent instance without installing Elixir or Node!*
+_That’s it! You now have a working Accent instance without installing Elixir or Node!_
## 🌳 Environment variables
Accent provides a default value for every required environment variable. This means that with the right PostgreSQL setup, you can just run `mix phx.server`.
-| Variable | Default | Description |
-|---------------------|-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `MIX_ENV` | `dev` | The application environment (`dev`, `prod`, or `test`) |
-| `DATABASE_URL` | `postgres://localhost/accent_development` | A valid database URL |
-| `CANONICAL_HOST` | `localhost` | The host that will be used to build internal URLs |
-| `PORT` | `4000` | A port to run the API on |
-| `WEBAPP_PORT` | `4200` | A port to run the Webapp on (only used in `dev` environment) |
-| `API_HOST` | `http://localhost:4000` | The API host |
-| `API_WS_HOST` | `ws://localhost:4000` | The API Websocket host |
+| Variable | Default | Description |
+| ---------------- | ----------------------------------------- | ------------------------------------------------------------------------------------- |
+| `MIX_ENV` | `dev` | The application environment (`dev`, `prod`, or `test`) |
+| `DATABASE_URL` | `postgres://localhost/accent_development` | A valid database URL |
+| `CANONICAL_HOST` | `localhost` | The host that will be used to build internal URLs |
+| `PORT` | `4000` | A port to run the API on |
+| `WEBAPP_PORT` | `4200` | A port to run the Webapp on (only used in `dev` environment) |
+| `API_HOST` | `http://localhost:4000` | The API host |
+| `API_WS_HOST` | `ws://localhost:4000` | The API Websocket host |
+| `WEBAPP_URL` | `http://localhost:4000` | The Web client’s endpoint. Used in the authentication process and in the sent emails. |
+
### Production setup
-| Variable | Default | Description |
-|------------------------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `SENTRY_DSN` | _none_ | The *secret* Sentry DSN used to collect API runtime errors |
-| `WEBAPP_SENTRY_DSN` | _none_ | The *public* Sentry DSN used to collect Webapp runtime errors |
-| `GOOGLE_API_CLIENT_ID` | _none_ | When deploying in a `prod` environment, the Google login is the only way to authenticate user. In `dev` environment, a fake login provider is used so you don’t have to setup a Google app. |
-| `RESTRICTED_DOMAIN` | _none_ | If specified, only authenticated users from this domain name will be able to create new projects. |
-| `DUMMY_LOGIN_ENABLED` | _none_ | If specified, the password-less authentication (with only the email) will be available. |
+| Variable | Default | Description |
+| ------------------- | ------- | ------------------------------------------------------------------------------------------------- |
+| `SENTRY_DSN` | _none_ | The _secret_ Sentry DSN used to collect API runtime errors |
+| `WEBAPP_SENTRY_DSN` | _none_ | The _public_ Sentry DSN used to collect Webapp runtime errors |
+| `RESTRICTED_DOMAIN` | _none_ | If specified, only authenticated users from this domain name will be able to create new projects. |
+
+### Authentication setup
+
+Various login providers are included in Accent using the awesomer Uberauth library to abstract services.
+
+| Variable | Default | Description |
+| -------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
+| `DUMMY_LOGIN_ENABLED` | _none_ | If specified (or no other authentication configs are provided), the password-less authentication (with only the email) will be available. |
+| `GOOGLE_API_CLIENT_ID` | _none_ | |
+| `GOOGLE_API_CLIENT_SECRET` | _none_ | |
+| `GITHUB_CLIENT_ID` | _none_ | |
+| `GITHUB_CLIENT_SECRET` | _none_ | |
+| `SLACK_CLIENT_ID` | _none_ | |
+| `SLACK_CLIENT_SECRET` | _none_ | |
+| `SLACK_TEAM_ID` | _none_ | |
### Email setup
+
If you want to send emails, you’ll have to configure the following environment variables:
-| Variable | Default | Description |
-| --- | --- | --- |
-| `WEBAPP_EMAIL_HOST` | _none_ | The Web client’s hostname. Used in the sent emails to link to the right URL. |
-| `MAILER_FROM` | _none_ | The email address used to send emails. |
-| `SMTP_ADDRESS` | _none_ | The SMTP server address you want to use to send your emails. |
-| `SMTP_PORT` | _none_ | The port ex: (25, 465, 587). |
-| `SMTP_USERNAME` | _none_ | The username for authentification. |
-| `SMTP_PASSWORD` | _none_ | The password for authentification. |
-| `SMTP_API_HEADER` | _none_ | An optional API header that will be added to sent emails. |
+| Variable | Default | Description |
+| ----------------- | ------- | ------------------------------------------------------------ |
+| `MAILER_FROM` | _none_ | The email address used to send emails. |
+| `SMTP_ADDRESS` | _none_ | The SMTP server address you want to use to send your emails. |
+| `SMTP_PORT` | _none_ | The port ex: (25, 465, 587). |
+| `SMTP_USERNAME` | _none_ | The username for authentification. |
+| `SMTP_PASSWORD` | _none_ | The password for authentification. |
+| `SMTP_API_HEADER` | _none_ | An optional API header that will be added to sent emails. |
## ✅ Tests
@@ -157,16 +172,16 @@ Don’t forget to run the `./priv/scripts/ci-check.sh` script to make sure that
## Contributors
-* [@simonprev](https://github.com/simonprev)
-* [@loboulet](https://github.com/loboulet)
-* [@remiprev](https://github.com/remiprev)
-* [@charlesdemers](https://github.com/charlesdemers)
-* [@ddrmanxbxfr](https://github.com/ddrmanxbxfr)
-* [@thermech](https://github.com/thermech)
+- [@simonprev](https://github.com/simonprev)
+- [@loboulet](https://github.com/loboulet)
+- [@remiprev](https://github.com/remiprev)
+- [@charlesdemers](https://github.com/charlesdemers)
+- [@ddrmanxbxfr](https://github.com/ddrmanxbxfr)
+- [@thermech](https://github.com/thermech)
## License
-Accent is © 2015-2018 [Mirego](https://www.mirego.com) and may be freely distributed under the [New BSD license](http://opensource.org/licenses/BSD-3-Clause). See the [`LICENSE.md`](https://github.com/mirego/accent/blob/master/LICENSE.md) file.
+Accent is © 2015-2019 [Mirego](https://www.mirego.com) and may be freely distributed under the [New BSD license](http://opensource.org/licenses/BSD-3-Clause). See the [`LICENSE.md`](https://github.com/mirego/accent/blob/master/LICENSE.md) file.
## About Mirego
diff --git a/config/prod.exs b/config/prod.exs
index e4f7f2fa..bb3b9c98 100644
--- a/config/prod.exs
+++ b/config/prod.exs
@@ -6,6 +6,4 @@ config :accent, Accent.Endpoint,
root: ".",
cache_static_manifest: "priv/static/cache_manifest.json"
-config :accent, dummy_provider_enabled: System.get_env("DUMMY_LOGIN_ENABLED")
-
config :logger, level: :info
diff --git a/config/test.exs b/config/test.exs
index 0c602e52..68d298c0 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -17,7 +17,7 @@ config :accent, Accent.Repo,
pool: Ecto.Adapters.SQL.Sandbox
config :accent, Accent.Mailer,
- webapp_host: "http://example.com",
+ webapp_url: "http://example.com",
mailer_from: "accent-test@example.com",
x_smtpapi_header: ~s({"category": ["test", "accent-api-test"]}),
adapter: Bamboo.TestAdapter
diff --git a/lib/accent/auth/uberauth/dummy_strategy.ex b/lib/accent/auth/uberauth/dummy_strategy.ex
new file mode 100644
index 00000000..78a1b849
--- /dev/null
+++ b/lib/accent/auth/uberauth/dummy_strategy.ex
@@ -0,0 +1,47 @@
+defmodule Accent.Auth.Ueberauth.DummyStrategy do
+ @moduledoc """
+ A username strategy for Ueberauth
+ """
+
+ use Ueberauth.Strategy
+
+ alias Ueberauth.Auth.Credentials
+ alias Ueberauth.Auth.Extra
+ alias Ueberauth.Auth.Info
+
+ @doc """
+ iex> uid(%Plug.Conn{params: %{"email" => "foo"}})
+ "foo"
+ """
+ @impl Ueberauth.Strategy
+ def uid(conn) do
+ conn.params["email"]
+ end
+
+ @doc """
+ iex> info(%Plug.Conn{params: %{"email" => "foo"}})
+ %Ueberauth.Auth.Info{email: "foo"}
+ """
+ @impl Ueberauth.Strategy
+ def info(conn) do
+ %Info{email: conn.params["email"]}
+ end
+
+ @doc """
+ iex> credentials(%Plug.Conn{})
+ %Ueberauth.Auth.Credentials{}
+ """
+ @impl Ueberauth.Strategy
+ def credentials(_conn) do
+ %Credentials{}
+ end
+
+ @doc """
+ iex> extra(%Plug.Conn{params: %{"email" => "foo"}})
+ %Ueberauth.Auth.Extra{raw_info: %{"email" => "foo"}}
+ """
+ @impl Ueberauth.Strategy
+ def extra(conn) do
+ %Extra{raw_info: conn.params}
+ end
+end
diff --git a/lib/accent/auth/user_remote/adapter/fetcher.ex b/lib/accent/auth/user_remote/adapter/fetcher.ex
deleted file mode 100644
index 21845011..00000000
--- a/lib/accent/auth/user_remote/adapter/fetcher.ex
+++ /dev/null
@@ -1,5 +0,0 @@
-defmodule Accent.UserRemote.Adapter.Fetcher do
- alias Accent.UserRemote.Adapter.User
-
- @callback fetch(String.t()) :: {:ok, User.t()} | {:error, list(String.t())}
-end
diff --git a/lib/accent/auth/user_remote/adapters/dummy.ex b/lib/accent/auth/user_remote/adapters/dummy.ex
deleted file mode 100644
index cff90fcb..00000000
--- a/lib/accent/auth/user_remote/adapters/dummy.ex
+++ /dev/null
@@ -1,17 +0,0 @@
-if Application.get_env(:accent, :dummy_provider_enabled) do
- defmodule Accent.UserRemote.Adapters.Dummy do
- @moduledoc """
- This is the simplest adapter for user remote fetching.
-
- It simply returns the value as both the email and the uid.
- """
-
- @behaviour Accent.UserRemote.Adapter.Fetcher
- @name "dummy"
-
- alias Accent.UserRemote.Adapter.User
-
- def fetch(value) when value === "", do: {:error, ["invalid email"]}
- def fetch(value), do: {:ok, %User{email: String.downcase(value), provider: @name, uid: value}}
- end
-end
diff --git a/lib/accent/auth/user_remote/adapters/google.ex b/lib/accent/auth/user_remote/adapters/google.ex
deleted file mode 100644
index 5bcceea3..00000000
--- a/lib/accent/auth/user_remote/adapters/google.ex
+++ /dev/null
@@ -1,39 +0,0 @@
-defmodule Accent.UserRemote.Adapters.Google do
- @moduledoc """
- Fetches the email and the uid from the id_token
- using the Google API v3 token info endpoint.
- """
-
- @behaviour Accent.UserRemote.Adapter.Fetcher
- @name "google"
-
- alias Accent.UserRemote.Adapter.User
-
- defmodule TokenInfoClient do
- use HTTPoison.Base
-
- @base_url "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token="
-
- def process_url(token), do: @base_url <> token
-
- def process_response_body(body), do: Jason.decode!(body)
- end
-
- def fetch(token) when token === "", do: {:error, "invalid token"}
-
- def fetch(token) do
- token
- |> TokenInfoClient.get()
- |> case do
- {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
- email = String.downcase(body["email"])
- {:ok, %User{provider: @name, fullname: body["name"], picture_url: body["picture"], email: email, uid: email}}
-
- {:ok, %HTTPoison.Response{status_code: 400}} ->
- {:error, "invalid token"}
-
- {:error, %HTTPoison.Error{reason: reason}} ->
- {:error, reason}
- end
- end
-end
diff --git a/lib/accent/auth/user_remote/authenticator.ex b/lib/accent/auth/user_remote/authenticator.ex
index 5daa92a3..c815d466 100644
--- a/lib/accent/auth/user_remote/authenticator.ex
+++ b/lib/accent/auth/user_remote/authenticator.ex
@@ -1,27 +1,34 @@
defmodule Accent.UserRemote.Authenticator do
- alias Accent.UserRemote.{CollaboratorNormalizer, Fetcher, Persister, TokenGiver}
+ alias Accent.UserRemote.{CollaboratorNormalizer, Persister, TokenGiver, User}
- def authenticate(provider, uid) do
- provider
- |> fetch(uid)
- |> persist
- |> normalize_collaborators
- |> grant_token
+ def authenticate(%{provider: provider, info: info}) do
+ info
+ |> map_user(provider)
+ |> Persister.persist()
+ |> CollaboratorNormalizer.normalize()
+ |> TokenGiver.grant_token()
end
- defp fetch(provider, uid), do: Fetcher.fetch(provider, uid)
-
- defp persist({:error, error}), do: {:error, error}
- defp persist({:ok, user}), do: Persister.persist(user)
-
- defp normalize_collaborators({:error, error}), do: {:error, error}
-
- defp normalize_collaborators({:ok, user, provider}) do
- CollaboratorNormalizer.normalize(user)
-
- {:ok, user, provider}
+ defp map_user(info, :dummy) do
+ %User{
+ provider: "dummy",
+ fullname: info.email,
+ email: normalize_email(info.email),
+ uid: normalize_email(info.email)
+ }
end
- defp grant_token({:error, error}), do: {:error, error}
- defp grant_token({:ok, user, _provider}), do: TokenGiver.grant_token(user)
+ defp map_user(info, provider) do
+ %User{
+ provider: to_string(provider),
+ fullname: info.name,
+ picture_url: info.image,
+ email: normalize_email(info.email),
+ uid: normalize_email(info.email)
+ }
+ end
+
+ defp normalize_email(email) do
+ String.downcase(email)
+ end
end
diff --git a/lib/accent/auth/user_remote/collaborator_normalizer.ex b/lib/accent/auth/user_remote/collaborator_normalizer.ex
index 40cc2826..c9dfd4e0 100644
--- a/lib/accent/auth/user_remote/collaborator_normalizer.ex
+++ b/lib/accent/auth/user_remote/collaborator_normalizer.ex
@@ -3,20 +3,21 @@ defmodule Accent.UserRemote.CollaboratorNormalizer do
alias Accent.{Collaborator, Repo, User}
- @spec normalize(User.t()) :: :ok
- def normalize(%User{id: id, email: email}) do
+ @spec normalize(User.t()) :: User.t()
+ def normalize(user = %User{id: id, email: email}) do
email
|> fetch_collaborators()
|> assign_user_id(id)
|> Enum.each(&Repo.update/1)
- :ok
+ user
end
defp fetch_collaborators(email) do
email = String.downcase(email)
- from(c in Collaborator, where: [email: ^email])
+ Collaborator
+ |> from(where: [email: ^email])
|> Repo.all()
end
diff --git a/lib/accent/auth/user_remote/fetcher.ex b/lib/accent/auth/user_remote/fetcher.ex
deleted file mode 100644
index 8925f326..00000000
--- a/lib/accent/auth/user_remote/fetcher.ex
+++ /dev/null
@@ -1,21 +0,0 @@
-defmodule Accent.UserRemote.Fetcher do
- @moduledoc """
- Fetch a user (email and provider infos) based on a single value.
- """
-
- alias Accent.UserRemote.Adapters.Google
-
- def fetch(_provider, ""), do: {:error, %{value: "empty"}}
- def fetch(_provider, nil), do: {:error, %{value: "empty"}}
-
- def fetch("dummy", value) do
- if Application.get_env(:accent, :dummy_provider_enabled) do
- Accent.UserRemote.Adapters.Dummy.fetch(value)
- else
- fetch(nil, value)
- end
- end
-
- def fetch("google", value), do: Google.fetch(value)
- def fetch(_provider, _value), do: {:error, %{provider: "unknown"}}
-end
diff --git a/lib/accent/auth/user_remote/persister.ex b/lib/accent/auth/user_remote/persister.ex
index e628b8d6..dd8a1a3e 100644
--- a/lib/accent/auth/user_remote/persister.ex
+++ b/lib/accent/auth/user_remote/persister.ex
@@ -15,28 +15,31 @@ defmodule Accent.UserRemote.Persister do
alias Accent.AuthProvider
alias Accent.Repo
alias Accent.User, as: RepoUser
- alias Accent.UserRemote.Adapter.User, as: FetchedUser
+ alias Accent.UserRemote.User, as: FetchedUser
alias Ecto.Changeset
- @spec persist(FetchedUser.t()) :: {:ok, RepoUser.t(), AuthProvider.t()}
+ @spec persist(FetchedUser.t()) :: RepoUser.t()
def persist(user = %FetchedUser{provider: provider, uid: uid}) do
- user = find_user(user)
- provider = find_provider(user, provider, uid)
-
- {:ok, user, provider}
+ user
+ |> find_or_create_user()
+ |> find_or_create_provider(provider, uid)
end
- defp find_user(fetched_user) do
+ defp find_or_create_user(fetched_user) do
case Repo.get_by(RepoUser, email: fetched_user.email) do
- user = %RepoUser{} -> update_user(user, fetched_user)
- _ -> create_user(fetched_user)
+ nil -> create_user(fetched_user)
+ user -> update_user(user, fetched_user)
end
end
- defp find_provider(user, provider_name, uid) do
+ defp find_or_create_provider(user, provider_name, uid) do
case Repo.get_by(AuthProvider, name: provider_name, uid: uid) do
- provider = %AuthProvider{} -> provider
- _ -> create_provider(user, provider_name, uid)
+ nil ->
+ create_provider(user, provider_name, uid)
+ user
+
+ _ ->
+ user
end
end
diff --git a/lib/accent/auth/user_remote/token_giver.ex b/lib/accent/auth/user_remote/token_giver.ex
index 67ab9264..1244c705 100644
--- a/lib/accent/auth/user_remote/token_giver.ex
+++ b/lib/accent/auth/user_remote/token_giver.ex
@@ -4,10 +4,7 @@ defmodule Accent.UserRemote.TokenGiver do
def grant_token(user) do
invalidate_tokens(user)
-
- token = create_token(user)
-
- {:ok, user, token}
+ create_token(user)
end
defp invalidate_tokens(user) do
@@ -17,9 +14,9 @@ defmodule Accent.UserRemote.TokenGiver do
end
defp create_token(user) do
- user
- |> Ecto.build_assoc(:access_tokens)
- |> Map.put(:token, SecureRandom.urlsafe_base64(70))
- |> Repo.insert!()
+ token = Ecto.build_assoc(user, :access_tokens)
+ token = %{token | token: SecureRandom.urlsafe_base64(70)}
+
+ Repo.insert(token)
end
end
diff --git a/lib/accent/auth/user_remote/adapter/user.ex b/lib/accent/auth/user_remote/user.ex
similarity index 81%
rename from lib/accent/auth/user_remote/adapter/user.ex
rename to lib/accent/auth/user_remote/user.ex
index 174251a8..6871dd51 100644
--- a/lib/accent/auth/user_remote/adapter/user.ex
+++ b/lib/accent/auth/user_remote/user.ex
@@ -1,4 +1,4 @@
-defmodule Accent.UserRemote.Adapter.User do
+defmodule Accent.UserRemote.User do
defstruct ~w(email provider uid fullname picture_url)a
@type t :: %__MODULE__{email: String.t(), provider: String.t(), uid: String.t(), fullname: String.t(), picture_url: String.t()}
diff --git a/lib/accent/projects/project_creator.ex b/lib/accent/projects/project_creator.ex
index 40fd1f17..22df0306 100644
--- a/lib/accent/projects/project_creator.ex
+++ b/lib/accent/projects/project_creator.ex
@@ -43,11 +43,9 @@ defmodule Accent.ProjectCreator do
end
def generate_bot_user_with_access do
- {:ok, bot_user, _token} =
- @bot
- |> Repo.insert!()
- |> TokenGiver.grant_token()
+ user = Repo.insert!(@bot)
+ {:ok, _token} = TokenGiver.grant_token(user)
- bot_user
+ user
end
end
diff --git a/lib/graphql/types/viewer.ex b/lib/graphql/types/viewer.ex
index 931d1760..9876eca7 100644
--- a/lib/graphql/types/viewer.ex
+++ b/lib/graphql/types/viewer.ex
@@ -4,7 +4,7 @@ defmodule Accent.GraphQL.Types.Viewer do
import Accent.GraphQL.Helpers.Authorization
object :viewer do
- field(:user, :user)
+ field(:user, :user, resolve: fn user, _, _ -> {:ok, user} end)
field :permissions, list_of(:string) do
resolve(viewer_authorize(:index_permissions, &Accent.GraphQL.Resolvers.Permission.list_viewer/3))
diff --git a/lib/web/controllers/auth_controller.ex b/lib/web/controllers/auth_controller.ex
new file mode 100644
index 00000000..6f4112d8
--- /dev/null
+++ b/lib/web/controllers/auth_controller.ex
@@ -0,0 +1,25 @@
+defmodule Accent.AuthController do
+ use Phoenix.Controller
+
+ alias Accent.UserRemote.Authenticator
+
+ plug Ueberauth
+
+ def callback(conn = %{assigns: %{ueberauth_auth: auth}}, _) do
+ case Authenticator.authenticate(auth) do
+ {:ok, token} ->
+ redirect(conn, external: webapp_url() <> "?token=" <> token.token)
+
+ _ ->
+ redirect(conn, external: webapp_url())
+ end
+ end
+
+ def callback(conn, _) do
+ redirect(conn, external: webapp_url())
+ end
+
+ defp webapp_url do
+ Application.get_env(:accent, :webapp_url)
+ end
+end
diff --git a/lib/web/controllers/authentication_controller.ex b/lib/web/controllers/authentication_controller.ex
deleted file mode 100644
index 58d55220..00000000
--- a/lib/web/controllers/authentication_controller.ex
+++ /dev/null
@@ -1,43 +0,0 @@
-defmodule Accent.AuthenticationController do
- use Plug.Builder
-
- alias Accent.UserRemote.Authenticator
-
- import Phoenix.Controller, only: [json: 2]
-
- plug(:fetch_authentication)
- plug(:create)
-
- def create(conn = %{assigns: %{user: user, token: token}}, _) do
- conn
- |> json(%{
- token: token.token,
- user: %{
- id: user.id,
- email: user.email,
- picture_url: user.picture_url,
- fullname: user.fullname
- }
- })
- end
-
- def create(conn, _) do
- conn
- |> put_status(:unauthorized)
- |> json(%{error: conn.assigns[:error]})
- end
-
- defp fetch_authentication(conn = %{params: %{"uid" => uid, "provider" => provider}}, _) do
- case Authenticator.authenticate(provider, uid) do
- {:ok, user, token} ->
- conn
- |> assign(:user, user)
- |> assign(:token, token)
-
- {:error, error} ->
- assign(conn, :error, error)
- end
- end
-
- defp fetch_authentication(conn, _), do: assign(conn, :error, "Invalid params")
-end
diff --git a/lib/web/router.ex b/lib/web/router.ex
index 9ce0ac44..8cdca291 100644
--- a/lib/web/router.ex
+++ b/lib/web/router.ex
@@ -51,7 +51,7 @@ defmodule Accent.Router do
scope "/", Accent do
# Users
- post("/auth", AuthenticationController, :create)
+ # post("/auth", AuthenticationController, :create)
get("/:id/percentage_reviewed_badge.svg", BadgeController, :percentage_reviewed_count)
get("/:id/reviewed_badge.svg", BadgeController, :reviewed_count)
@@ -59,6 +59,13 @@ defmodule Accent.Router do
get("/:id/translations_badge.svg", BadgeController, :translations_count)
end
+ scope "/auth", Accent do
+ pipe_through [:browser]
+
+ get "/:provider", AuthController, :request
+ get "/:provider/callback", AuthController, :callback
+ end
+
scope "/", Accent do
pipe_through(:browser)
diff --git a/lib/web/templates/email/create_comment.html.eex b/lib/web/templates/email/create_comment.html.eex
index 627e2c56..968a9df8 100644
--- a/lib/web/templates/email/create_comment.html.eex
+++ b/lib/web/templates/email/create_comment.html.eex
@@ -1,4 +1,4 @@
"><%= @commenter.email %> has commented on <%= @translation.key %>:
"><%= @comment.text %>
-">You can reply here
+">You can reply here
diff --git a/lib/web/templates/email/create_comment.text.eex b/lib/web/templates/email/create_comment.text.eex
index 6a8efc23..c8df4adc 100644
--- a/lib/web/templates/email/create_comment.text.eex
+++ b/lib/web/templates/email/create_comment.text.eex
@@ -3,4 +3,4 @@
---
<%= @comment.text %>
-You can reply here (<%= webapp_host() %><%= @translation_path %>)
+You can reply here (<%= webapp_url() %><%= @translation_path %>)
diff --git a/lib/web/templates/email/project_invite.html.eex b/lib/web/templates/email/project_invite.html.eex
index 86b7bcd3..a90b217a 100644
--- a/lib/web/templates/email/project_invite.html.eex
+++ b/lib/web/templates/email/project_invite.html.eex
@@ -1,5 +1,5 @@
"><%= @user.email %> has invited you to collaborate on the project "<%= @project.name %>"
-">If you already have an account (<%= @email %>), you can just login and you’ll see the project.
+">If you already have an account (<%= @email %>), you can just login and you’ll see the project.
-">If not, creating an account is as simple as logging in with the invited Google account (<%= @email %>) on <%= webapp_host() %>
+">If not, creating an account is as simple as logging in with the invited Google account (<%= @email %>) on <%= webapp_url() %>
diff --git a/lib/web/templates/email/project_invite.text.eex b/lib/web/templates/email/project_invite.text.eex
index 5d370b84..1d4871b2 100644
--- a/lib/web/templates/email/project_invite.text.eex
+++ b/lib/web/templates/email/project_invite.text.eex
@@ -1,5 +1,5 @@
<%= @user.email %> has invited you to collaborate on the project "<%= @project.name %>".
-If you already have an account (<%= @email %>), you can just login (<%= webapp_host() %>) and you’ll see the project.
+If you already have an account (<%= @email %>), you can just login (<%= webapp_url() %>) and you’ll see the project.
-If not, creating an account is as simple as logging in with the invited Google account (<%= @email %>) on <%= webapp_host() %>
+If not, creating an account is as simple as logging in with the invited Google account (<%= @email %>) on <%= webapp_url() %>
diff --git a/lib/web/templates/email_layout/index.html.eex b/lib/web/templates/email_layout/index.html.eex
index 06501115..94665726 100644
--- a/lib/web/templates/email_layout/index.html.eex
+++ b/lib/web/templates/email_layout/index.html.eex
@@ -13,7 +13,7 @@
- " href="<%= webapp_host() %>"><%= webapp_host() %>
+ " href="<%= webapp_url() %>"><%= webapp_url() %>
diff --git a/lib/web/templates/email_layout/index.text.eex b/lib/web/templates/email_layout/index.text.eex
index 209954e1..9c193500 100644
--- a/lib/web/templates/email_layout/index.text.eex
+++ b/lib/web/templates/email_layout/index.text.eex
@@ -6,4 +6,4 @@ Accent
-
The Accent Team
-(<%= webapp_host() %>)
+(<%= webapp_url() %>)
diff --git a/lib/web/views/email_view_config_helper.ex b/lib/web/views/email_view_config_helper.ex
index ed8ff673..510a2266 100644
--- a/lib/web/views/email_view_config_helper.ex
+++ b/lib/web/views/email_view_config_helper.ex
@@ -1,7 +1,7 @@
defmodule Accent.EmailViewConfigHelper do
def x_smtpapi_header, do: config()[:x_smtpapi_header]
def mailer_from, do: config()[:mailer_from]
- def webapp_host, do: config()[:webapp_host]
+ def webapp_url, do: config()[:webapp_url]
defp config do
Application.get_env(:accent, Accent.Mailer)
diff --git a/mix.exs b/mix.exs
index 67dd47ed..7ef7e214 100644
--- a/mix.exs
+++ b/mix.exs
@@ -73,6 +73,13 @@ defmodule Accent.Mixfile do
{:xml_builder, "~> 2.0"},
{:ex_minimatch, "~> 0.0.1"},
+ # Auth
+ {:oauth2, "~> 0.9", override: true},
+ {:ueberauth, "~> 0.6"},
+ {:ueberauth_google, "~> 0.6"},
+ {:ueberauth_github, "~> 0.7"},
+ {:ueberauth_slack, github: "ueberauth/ueberauth_slack", ref: "525594c870f959ab"},
+
# Errors
{:sentry, "~> 7.0"},
diff --git a/mix.lock b/mix.lock
index 6f8410f8..f6e8519a 100644
--- a/mix.lock
+++ b/mix.lock
@@ -51,6 +51,7 @@
"mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
"mock": {:hex, :mock, "0.3.2", "e98e998fd76c191c7e1a9557c8617912c53df3d4a6132f561eb762b699ef59fa", [:mix], [{:meck, "~> 0.8.8", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"},
"mox": {:hex, :mox, "0.5.0", "c519b48407017a85f03407a9a4c4ceb7cc6dec5fe886b2241869fb2f08476f9e", [:mix], [], "hexpm"},
+ "oauth2": {:hex, :oauth2, "0.9.4", "632e8e8826a45e33ac2ea5ac66dcc019ba6bb5a0d2ba77e342d33e3b7b252c6e", [:mix], [{:hackney, "~> 1.7", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
"p1_utils": {:git, "https://github.com/processone/p1_utils.git", "bcd456d11908fd6087235d1aef941a8db5f25ea3", []},
"parallel_stream": {:hex, :parallel_stream, "1.0.6", "b967be2b23f0f6787fab7ed681b4c45a215a81481fb62b01a5b750fa8f30f76c", [:mix], [], "hexpm"},
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
@@ -73,6 +74,10 @@
"sentry": {:hex, :sentry, "7.0.3", "093fa4b6937760afb9a5fcb0e4a9092a305b6c0ff26a710e977614b201feab75", [:mix], [{:hackney, "~> 1.8 or 1.6.5", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.3", [hex: :phoenix, repo: "hexpm", optional: true]}, {:plug, "~> 1.6", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
+ "ueberauth": {:hex, :ueberauth, "0.6.1", "9e90d3337dddf38b1ca2753aca9b1e53d8a52b890191cdc55240247c89230412", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
+ "ueberauth_github": {:hex, :ueberauth_github, "0.7.0", "637067c5500f7b13c18caca3db66d09eba661524e0d0e9518b54151e99484bad", [:mix], [{:oauth2, "~> 0.9", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.4", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm"},
+ "ueberauth_google": {:hex, :ueberauth_google, "0.8.0", "dc0e8417061c74107a3ba1419943cc930d3403b5c536b3757886964a3a70c333", [:mix], [{:oauth2, "~> 0.9", [hex: :oauth2, repo: "hexpm", optional: false]}, {:ueberauth, "~> 0.4", [hex: :ueberauth, repo: "hexpm", optional: false]}], "hexpm"},
+ "ueberauth_slack": {:git, "https://github.com/ueberauth/ueberauth_slack.git", "525594c870f959aba67acc759d5c1a588ee75e9e", [ref: "525594c870f959ab"]},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
"uuid": {:hex, :uuid, "1.1.8", "e22fc04499de0de3ed1116b770c7737779f226ceefa0badb3592e64d5cfb4eb9", [:mix], [], "hexpm"},
"xml_builder": {:hex, :xml_builder, "2.1.1", "2d6d665f09cf1319e3e1c46035755271b414d99ad8615d0bd6f337623e0c885b", [:mix], [], "hexpm"},
diff --git a/package.json b/package.json
index 3a85c5ca..8cff497f 100644
--- a/package.json
+++ b/package.json
@@ -4,8 +4,8 @@
"description": "The new new Accent Web app",
"private": true,
"scripts": {
- "prettier": "prettier --write './{webapp,jipt,cli}/!(node_modules)/**/*.{js,ts,json,gql,svg}'",
- "prettier-check": "prettier --check './{webapp,jipt,cli}/!(node_modules)/**/*.{js,ts,json,gql,svg}'",
+ "prettier": "prettier --write './{webapp,jipt,cli}/!(node_modules)/**/*.{js,ts,json,gql,svg,md}' '*.md'",
+ "prettier-check": "prettier --check './{webapp,jipt,cli}/!(node_modules)/**/*.{js,ts,json,gql,svg,md}' '*.md'",
"lint-scripts": "eslint webapp/. cli/. jipt/.",
"lint-scripts-fix": "eslint --fix webapp/.",
"tslint-scripts": "tslint -c tslint.json '{cli,jipt}/src/**/*.{js,ts,json}'",
diff --git a/rel/config/config.exs b/rel/config/config.exs
index 1d9369ca..0cd6fe7a 100644
--- a/rel/config/config.exs
+++ b/rel/config/config.exs
@@ -28,10 +28,10 @@ config :accent, Accent.Repo,
url: System.get_env("DATABASE_URL") || "postgres://localhost/accent_development"
config :accent,
+ webapp_url: System.get_env("WEBAPP_URL") || "http://localhost:4000",
force_ssl: Utilities.string_to_boolean(System.get_env("FORCE_SSL")),
hook_broadcaster: Accent.Hook.Broadcaster,
hook_github_file_server: Accent.Hook.Consumers.GitHub.FileServer.HTTP,
- dummy_provider_enabled: true,
restricted_domain: System.get_env("RESTRICTED_DOMAIN")
# Configures canary custom handlers and repo
@@ -40,6 +40,26 @@ config :canary,
unauthorized_handler: {Accent.ErrorController, :handle_unauthorized},
not_found_handler: {Accent.ErrorController, :handle_not_found}
+providers = []
+providers = if System.get_env("GOOGLE_API_CLIENT_ID"), do: [{:google, {Ueberauth.Strategy.Google, [scope: "email openid"]}} | providers], else: providers
+providers = if System.get_env("SLACK_CLIENT_ID"), do: [{:slack, {Ueberauth.Strategy.Slack, [team: System.get_env("SLACK_TEAM_ID")]}} | providers], else: providers
+providers = if System.get_env("GITHUB_CLIENT_ID"), do: [{:github, {Ueberauth.Strategy.Github, []}} | providers], else: providers
+providers = if System.get_env("DUMMY_LOGIN_ENABLED") || providers === [], do: [{:dummy, {Accent.Auth.Ueberauth.DummyStrategy, []}} | providers], else: providers
+
+config :ueberauth, Ueberauth, providers: providers
+
+config :ueberauth, Ueberauth.Strategy.Google.OAuth,
+ client_id: System.get_env("GOOGLE_API_CLIENT_ID"),
+ client_secret: System.get_env("GOOGLE_API_CLIENT_SECRET")
+
+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.Slack.OAuth,
+ client_id: System.get_env("SLACK_CLIENT_ID"),
+ client_secret: System.get_env("SLACK_CLIENT_SECRET")
+
# Configures Elixir's Logger
config :logger, :console,
format: "$time $metadata[$level] $message\n",
diff --git a/rel/config/mailer.exs b/rel/config/mailer.exs
index dc4ecb54..918a24da 100644
--- a/rel/config/mailer.exs
+++ b/rel/config/mailer.exs
@@ -2,7 +2,7 @@ use Mix.Config
if System.get_env("SMTP_ADDRESS") do
config :accent, Accent.Mailer,
- webapp_host: System.get_env("WEBAPP_EMAIL_HOST"),
+ webapp_url: System.get_env("WEBAPP_URL"),
mailer_from: System.get_env("MAILER_FROM"),
adapter: Bamboo.SMTPAdapter,
server: System.get_env("SMTP_ADDRESS"),
@@ -12,7 +12,7 @@ if System.get_env("SMTP_ADDRESS") do
x_smtpapi_header: System.get_env("SMTP_API_HEADER")
else
config :accent, Accent.Mailer,
- webapp_host: System.get_env("WEBAPP_EMAIL_HOST"),
+ webapp_url: System.get_env("WEBAPP_URL"),
mailer_from: System.get_env("MAILER_FROM"),
adapter: Bamboo.LocalAdapter
end
diff --git a/test/auth/uberauth/dummy_strategy_test.exs b/test/auth/uberauth/dummy_strategy_test.exs
new file mode 100644
index 00000000..084c2f29
--- /dev/null
+++ b/test/auth/uberauth/dummy_strategy_test.exs
@@ -0,0 +1,4 @@
+defmodule AccentTest.Auth.Uberauth.DummyStrategy do
+ use ExUnit.Case, async: true
+ doctest(Accent.Auth.Ueberauth.DummyStrategy, import: true)
+end
diff --git a/test/auth/user_remote/adapters/google_test.exs b/test/auth/user_remote/adapters/google_test.exs
deleted file mode 100644
index f8ac588d..00000000
--- a/test/auth/user_remote/adapters/google_test.exs
+++ /dev/null
@@ -1,55 +0,0 @@
-defmodule AccentTest.UserRemote.Adapters.Google do
- use Accent.RepoCase, async: false
-
- import Mock
-
- alias Accent.UserRemote.Adapter.User
- alias Accent.UserRemote.Adapters.Google
-
- defp mock_response(status, body) do
- %HTTPoison.Response{status_code: status, body: body}
- end
-
- test "valid" do
- response = [request: fn _, _, _, _, _, _, _, _, _ -> {:ok, mock_response(200, %{"email" => "test@example.com", "name" => "Test"})} end]
-
- with_mock HTTPoison.Base, response do
- expected_user = %User{email: "test@example.com", fullname: "Test", picture_url: nil, provider: "google", uid: "test@example.com"}
- assert Google.fetch("test") == {:ok, expected_user}
- end
- end
-
- test "valid with picture" do
- response = [request: fn _, _, _, _, _, _, _, _, _ -> {:ok, mock_response(200, %{"email" => "test@example.com", "name" => "Test", "picture" => "test.jpg"})} end]
-
- with_mock HTTPoison.Base, response do
- expected_user = %User{email: "test@example.com", fullname: "Test", picture_url: "test.jpg", provider: "google", uid: "test@example.com"}
- assert Google.fetch("test") == {:ok, expected_user}
- end
- end
-
- test "valid with uppercase email" do
- response = [request: fn _, _, _, _, _, _, _, _, _ -> {:ok, mock_response(200, %{"email" => "TEST@example.com", "name" => "Test"})} end]
-
- with_mock HTTPoison.Base, response do
- expected_user = %User{email: "test@example.com", fullname: "Test", picture_url: nil, provider: "google", uid: "test@example.com"}
- assert Google.fetch("test") == {:ok, expected_user}
- end
- end
-
- test "invalid with status" do
- response = [request: fn _, _, _, _, _, _, _, _, _ -> {:ok, mock_response(400, %{})} end]
-
- with_mock HTTPoison.Base, response do
- assert Google.fetch("test") == {:error, "invalid token"}
- end
- end
-
- test "error" do
- response = [request: fn _, _, _, _, _, _, _, _, _ -> {:error, %HTTPoison.Error{reason: "no internet"}} end]
-
- with_mock HTTPoison.Base, response do
- assert Google.fetch("test") == {:error, "no internet"}
- end
- end
-end
diff --git a/test/auth/user_remote/authenticator_test.exs b/test/auth/user_remote/authenticator_test.exs
index 0bf45fa5..d34507d2 100644
--- a/test/auth/user_remote/authenticator_test.exs
+++ b/test/auth/user_remote/authenticator_test.exs
@@ -12,17 +12,18 @@ defmodule AccentTest.UserRemote.Authenticator do
}
test "grant token new user" do
- {:ok, user, token} = Authenticator.authenticate("dummy", "test@example.com")
+ {:ok, token} = Authenticator.authenticate(%{provider: :dummy, info: %{email: "test@example.com"}})
+ user = Repo.get_by(User, email: "test@example.com")
assert user.email == "test@example.com"
assert token.user_id == user.id
end
test "grant token existing user" do
- {:ok, user, _token} = Authenticator.authenticate("dummy", "test@example.com")
- {:ok, existing_user, _token} = Authenticator.authenticate("dummy", "test@example.com")
+ {:ok, _token} = Authenticator.authenticate(%{provider: :dummy, info: %{email: "test@example.com"}})
+ {:ok, _token} = Authenticator.authenticate(%{provider: :dummy, info: %{email: "test@example.com"}})
- assert user.id == existing_user.id
+ assert Repo.get_by(User, email: "test@example.com")
end
test "normalize collaborators with email" do
@@ -31,7 +32,8 @@ defmodule AccentTest.UserRemote.Authenticator do
project = %Project{main_color: "#f00", name: "My project", language_id: language.id} |> Repo.insert!()
collaborator = %Collaborator{project_id: project.id, role: "admin", assigner_id: assigner.id, email: "test@example.com"} |> Repo.insert!()
- {:ok, user, _token} = Authenticator.authenticate("dummy", "test@example.com")
+ {:ok, _token} = Authenticator.authenticate(%{provider: :dummy, info: %{email: "test@example.com"}})
+ user = Repo.get_by(User, email: "test@example.com")
updated_collaborator = Repo.get(Collaborator, collaborator.id)
assert updated_collaborator.user_id == user.id
@@ -43,7 +45,8 @@ defmodule AccentTest.UserRemote.Authenticator do
project = %Project{main_color: "#f00", name: "My project", language_id: language.id} |> Repo.insert!()
collaborator = %Collaborator{project_id: project.id, role: "admin", assigner_id: assigner.id, email: "test@example.com"} |> Repo.insert!()
- {:ok, user, _token} = Authenticator.authenticate("dummy", "TeSt@eXamPle.com")
+ {:ok, _token} = Authenticator.authenticate(%{provider: :dummy, info: %{email: "TeSt@eXamPle.com"}})
+ user = Repo.get_by(User, email: "test@example.com")
updated_collaborator = Repo.get(Collaborator, collaborator.id)
assert updated_collaborator.user_id == user.id
diff --git a/test/auth/user_remote/fetcher_test.exs b/test/auth/user_remote/fetcher_test.exs
deleted file mode 100644
index 03139d14..00000000
--- a/test/auth/user_remote/fetcher_test.exs
+++ /dev/null
@@ -1,38 +0,0 @@
-defmodule AccentTest.UserRemote.Fetcher do
- use Accent.RepoCase, async: false
-
- import Mock
-
- alias Accent.UserRemote.Adapter.User
- alias Accent.UserRemote.Fetcher
-
- defp mock_response(status, body) do
- %HTTPoison.Response{status_code: status, body: body}
- end
-
- test "google" do
- response = [request: fn _, _, _, _, _, _, _, _, _ -> {:ok, mock_response(200, %{"email" => "test@example.com", "name" => "Test"})} end]
-
- with_mock HTTPoison.Base, response do
- expected_user = %User{email: "test@example.com", fullname: "Test", picture_url: nil, provider: "google", uid: "test@example.com"}
- assert Fetcher.fetch("google", "test") == {:ok, expected_user}
- end
- end
-
- test "dummy" do
- expected_user = %User{email: "test@example.com", provider: "dummy", uid: "test@example.com"}
- assert Fetcher.fetch("dummy", "test@example.com") == {:ok, expected_user}
- end
-
- test "nil token" do
- assert Fetcher.fetch("dummy", nil) == {:error, %{value: "empty"}}
- end
-
- test "empty token" do
- assert Fetcher.fetch("dummy", "") == {:error, %{value: "empty"}}
- end
-
- test "unknown provider" do
- assert Fetcher.fetch("foo", "test") == {:error, %{provider: "unknown"}}
- end
-end
diff --git a/test/auth/user_remote/persister_test.exs b/test/auth/user_remote/persister_test.exs
index 5fdcb917..41b9413d 100644
--- a/test/auth/user_remote/persister_test.exs
+++ b/test/auth/user_remote/persister_test.exs
@@ -4,35 +4,35 @@ defmodule AccentTest.UserRemote.Persister do
alias Accent.AuthProvider
alias Accent.Repo
alias Accent.User
- alias Accent.UserRemote.Adapter.User, as: UserFromFetcher
alias Accent.UserRemote.Persister
+ alias Accent.UserRemote.User, as: UserFromFetcher
@user %UserFromFetcher{email: "test@test.com", provider: "google", uid: "1234"}
test "persist with new user" do
- {:ok, user, provider} = Persister.persist(@user)
+ user = Persister.persist(@user)
assert user.id === Repo.get_by!(User, email: "test@test.com").id
- assert provider.id === Repo.get_by!(AuthProvider, uid: "1234", name: "google", user_id: user.id).id
+ assert Repo.get_by!(AuthProvider, uid: "1234", name: "google", user_id: user.id)
end
test "persist with existing user existing provider" do
existing_user = Repo.insert!(%User{email: @user.email})
- existing_provider = Repo.insert!(%AuthProvider{name: @user.provider, uid: @user.uid})
+ Repo.insert!(%AuthProvider{name: @user.provider, uid: @user.uid})
- {:ok, user, provider} = Persister.persist(@user)
+ user = Persister.persist(@user)
assert user.id === existing_user.id
- assert provider.id === existing_provider.id
+ assert length(Repo.all(AuthProvider)) === 1
end
test "persist with existing user new provider" do
existing_user = Repo.insert!(%User{email: @user.email})
Repo.insert!(%AuthProvider{name: "dummy", uid: @user.email})
- {:ok, user, provider} = Persister.persist(@user)
+ user = Persister.persist(@user)
assert user.id === existing_user.id
- assert provider.id === Repo.get_by!(AuthProvider, uid: "1234", name: "google", user_id: user.id).id
+ assert Repo.get_by!(AuthProvider, uid: "1234", name: "google", user_id: user.id)
end
end
diff --git a/test/services/collaborator_normalizer_test.exs b/test/services/collaborator_normalizer_test.exs
index 4acbc5a3..8ed89953 100644
--- a/test/services/collaborator_normalizer_test.exs
+++ b/test/services/collaborator_normalizer_test.exs
@@ -23,7 +23,7 @@ defmodule AccentTest.CollaboratorNormalizer do
new_user = %User{email: "test@test.com"} |> Repo.insert!()
- :ok = CollaboratorNormalizer.normalize(new_user)
+ %User{} = CollaboratorNormalizer.normalize(new_user)
new_collaborators = Collaborator |> where([c], c.id in ^collaborator_ids) |> Repo.all()
@@ -48,7 +48,7 @@ defmodule AccentTest.CollaboratorNormalizer do
new_user = %User{email: "Test@test.com"} |> Repo.insert!()
- :ok = CollaboratorNormalizer.normalize(new_user)
+ %User{} = CollaboratorNormalizer.normalize(new_user)
new_collaborators = Collaborator |> where([c], c.id in ^collaborator_ids) |> Repo.all()
@@ -58,7 +58,7 @@ defmodule AccentTest.CollaboratorNormalizer do
test "create without collaborations" do
new_user = %User{email: "Test@test.com"} |> Repo.insert!()
- :ok = CollaboratorNormalizer.normalize(new_user)
+ %User{} = CollaboratorNormalizer.normalize(new_user)
new_collaborators = Collaborator |> Repo.all()
diff --git a/test/web/controllers/auth_controller_test.exs b/test/web/controllers/auth_controller_test.exs
new file mode 100644
index 00000000..7f7da3d6
--- /dev/null
+++ b/test/web/controllers/auth_controller_test.exs
@@ -0,0 +1,34 @@
+defmodule AccentTest.AuthenticationController do
+ use Accent.ConnCase
+
+ alias Accent.{AccessToken, AuthController, Repo, User}
+
+ test "create responds with error when invalid params", %{conn: conn} do
+ conn = AuthController.callback(conn, nil)
+
+ assert redirected_to(conn, 302) == "http://localhost:4000"
+ end
+
+ test "create responds with valid dummy params", %{conn: conn} do
+ conn =
+ conn
+ |> assign(:ueberauth_auth, %{provider: :dummy, info: %{email: "dummy@test.com"}})
+ |> AuthController.callback(nil)
+
+ user = Repo.get_by(User, email: "dummy@test.com")
+ token = Repo.get_by(AccessToken, user_id: user.id)
+ assert redirected_to(conn, 302) =~ "http://localhost:4000?token=#{token.token}"
+ end
+
+ test "create responds with valid google params", %{conn: conn} do
+ conn =
+ conn
+ |> assign(:ueberauth_auth, %{provider: :google, info: %{name: "Dummy", email: "dummy@test.com", image: nil}})
+ |> AuthController.callback(nil)
+
+ user = Repo.get_by(User, email: "dummy@test.com")
+ token = Repo.get_by(AccessToken, user_id: user.id)
+ assert user.fullname === "Dummy"
+ assert redirected_to(conn, 302) =~ "http://localhost:4000?token=#{token.token}"
+ end
+end
diff --git a/test/web/controllers/authentication_controller_test.exs b/test/web/controllers/authentication_controller_test.exs
deleted file mode 100644
index 7de6fe59..00000000
--- a/test/web/controllers/authentication_controller_test.exs
+++ /dev/null
@@ -1,31 +0,0 @@
-defmodule AccentTest.AuthenticationController do
- use Accent.ConnCase
-
- test "create responds with error when invalid params", %{conn: conn} do
- response =
- conn
- |> post(authentication_path(conn, :create))
- |> json_response(401)
-
- assert response == %{"error" => "Invalid params"}
- end
-
- test "create responds with authenticated user", %{conn: conn} do
- response =
- conn
- |> post(authentication_path(conn, :create), %{uid: "test@example.com", provider: "dummy"})
- |> json_response(200)
-
- assert get_in(response, ["user", "email"]) == "test@example.com"
- assert get_in(response, ["token"]) != nil
- end
-
- test "create responds with error on unkown provider", %{conn: conn} do
- response =
- conn
- |> post(authentication_path(conn, :create), %{uid: "test@example.com", provider: "test"})
- |> json_response(401)
-
- assert response == %{"error" => %{"provider" => "unknown"}}
- end
-end
diff --git a/webapp/app/locales/en/translations.js b/webapp/app/locales/en/translations.js
index 383c4475..0282ad61 100644
--- a/webapp/app/locales/en/translations.js
+++ b/webapp/app/locales/en/translations.js
@@ -38,11 +38,20 @@ export default {
translation_comments_subscriptions: {
title: 'Notify on new messages'
},
- google_login_form: {
- title: 'Google authentication',
- subtitle:
- 'We use your Google account for authentication only, we will not spam you or access any of your sensitive informations.',
- login_button: 'Login with Google'
+ login_header: {
+ text: 'Login with any service below,',
+ subtext:
+ 'Your email address will be used to identify you uniquely through all services.'
+ },
+ login_footer: {
+ text: 'Nothing to hide, check the source code:',
+ link: 'mirego/accent'
+ },
+ login_forms: {
+ github: 'Login with GitHub →',
+ google: 'Login with Google →',
+ slack: 'Login with Slack →',
+ dummy: 'Enter an email and login →'
},
dummy_login_form: {
title: 'Fake authentication',
diff --git a/webapp/app/pods/application/controller.js b/webapp/app/pods/application/controller.js
deleted file mode 100644
index 53934544..00000000
--- a/webapp/app/pods/application/controller.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import Controller from '@ember/controller';
-
-export default Controller.extend();
diff --git a/webapp/app/pods/application/route.js b/webapp/app/pods/application/route.js
index cb8d06cf..aac0cfb4 100644
--- a/webapp/app/pods/application/route.js
+++ b/webapp/app/pods/application/route.js
@@ -9,22 +9,20 @@ export default Route.extend({
beforeModel() {
raven.config(config.SENTRY.DSN).install();
- this._tryGoogleLoginAfterRedirect();
+ this._tryLoginAfterRedirect();
},
- _tryGoogleLoginAfterRedirect() {
- if (!config.GOOGLE_LOGIN_ENABLED) return;
-
- const match = window.location.href
- .substring(window.location.href.indexOf('#') + 1)
+ _tryLoginAfterRedirect() {
+ const match = window.location.search
+ .substring(window.location.search.indexOf('?') + 1)
.split('&')
- .find(segment => segment.split('=')[0] === 'id_token');
+ .find(segment => segment.split('=')[0] === 'token');
const token = match && match.split('=')[1];
if (!token) return;
- this.session.login({token, provider: 'google'}).then(data => {
- if (data && data.token) this.transitionTo('logged-in.projects');
+ this.session.login({token}).then(data => {
+ if (data && data.user) this.transitionTo('logged-in.projects');
});
}
});
diff --git a/webapp/app/pods/components/google-login-form/component.js b/webapp/app/pods/components/google-login-form/component.js
deleted file mode 100644
index 89962ece..00000000
--- a/webapp/app/pods/components/google-login-form/component.js
+++ /dev/null
@@ -1,59 +0,0 @@
-import Component from '@ember/component';
-import config from 'accent-webapp/config/environment';
-
-export default Component.extend({
- loginEnabled: false,
-
- didInsertElement() {
- const loginButton = this.element.querySelector('.googleLoginButton');
-
- this._loadGoogleScript().then(() => {
- if (!window.gapi) return;
- this.set('loginEnabled', true);
-
- window.gapi.load('auth2', () => {
- const google = window.gapi.auth2.init({
- client_id: config.GOOGLE_API.CLIENT_ID, // eslint-disable-line camelcase
- ux_mode: 'redirect', // eslint-disable-line camelcase
- cookiepolicy: 'single_host_origin'
- });
-
- google.attachClickHandler(loginButton);
- });
- });
- },
-
- _loadGoogleScript() {
- return new Promise((resolve, reject) => {
- let script = document.createElement('script');
- const prior = this.element;
-
- script.async = true;
- script.defer = true;
-
- const onloadHander = (_, isAbort) => {
- if (
- isAbort ||
- !script.readyState ||
- /loaded|complete/.test(script.readyState)
- ) {
- script.onload = null;
- script.onreadystatechange = null;
- script = undefined;
-
- if (isAbort) {
- reject();
- } else {
- resolve();
- }
- }
- };
-
- script.onload = onloadHander;
- script.onreadystatechange = onloadHander;
-
- script.src = 'https://apis.google.com/js/api:client.js';
- prior.parentNode.insertBefore(script, prior);
- });
- }
-});
diff --git a/webapp/app/pods/components/google-login-form/styles.scss b/webapp/app/pods/components/google-login-form/styles.scss
index f441e4dc..56297510 100644
--- a/webapp/app/pods/components/google-login-form/styles.scss
+++ b/webapp/app/pods/components/google-login-form/styles.scss
@@ -1,31 +1,16 @@
& {
max-width: 500px;
- margin: 100px auto 30px;
- padding: 20px;
- box-shadow: 0 3px 21px rgba(#000, 0.07);
- border: 1px solid #eee;
- background: $color-light-background;
+ margin: 0 auto 30px;
text-align: center;
}
-.title {
- margin-bottom: 15px;
- font-weight: 700;
- font-size: 19px;
-}
-
-.subtitle {
- margin: 0 15px 15px;
- font-size: 14px;
-}
-
.googleLogo {
width: 30px;
margin-right: 10px;
}
.googleLoginButton {
- opacity: 0.3;
+ opacity: 1;
margin-top: 10px;
padding: 7px 40px 7px 20px;
background: #4d90fe;
@@ -33,13 +18,9 @@
font-family: arial, sans-serif;
font-size: 14px;
- &.googleLoginButton--enabled {
- opacity: 1;
-
- &:focus,
- &:hover {
- background: darken(#4d90fe, 8%);
- }
+ &:focus,
+ &:hover {
+ background: darken(#4d90fe, 8%);
}
}
diff --git a/webapp/app/pods/components/google-login-form/template.hbs b/webapp/app/pods/components/google-login-form/template.hbs
index a3a20ac9..f42a77d2 100644
--- a/webapp/app/pods/components/google-login-form/template.hbs
+++ b/webapp/app/pods/components/google-login-form/template.hbs
@@ -1,7 +1,4 @@
-{{t 'components.google_login_form.title'}}
-{{t 'components.google_login_form.subtitle'}}
-
-
+
diff --git a/webapp/app/pods/components/login-footer/styles.scss b/webapp/app/pods/components/login-footer/styles.scss
new file mode 100644
index 00000000..8b99f956
--- /dev/null
+++ b/webapp/app/pods/components/login-footer/styles.scss
@@ -0,0 +1,27 @@
+& {
+ max-width: 500px;
+ margin: 30px auto 60px;
+ padding-top: 14px;
+ border-top: 1px solid #eee;
+}
+
+.text {
+ font-size: 12px;
+ color: lighten($color-black, 50%);
+}
+
+.link {
+ color: $color-green;
+ text-decoration: none;
+
+ &:focus,
+ &:hover {
+ text-decoration: underline;
+ }
+}
+
+@media (max-width: ($screen-sm)) {
+ & {
+ margin-top: 30px;
+ }
+}
diff --git a/webapp/app/pods/components/login-footer/template.hbs b/webapp/app/pods/components/login-footer/template.hbs
new file mode 100644
index 00000000..8e0bf613
--- /dev/null
+++ b/webapp/app/pods/components/login-footer/template.hbs
@@ -0,0 +1,6 @@
+
+ {{t 'components.login_footer.text'}}
+
+ {{t 'components.login_footer.link'}}
+
+
diff --git a/webapp/app/pods/components/login-forms/component.js b/webapp/app/pods/components/login-forms/component.js
new file mode 100644
index 00000000..9e0a2418
--- /dev/null
+++ b/webapp/app/pods/components/login-forms/component.js
@@ -0,0 +1,22 @@
+// Vendor
+import Component from '@ember/component';
+import {computed} from '@ember/object';
+import config from 'accent-webapp/config/environment';
+
+export default Component.extend({
+ username: '',
+
+ googleLoginEnabled: computed(() => config.AUTH_PROVIDERS.includes('google')),
+ dummyLoginEnabled: computed(() => config.AUTH_PROVIDERS.includes('dummy')),
+ githubLoginEnabled: computed(() => config.AUTH_PROVIDERS.includes('github')),
+ slackLoginEnabled: computed(() => config.AUTH_PROVIDERS.includes('slack')),
+
+ googleUrl: computed(() => `${config.API.AUTHENTICATION_PATH}/google`),
+ githubUrl: computed(() => `${config.API.AUTHENTICATION_PATH}/github`),
+ slackUrl: computed(() => `${config.API.AUTHENTICATION_PATH}/slack`),
+ dummyUrl: computed('username', function() {
+ return `${
+ config.API.AUTHENTICATION_PATH
+ }/dummy/callback?email=${this.username}`;
+ })
+});
diff --git a/webapp/app/pods/components/login-forms/styles.scss b/webapp/app/pods/components/login-forms/styles.scss
new file mode 100644
index 00000000..735f8abb
--- /dev/null
+++ b/webapp/app/pods/components/login-forms/styles.scss
@@ -0,0 +1,82 @@
+& {
+ max-width: 400px;
+ margin: 0 auto 30px;
+}
+
+.input {
+ @extend %textInput;
+ max-width: 300px;
+ padding: 10px;
+ width: 100%;
+ font-family: $font-primary;
+ font-size: 13px;
+ border: 2px solid darken($color-green, 5%);
+
+ &:focus {
+ border-color: $color-green;
+ }
+}
+
+.loginButton {
+ max-width: 300px;
+ position: relative;
+ display: inline-flex;
+ justify-content: center;
+ width: 100%;
+ padding: 12px 10px;
+ margin-bottom: 15px;
+ text-align: center;
+ background: #fff;
+ border: 2px solid #888;
+ font-size: 13px;
+
+ &.loginButton--google {
+ border: 2px solid darken(#4d90fe, 5%);
+ text-shadow: none;
+ color: #4d90fe;
+
+ &:focus,
+ &:hover {
+ background: lighten(#4d90fe, 30%);
+ }
+ }
+
+ &.loginButton--dummy {
+ border: 2px solid darken($color-green, 5%);
+ background: $color-green;
+ border-radius: 0 0 4px 4px;
+ margin-top: -3px;
+ }
+
+ &.loginButton--github {
+ text-shadow: none;
+ color: #000;
+
+ &:focus,
+ &:hover {
+ background: #eee;
+ }
+ }
+
+ &.loginButton--slack {
+ border: 2px solid #3f0f3f;
+ text-shadow: none;
+ color: #3f0f3f;
+
+ &:focus,
+ &:hover {
+ background: lighten(#3f0f3f, 79%);
+ }
+ }
+}
+
+.loginButton-logo {
+ position: absolute;
+ left: 10px;
+ top: 8px;
+ width: 26px;
+ margin-right: 10px;
+ color: #fff;
+ font-size: 26px;
+ line-height: 1;
+}
diff --git a/webapp/app/pods/components/login-forms/template.hbs b/webapp/app/pods/components/login-forms/template.hbs
new file mode 100644
index 00000000..2496bce1
--- /dev/null
+++ b/webapp/app/pods/components/login-forms/template.hbs
@@ -0,0 +1,32 @@
+{{login-header}}
+
+{{#if dummyLoginEnabled}}
+ {{input value=username class="input"}}
+
+
+ {{t 'components.login_forms.dummy'}}
+
+{{/if}}
+
+{{#if googleLoginEnabled}}
+
+
+ {{t 'components.login_forms.google'}}
+
+{{/if}}
+
+{{#if githubLoginEnabled}}
+
+
+ {{t 'components.login_forms.github'}}
+
+{{/if}}
+
+{{#if slackLoginEnabled}}
+
+
+ {{t 'components.login_forms.slack'}}
+
+{{/if}}
+
+{{login-footer}}
diff --git a/webapp/app/pods/components/login-header/styles.scss b/webapp/app/pods/components/login-header/styles.scss
new file mode 100644
index 00000000..81a019a5
--- /dev/null
+++ b/webapp/app/pods/components/login-header/styles.scss
@@ -0,0 +1,44 @@
+& {
+ max-width: 500px;
+ margin: 100px auto 60px;
+}
+
+.title {
+ display: flex;
+ align-items: center;
+ margin-bottom: 45px;
+}
+
+.title-name {
+ margin-left: 15px;
+ text-decoration: none;
+ font-size: 25px;
+ font-weight: 700;
+ color: $color-black;
+}
+
+.text {
+ font-size: 17px;
+ line-height: 1.3;
+ color: lighten($color-black, 40%);
+}
+
+.text-emphasis {
+ display: block;
+ margin-bottom: 10px;
+ font-weight: bold;
+ font-size: 22px;
+ font-style: normal;
+ color: $color-black;
+}
+
+.logo {
+ width: 30px;
+ height: 30px;
+}
+
+@media (max-width: ($screen-sm)) {
+ & {
+ margin-top: 30px;
+ }
+}
diff --git a/webapp/app/pods/components/login-header/template.hbs b/webapp/app/pods/components/login-header/template.hbs
new file mode 100644
index 00000000..98382468
--- /dev/null
+++ b/webapp/app/pods/components/login-header/template.hbs
@@ -0,0 +1,12 @@
+
+ {{inline-svg 'assets/logo.svg' class='logo'}}
+
+
+ {{t 'general.application_name'}}
+
+
+
+
+ {{t 'components.login_header.text'}}
+ {{t 'components.login_header.subtext'}}
+
diff --git a/webapp/app/pods/components/projects-header/template.hbs b/webapp/app/pods/components/projects-header/template.hbs
index 8a039831..0d45b878 100644
--- a/webapp/app/pods/components/projects-header/template.hbs
+++ b/webapp/app/pods/components/projects-header/template.hbs
@@ -37,8 +37,8 @@
{{#if session.credentials.user}}
- {{#if session.credentials.user.picture_url}}
-
![]({{session.credentials.user.picture_url}})
+ {{#if session.credentials.user.pictureUrl}}
+
![]({{session.credentials.user.pictureUrl}})
{{/if}}
{{session.credentials.user.fullname}}
diff --git a/webapp/app/pods/login/controller.js b/webapp/app/pods/login/controller.js
index 1db84fbb..a7493e5e 100644
--- a/webapp/app/pods/login/controller.js
+++ b/webapp/app/pods/login/controller.js
@@ -1,30 +1,10 @@
import {inject as service} from '@ember/service';
-import {computed} from '@ember/object';
import Controller from '@ember/controller';
-import config from 'accent-webapp/config/environment';
export default Controller.extend({
jipt: service('jipt'),
- session: service('session'),
-
- username: '',
init() {
this.jipt.login();
- },
-
- googleLoginEnabled: computed(() => config.GOOGLE_LOGIN_ENABLED),
- dummyLoginEnabled: computed(() => config.DUMMY_LOGIN_ENABLED),
-
- actions: {
- dummyLogin(token) {
- this._login({token, provider: 'dummy'});
- }
- },
-
- _login({token, provider}) {
- this.session.login({token, provider}).then(data => {
- if (data && data.token) this.transitionToRoute('logged-in.projects');
- });
}
});
diff --git a/webapp/app/pods/login/template.hbs b/webapp/app/pods/login/template.hbs
index 210a9e81..975bb74b 100644
--- a/webapp/app/pods/login/template.hbs
+++ b/webapp/app/pods/login/template.hbs
@@ -1,13 +1,3 @@
-
- {{projects-header session=session}}
-
- {{#if googleLoginEnabled}}
- {{google-login-form}}
- {{/if}}
-
- {{#if dummyLoginEnabled}}
- {{dummy-login-form onDummyLogin=(action 'dummyLogin')}}
- {{/if}}
-
+{{login-forms}}
{{application-footer}}
diff --git a/webapp/app/services/session.js b/webapp/app/services/session.js
index 61bcfb7a..8ddb050f 100644
--- a/webapp/app/services/session.js
+++ b/webapp/app/services/session.js
@@ -24,19 +24,18 @@ export default Service.extend({
}
}),
- login(...args) {
- return this.sessionCreator
- .createSession(...args)
- .then(credentials => this.set('credentials', credentials))
- .then(credentials => {
- if (credentials && credentials.token) this.jipt.loggedIn();
+ login({token}) {
+ return this.sessionCreator.createSession({token}).then(credentials => {
+ if (!credentials || !credentials.viewer) return;
- return credentials;
- });
+ this.set('credentials', {token, ...credentials.viewer});
+ this.jipt.loggedIn();
+
+ return credentials.viewer;
+ });
},
logout() {
this.sessionDestroyer.destroySession();
- if (this.googleAuth) this.googleAuth.disconnect();
}
});
diff --git a/webapp/app/services/session/creator.js b/webapp/app/services/session/creator.js
index f46a27fc..91c53d68 100644
--- a/webapp/app/services/session/creator.js
+++ b/webapp/app/services/session/creator.js
@@ -3,20 +3,35 @@ import RSVP from 'rsvp';
import config from 'accent-webapp/config/environment';
import fetch from 'fetch';
+const uri = `${config.API.HOST}/graphql`;
+
export default Service.extend({
- createSession({token, provider}) {
+ createSession({token}) {
return new RSVP.Promise((resolve, reject) => {
- const uid = token;
const options = {
method: 'POST',
headers: {
- 'Content-Type': 'application/json'
+ 'Content-Type': 'application/json',
+ Authorization: `Bearer ${token}`
},
- body: JSON.stringify({uid, provider})
+ body: JSON.stringify({
+ query: `
+ query Viewer {
+ viewer {
+ user {
+ id
+ email
+ pictureUrl
+ fullname
+ }
+ }
+ }
+ `
+ })
};
- fetch(config.API.AUTHENTICATION_PATH, options)
- .then(data => data.json().then(resolve))
+ fetch(uri, options)
+ .then(data => data.json().then(({data}) => resolve(data)))
.catch((_jqXHR, _textStatus, error) => reject(error));
});
}
diff --git a/webapp/config/environment.js b/webapp/config/environment.js
index 7c1694d8..d0244261 100644
--- a/webapp/config/environment.js
+++ b/webapp/config/environment.js
@@ -6,6 +6,7 @@
module.exports = function(environment) {
const wsHost = process.env.API_WS_HOST || 'ws://localhost:4000';
const host = process.env.API_HOST || 'http://localhost:4000';
+ const providers = (process.env.WEBAPP_AUTH_PROVIDERS || 'dummy').split(',');
const ENV = {
modulePrefix: 'accent-webapp',
@@ -41,13 +42,7 @@ module.exports = function(environment) {
JIPT_SCRIPT_PATH: `${host}/static/jipt/index.js`
};
- ENV.GOOGLE_API = {
- CLIENT_ID: process.env.GOOGLE_API_CLIENT_ID
- };
-
- ENV.GOOGLE_LOGIN_ENABLED = Boolean(ENV.GOOGLE_API.CLIENT_ID);
- ENV.DUMMY_LOGIN_ENABLED =
- Boolean(process.env.DUMMY_LOGIN_ENABLED) || !ENV.GOOGLE_LOGIN_ENABLED;
+ ENV.AUTH_PROVIDERS = providers;
ENV.SENTRY = {
DSN: process.env.WEBAPP_SENTRY_DSN
diff --git a/webapp/public/assets/auth_providers/dummy.svg b/webapp/public/assets/auth_providers/dummy.svg
new file mode 100644
index 00000000..93597016
--- /dev/null
+++ b/webapp/public/assets/auth_providers/dummy.svg
@@ -0,0 +1,16 @@
+
diff --git a/webapp/public/assets/auth_providers/github.svg b/webapp/public/assets/auth_providers/github.svg
new file mode 100644
index 00000000..28217563
--- /dev/null
+++ b/webapp/public/assets/auth_providers/github.svg
@@ -0,0 +1,14 @@
+
diff --git a/webapp/public/assets/auth_providers/google.svg b/webapp/public/assets/auth_providers/google.svg
new file mode 100644
index 00000000..25c56f61
--- /dev/null
+++ b/webapp/public/assets/auth_providers/google.svg
@@ -0,0 +1,26 @@
+
diff --git a/webapp/public/assets/auth_providers/slack.svg b/webapp/public/assets/auth_providers/slack.svg
new file mode 100644
index 00000000..9063858d
--- /dev/null
+++ b/webapp/public/assets/auth_providers/slack.svg
@@ -0,0 +1,22 @@
+
diff --git a/webapp/public/assets/google-logo.png b/webapp/public/assets/google-logo.png
deleted file mode 100644
index 64471f28..00000000
Binary files a/webapp/public/assets/google-logo.png and /dev/null differ