diff --git a/README.md b/README.md
index 2a4d2a3..867781f 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,7 @@ The _easiest_ way to add Google OAuth authentication to your Elixir Apps.
[](https://hex.pm/packages/elixir_auth_google)
[](https://github.com/dwyl/elixir-auth-google/issues)
[](http://hits.dwyl.com/dwyl/elixir-auth-google)
+
@@ -31,29 +32,27 @@ Following best practices for security & privacy
and avoiding complexity
by having sensible defaults for all settings.
-
> We built a lightweight solution
-that only does _one_ thing
-and is easy for complete beginners to understand/use.
-There were already _several_ available options
-for adding Google Auth to apps on
-[hex.pm/packages?search=google](https://hex.pm/packages?search=google)
-that all added _far_ too many implementation steps (complexity)
-and had incomplete docs (**`@doc false`**) and tests.
-e.g:
-[github.com/googleapis/elixir-google-api](https://github.com/googleapis/elixir-google-api)
-which is a
-["_generated_"](https://github.com/googleapis/elixir-google-api/blob/master/scripts/generate_client.sh)
-client and is considered "experimental".
-We have drawn inspiration from several sources
-including code from other programming languages to build this package.
-This result is _much_ simpler
-than anything else
-and has both step-by-step instructions
-and a _complete working example_ App
-including how to encrypt tokens for secure storage
-to help you ship your app _fast_.
-
+> that only does _one_ thing
+> and is easy for complete beginners to understand/use.
+> There were already _several_ available options
+> for adding Google Auth to apps on
+> [hex.pm/packages?search=google](https://hex.pm/packages?search=google)
+> that all added _far_ too many implementation steps (complexity)
+> and had incomplete docs (**`@doc false`**) and tests.
+> e.g:
+> [github.com/googleapis/elixir-google-api](https://github.com/googleapis/elixir-google-api)
+> which is a
+> ["_generated_"](https://github.com/googleapis/elixir-google-api/blob/master/scripts/generate_client.sh)
+> client and is considered "experimental".
+> We have drawn inspiration from several sources
+> including code from other programming languages to build this package.
+> This result is _much_ simpler
+> than anything else
+> and has both step-by-step instructions
+> and a _complete working example_ App
+> including how to encrypt tokens for secure storage
+> to help you ship your app _fast_.
# _Who_? 👥
@@ -67,7 +66,6 @@ of auth "schemes" or "strategies".
Just follow the detailed instructions
and you'll be up-and running in 5 minutes.
-
# _How_? ✅
You can add Google Authentication to your Elixir App
@@ -81,19 +79,19 @@ Open your project's **`mix.exs`** file
and locate the **`deps`** (dependencies) section.
Add a line for **`:elixir_auth_google`** in the **`deps`** list:
+:exclamation: the Github action should update the OTP -> 25 and Elixir versions -> 1.14 ?
+
+:exclamation: this version is disruptive as it exposes only one function in the controller
+
```elixir
def deps do
[
- {:elixir_auth_google, "~> 1.6.3"}
+ {:elixir_auth_google, "~> 2.0"}
]
end
```
-Once you have added the line to your **`mix.exs`**,
-remember to run the **`mix deps.get`** command
-in your terminal
-to _download_ the dependencies.
-
+Once you have added the line to your **`mix.exs`** remember to run the **`mix deps.get`** command in your terminal to _download_ the dependencies.
## 2. Create Google APIs Application OAuth2 Credentials 🆕
@@ -103,15 +101,14 @@ and save the credentials as environment variables
accessible by your app, or put them in your config file.
> **Note**: There are a few steps for creating a set of Google APIs credentials,
-so if you don't already have a Google App,
-we created the following step-by-step guide
-to make it quick and _relatively_ painless:
-[create-google-app-guide.md](https://github.com/dwyl/elixir-auth-google/blob/master/create-google-app-guide.md)
-Don't be intimidated by all the buzz-words;
-it's quite straightforward.
-And if you get stuck, ask for
-[help!](https://github.com/dwyl/elixir-auth-google/issues)
-
+> so if you don't already have a Google App,
+> we created the following step-by-step guide
+> to make it quick and _relatively_ painless:
+> [create-google-app-guide.md](https://github.com/dwyl/elixir-auth-google/blob/master/create-google-app-guide.md)
+> Don't be intimidated by all the buzz-words;
+> it's quite straightforward.
+> And if you get stuck, ask for
+> [help!](https://github.com/dwyl/elixir-auth-google/issues)
## 3. Setup CLIENT_ID and CLIENT_SECRET in your project
@@ -121,6 +118,7 @@ You may either add those keys as environment variables or put them in the config
export GOOGLE_CLIENT_ID=631770888008-6n0oruvsm16kbkqg6u76p5cv5kfkcekt.apps.googleusercontent.com
export GOOGLE_CLIENT_SECRET=MHxv6-RGF5nheXnxh1b0LNDq
```
+
Or add the following in the config file:
```elixir
@@ -129,9 +127,9 @@ config :elixir_auth_google,
client_secret: "MHxv6-RGF5nheXnxh1b0LNDq"
```
-> ⚠️ Don't worry, these keys aren't valid.
-They are just here for illustration purposes.
+> ⚠️ Don't worry, these keys aren't valid.
+> They are just here for illustration purposes.
## 4. Create a `GoogleAuthController` in your Project 📝
@@ -140,27 +138,58 @@ Create a new file called
and add the following code:
```elixir
+# lib/app_web/controllers/google_auth_controller.ex
+
defmodule AppWeb.GoogleAuthController do
use AppWeb, :controller
+ action_fallback LoginErrorController
@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
- |> render(:welcome, profile: profile)
+ with {:ok, profile} <- ElixirAuthGoogle.get_profile(code, conn) do
+ conn
+ |> put_view(AppWeb.PageView)
+ |> render(:welcome, profile: profile)
+ end
end
end
```
+
This code does 3 things:
-+ Create a one-time auth `token` based on the response `code` sent by Google
-after the person authenticates.
-+ Request the person's profile data from Google based on the `access_token`
-+ Render a `:welcome` view displaying some profile data
-to confirm that login with Google was successful.
+- Requests the person's profile data from Google based on the response `code` sent by Google after the person authenticates.
+- Renders a `:welcome` view displaying some profile data
+ to confirm that login with Google was successful.
+- [Falls back](https://hexdocs.pm/phoenix/Phoenix.Controller.html#action_fallback/1) to the initial page whenever `get_profile/1` fails and sends a message `{:error, :bad_request}`.
+
+## 4.bis Optional: create an fallback controller
+
+In case the login process returns an error (because of wrong credentials, call didn't succeed), you may want ot capture and handle it nicely. This is what the `action_fallback` does: pass the flow to this controller.
+
+```elixir
+# lib/app_web/login_error_controller.ex
+
+defmodule AppWeb.LoginErrorController do
+ use AppWeb, :controller
+ require Logger
+
+ @moduledoc """
+ An example of a fallback. Returns to "/" and displays flash error
+ """
+ def call(conn, {:error, message}) do
+ oauth_google_url = ElixirAuthGoogle.generate_oauth_url(conn)
+
+ conn
+ |> fetch_session()
+ |> fetch_flash()
+ |> put_flash(:error, message)
+ |> put_view(AppWeb.PageView)
+ |> render("index.html", oauth_google_url: oauth_google_url)
+ end
+end
+```
## 5. Create the `/auth/google/callback` Endpoint 📍
@@ -170,16 +199,36 @@ and locate the section that looks like `scope "/", AppWeb do`
Add the following line:
```elixir
-get "/auth/google/callback", GoogleAuthController, :index
+# router.ex
+
+scope "/", AppWeb do
+ pipe_through :browser
+ [...]
+ get "/auth/google/callback", GoogleAuthController, :index
+end
```
Sample: [lib/app_web/router.ex#L20](https://github.com/dwyl/elixir-auth-google-demo/blob/4bb616dd134f498b84f079104c0f3345769517c4/lib/app_web/router.ex#L20)
+> **Note**: Google recommends to set a [Content Security Policy](https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid#content_security_policy) to prevent XSS attacks.
+> This can be this way by adding the CSP in the router as a plug in the `:borwser` pipeline. We set is as "report-only" below.
+
+```elixir
+# router
+@csp "script-src https://accounts.google.com/gsi/client;" <>
+ "frame-src https://accounts.google.com/gsi/;" <>
+ "connect-src https://accounts.google.com/gsi/;"
+
+pipeline :browser
+ [...]
+ plug(:put_secure_browser_headers, %{"content-security-policy-report-only" => @csp})
+```
+
### Different callback url?
You can specify the env var
-```
+```bash
export GOOGLE_CALLBACK_PATH=/myauth/google_callback
```
@@ -203,6 +252,7 @@ Open the `lib/app_web/controllers/page_controller.ex` file
and update the `index` function:
From:
+
```elixir
def index(conn, _params) do
render(conn, "index.html")
@@ -210,7 +260,10 @@ end
```
To:
+
```elixir
+# lib/app_web/controllers/page_controller.ex
+
def index(conn, _params) do
oauth_google_url = ElixirAuthGoogle.generate_oauth_url(conn)
render(conn, "index.html",[oauth_google_url: oauth_google_url])
@@ -223,12 +276,16 @@ Open the `/lib/app_web/templates/page/index.html.eex` file
and type the following code:
```html
+# lib/app_web/templates/page/index.html.heex
+
Welcome to Awesome App!
- To get started, login to your Google Account:
-
-
-
+
To get started, login to your Google Account:
+
+
+
+
+
```
@@ -245,7 +302,6 @@ where you can display a "login success" message:

-
### _Optional_: Scopes
Most of the time you will only want/need
@@ -263,7 +319,7 @@ simply define them using an environment variable, e.g:
GOOGLE_SCOPE=email contacts photoslibrary
```
-***or*** you can set them as a config variable if you prefer:
+**_or_** you can set them as a config variable if you prefer:
```
config :elixir_auth_google,
@@ -275,13 +331,12 @@ once the person authenticates/authorizes.
-
## _Optimised_ SVG+CSS Button
In **step 6.1** above, we suggest using an ` `
for the `Sign in with GitHub` button.
-But even though this image appears small **`389 × 93 px`**
+But even though this image appears small **`389 × 93 px`**
https://i.imgur.com/Kagbzkq.png it is "only" **`8kb`**:

@@ -290,43 +345,64 @@ We could spend some time in a graphics editor optimising the image,
but we _know_ we can do better by using our `CSS` skills! 💡
> **Note**: This is the _official_ button provided by Google:
-[developers.google.com/identity/images/signin-assets.zip](developers.google.com/identity/images/signin-assets.zip)
-So if there was any optimisation they could squeeze out of it,
-they probably would have done it before publishing the zip!
+> [developers.google.com/identity/images/signin-assets.zip](developers.google.com/identity/images/signin-assets.zip)
+> So if there was any optimisation they could squeeze out of it,
+> they probably would have done it before publishing the zip!
The following code re-creates the ` `
using the GitHub logo **`SVG`**
and `CSS` for layout/style:
```html
-
-
+
+
-
-
-
-
-
-
-
+ margin-top: 12px"
+ >
+
-
- Sign in with Google
-
+ Sign in with Google
```
> We created this from scratch using the SVG of the Google logo
-and some basic CSS.
-For the "making of" journey see:
-https://github.com/dwyl/elixir-auth-google/issues/25
+> and some basic CSS.
+> For the "making of" journey see:
+> https://github.com/dwyl/elixir-auth-google/issues/25
The result looks _better_ than the ` ` button:
@@ -336,7 +412,6 @@ It can be scaled to any screen size so it will _always_ look great!
Using http://bytesizematters.com we see that our SVG+CSS button is only **`1kb`**:

-
That is an **87.5%** bandwidth saving
on the **`8kb`** of the
[**`.png`** button](https://github.com/dwyl/elixir-auth-google/issues/25).
@@ -346,7 +421,6 @@ which means the page loads _even_ faster.
This is used in the Demo app:
[`lib/app_web/templates/page/index.html.eex`](https://github.com/dwyl/elixir-auth-google-demo/blob/4fdbeada2f13f4dd27d2372a916764ec7aad24e7/lib/app_web/templates/page/index.html.eex#L5-L26)
-
### `i18n`
The _biggest_ advantage of having an SVG+CSS button
@@ -377,7 +451,6 @@ which is a lot better than nothing,
the `SVG+CSS` button can be re-interpreted
by a non-screen device and more easily transformed.
-
## _Even_ More Detail 💡
@@ -398,27 +471,25 @@ including creating sessions and saving profile data to a database,
take a look at our MVP:
https://github.com/dwyl/app-mvp-phoenix
-
## Notes 📝
-+ Official Docs for Google Identity Platform:
-https://developers.google.com/identity/choose-auth
- + Web specific sample code (JS):
- https://developers.google.com/identity/sign-in/web
-+ Google Sign-In for server-side apps:
-https://developers.google.com/identity/sign-in/web/server-side-flow
-+ Using OAuth 2.0 for Web Server Applications:
-https://developers.google.com/identity/protocols/OAuth2WebServer
-+ Google Auth Branding Guidelines:
-https://developers.google.com/identity/branding-guidelines
-Only two colors are permitted for the button:
-**white** `#FFFFFF` and **blue** `#4285F4`
+- Official Docs for Google Identity Platform:
+ https://developers.google.com/identity/choose-auth
+ - Web specific sample code (JS):
+ https://developers.google.com/identity/sign-in/web
+- Google Sign-In for server-side apps:
+ https://developers.google.com/identity/sign-in/web/server-side-flow
+- Using OAuth 2.0 for Web Server Applications:
+ https://developers.google.com/identity/protocols/OAuth2WebServer
+- Google Auth Branding Guidelines:
+ https://developers.google.com/identity/branding-guidelines
+ Only two colors are permitted for the button:
+ **white** `#FFFFFF` and **blue** `#4285F4`

-
### Fun Facts 📈📊
Unlike other "social media" companies,
@@ -431,29 +502,29 @@ The following is a quick list of facts
that make adding Google Auth to your App
a compelling business case:
-+ As of May 2019, there are over
-[2.5 Billion](https://www.theverge.com/2019/5/7/18528297/google-io-2019-android-devices-play-store-total-number-statistic-keynote)
-_active_ Android devices;
-[87%](https://www.idc.com/promo/smartphone-market-share/os) global market share.
-All these people have Google Accounts in order to use Google services.
-+ YouTube has
-[2 billion](https://www.businessofapps.com/data/youtube-statistics/)
-monthly active YouTube users (_signed in with a Google Account_).
-+ Gmail has
-[1.5 Billion](https://www.thenewsminute.com/article/googles-gmail-turns-15-now-has-over-15-billion-monthly-active-users-99275)
-monthly active users a
-[27% share](https://seotribunal.com/blog/google-stats-and-facts)
- of the global email client market.
-+ [65%](https://techjury.net/stats-about/gmail-statistics)
-of Small and Medium sized businesses use Google Apps for business.
-+ [90%+](https://techjury.net/stats-about/gmail-statistics)
-of startups use Gmail. This is a good _proxy_ for "early adopters".
-+ [68%](https://eu.azcentral.com/story/opinion/op-ed/joannaallhands/2017/10/09/google-classroom-changing-teachers-students-education/708246001/)
-of schools in the US use Google Classroom and related G-suite products.
-So the _next_ generation of internet/app users have Google accounts.
-+ Google has
-[90.46%](https://seotribunal.com/blog/google-stats-and-facts/)
-of the search engine market share worldwide. 95.4% on Mobile.
+- As of May 2019, there are over
+ [2.5 Billion](https://www.theverge.com/2019/5/7/18528297/google-io-2019-android-devices-play-store-total-number-statistic-keynote)
+ _active_ Android devices;
+ [87%](https://www.idc.com/promo/smartphone-market-share/os) global market share.
+ All these people have Google Accounts in order to use Google services.
+- YouTube has
+ [2 billion](https://www.businessofapps.com/data/youtube-statistics/)
+ monthly active YouTube users (_signed in with a Google Account_).
+- Gmail has
+ [1.5 Billion](https://www.thenewsminute.com/article/googles-gmail-turns-15-now-has-over-15-billion-monthly-active-users-99275)
+ monthly active users a
+ [27% share](https://seotribunal.com/blog/google-stats-and-facts)
+ of the global email client market.
+- [65%](https://techjury.net/stats-about/gmail-statistics)
+ of Small and Medium sized businesses use Google Apps for business.
+- [90%+](https://techjury.net/stats-about/gmail-statistics)
+ of startups use Gmail. This is a good _proxy_ for "early adopters".
+- [68%](https://eu.azcentral.com/story/opinion/op-ed/joannaallhands/2017/10/09/google-classroom-changing-teachers-students-education/708246001/)
+ of schools in the US use Google Classroom and related G-suite products.
+ So the _next_ generation of internet/app users have Google accounts.
+- Google has
+ [90.46%](https://seotribunal.com/blog/google-stats-and-facts/)
+ of the search engine market share worldwide. 95.4% on Mobile.
Of the 4.5 billion internet users (58% of the world population),
around 3.2 billion (72%) have a Google account.
@@ -469,7 +540,7 @@ This is **`false`** and App developers have 100% control
over what data is sent to (stored by) Google.
An App can use Google Auth to _authenticate_ a person
(_identify them and get read-only access
- to their personal details like **first name** and **email address**_)
+to their personal details like **first name** and **email address**_)
without sending any data to Google.
Yes, it will mean that Google "knows" that the person is _using_ your App,
but it will not give Google any insight into _how_ they are using it
diff --git a/config/test.exs b/config/test.exs
index 0dd4ec4..3df37b5 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -1,4 +1,4 @@
-use Mix.Config
+import Config
config :elixir_auth_google,
client_id: "631770888008-6n0oruvsm16kbkqg6u76p5cv5kfkcekt.apps.googleusercontent.com",
diff --git a/create-google-app-guide.md b/create-google-app-guide.md
index 557de78..d85c706 100644
--- a/create-google-app-guide.md
+++ b/create-google-app-guide.md
@@ -1,176 +1,102 @@
# Creating a Google Application for OAuth2 Authentication
-This is a step-by-step guide
-for creating a Google App from scratch
-so that you can obtain the API keys
-to add Google OAuth2 Authentication
-to your Elixir App
-and save the credentials to environment variables.
+This is a step-by-step guide for creating a Google App from scratch so that you can obtain the API keys to add Google OAuth2 Authentication to your Elixir App and save the credentials to environment variables.
+
Our guide follows the _official_ docs:
-https://developers.google.com/identity/sign-in/web/server-side-flow
-We've added detail and screenshots to the steps
-because some people have found the official Google API docs confusing.
-_This_ guide is checked periodically by the @dwyl team/community,
-but Google are known to occasionally change their UI/Workflow,
-so if anything has changed, or there are extra/fewer steps,
-[please let us know!](https://github.com/dwyl/elixir-auth-google/issues)
+
+
+We've added detail and screenshots to the steps because some people have found the official Google API docs confusing.
+
+_This_ guide is checked periodically by the @dwyl team/community, but Google are known to occasionally change their UI/Workflow, so if anything has changed, or there are extra/fewer steps, [please let us know!](https://github.com/dwyl/elixir-auth-google/issues)
## 1. Create a New Project
-In your preferred web browser,
-visit:
-https://console.developers.google.com
-and ensure you are authenticated with your Google Account
-so you can see your "API & Services Dashboard":
+In your preferred web browser, visit:
+
+
+
+and ensure you are authenticated with your Google Account so you can see your "API & Services Dashboard":
If you don't already have a Google APIs project for your Elixir App,
click the **CREATE** button on the dashboard.
-
## 2. Define the Details for your New Project (App)
-Enter the details for your App's **Project name**
-and where appropriate input any additional/relevant info:
+
-
+
Click the **CREATE** button to create your project.
-
## 3. OAuth Consent Screen
-After creating the New Project,
-the UI will return to the APIs dashboard
-and the name of your app will appear in the top menu.
-
-Click the **OAuth Consent Screen** from the left side menu:
-
-
+Click on "Google Cloud", then "Dashboard", then
+**OAuth Consent Screen** from the left side menu:
-Make the Application **`Public`** (_the default option_) and
-input the same name as you used for your application in step 1.
-Upload an image if you have one (_e.g: the icon/logo for your app_):
+
-
+Set "external":
-Leave the "**Scopes for Google APIs**" set to the default
-**email**, **profile** and **openid**.
+
-No other data is required at this point, so skip the rest.
+and enter the details for your App's **Project name** and where appropriate input any additional/relevant info: App name, the User support email and Developper contanct information.
-Scroll down to the bottom and click "**Save**":
-
+## 4. Create Credentials
-This will take you to the API Credentials page.
+Click on "Google Cloud", then "Dashboard", then "APIs and services" then "Credentials", then "+ CREATE CREDENTIALS", then "OAuth client ID".
-## 4. Create Credentials
+
Click the **Create Credentials** button:
-
-That will popup a menu from which you will select **OAuth Client ID**.
-
-You will see a form that allows you to specify
-the details of your App for the credentials.
-
-
-
-+ Application Type: Web application
-+ Name: Elixir Auth Server
-+ Authorized JavaScript origins:
-http://localhost:4000
-(_the default for a Phoenix app on your local dev machine.
- you can add your "production" URL later._)
-+ Authorized redirect URIs:
-http://localhost:4000/auth/google/callback
-(_the endpoint to redirect to once authentication is successful.
- again, add your production URL once you have auth working on `localhost`_)
-
-> Ensure you hit the enter key after pasting/typing
-the URIs to ensure they are saved.
-A common stumbling block is that URIs aren't saved. See:
-https://stackoverflow.com/questions/24363041/redirect-uri-in-google-cloud-console-doesnt-save
-Once you have input the relevant data click the **Create** button.
+- Application Type: **Web application**
+- Name: "_the one you want_"
+- Authorized redirect URIs: **the same as the one yo udefined in your router**:
+ http://localhost:4000/auth/google/callback
+ (_the endpoint to redirect to once authentication is successful.
+ :exclamation: add your production URL once you have auth working on `localhost`_)
+
+Then save your credentials in `env. file:
-> This form/step can be confusing at first,
-but essentially you can have multiple credentials
-for the same project,
-e.g: if you had a Native Android App
-you would create a new set of credentials
-to ensure a separation of concerns between
-server and client implementations.
-For now just create the server (Elixir) credentials.
-
-
-## 5. Download the OAuth Client Credentials
-
-After you click the **Create** button
-in the **Create OAuth client ID** form (_step 4 above_),
-you will be shown your OAuth client Credentials:
-
-
-
-Download the credentials, e.g:
-
-+ Client ID: 631770888008-6n0oruvsm16kbkqg6u76p5cv5kfkcekt.apps.googleusercontent.com
-+ Client Secret: MHxv6-RGF5nheXnxh1b0LNDq
-
-> ⚠️ Don't worry, these keys aren't valid.
-We deleted them immediately after capturing the screenshot
-to avoid any security issues.
-Obviously treat your credentials
-like you would the username+password for your bank account;
-never share a **real** Client ID or secret on GitHub
-or any other public/insecure forum!
-
-You can also download the OAuth credentials as a json file:
-
-
-
-Example:
-```json
-{
- "web": {
- "client_id": "631770888008-6n0oruvsm16kbkqg6u76p5cv5kfkcekt.apps.googleusercontent.com",
- "project_id": "elixir-auth-demo",
- "auth_uri": "https://accounts.google.com/o/oauth2/auth",
- "token_uri": "https://oauth2.googleapis.com/token",
- "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
- "client_secret": "MHxv6-RGF5nheXnxh1b0LNDq",
- "redirect_uris": [
- "http://localhost:4000/auth/google/callback"
- ],
- "javascript_origins": [
- "http://localhost:4000"
- ]
- }
-}
+```env
+# .env
+export GOOGLE_CLIENT_ID="935113971xxx.apps.googleusercontent.com"
+export GOOGLE_CLIENT_SECRET="GOCSPX-5jxxx"
```
-> Again, for security reasons,
-these credentials were
-invalidated _immediately_ after downloading.
-But this is what the file looks like.
+> **Note**: the "client-id" is acccessible but not the secret. In case you missed it, you can always generate another one with "RESET SECRET"
+
+
+
+:red_circle: Authorized JavaScript origins: **with this library, you don't use Google's SDK so you don't need this**.
+
+> **Note**: if you want ot use **One tap**, then you need to pass an authorized url: in **DEV** mode, it is **AND** .
+Then, don't forget to:
-Return to step 3 of the
-[README.md](https://github.com/dwyl/elixir-auth-google/blob/master/README.md)
+
+> A common stumbling block is that URIs aren't saved. See:
+> https://stackoverflow.com/questions/24363041/redirect-uri-in-google-cloud-console-doesnt-save
-
+Once you have input the relevant data click the **Create** button.
+
+> This form/step can be confusing at first, but essentially you can have multiple credentials for the same project, e.g: if you had a Native Android App you would create a new set of credentials to ensure a separation of concerns between server and client implementations.
+> For now just create the server (Elixir) credentials.
-# Note
+## Update for production mode
When you ship your app to your Production environment,
-you will need to re-visit steps 3 & 4
-to update your app settings URL & callback
-to reflect the URl where you are deploying your app e.g:
+you will need to re-visit step 2 and 4 to update your app settings URL & callback to reflect the URl where you are deploying your app e.g:

-In our case
-https://elixir-auth-google-demo.herokuapp.com
-and
-https://elixir-auth-google-demo.herokuapp.com/auth/google/callback
+In our case:
+
+
+
+and:
+
+
diff --git a/images/Screenshot 2022-10-27 at 11.24.10.png b/images/Screenshot 2022-10-27 at 11.24.10.png
new file mode 100644
index 0000000..3076c7e
Binary files /dev/null and b/images/Screenshot 2022-10-27 at 11.24.10.png differ
diff --git a/images/Screenshot 2022-10-27 at 11.25.41.png b/images/Screenshot 2022-10-27 at 11.25.41.png
new file mode 100644
index 0000000..345ae4a
Binary files /dev/null and b/images/Screenshot 2022-10-27 at 11.25.41.png differ
diff --git a/images/Screenshot 2022-10-27 at 11.32.42.png b/images/Screenshot 2022-10-27 at 11.32.42.png
new file mode 100644
index 0000000..b3d43f7
Binary files /dev/null and b/images/Screenshot 2022-10-27 at 11.32.42.png differ
diff --git a/images/Screenshot 2022-10-27 at 14.04.54.png b/images/Screenshot 2022-10-27 at 14.04.54.png
new file mode 100644
index 0000000..926613e
Binary files /dev/null and b/images/Screenshot 2022-10-27 at 14.04.54.png differ
diff --git a/images/Screenshot 2022-10-27 at 14.06.37.png b/images/Screenshot 2022-10-27 at 14.06.37.png
new file mode 100644
index 0000000..7cb280f
Binary files /dev/null and b/images/Screenshot 2022-10-27 at 14.06.37.png differ
diff --git a/images/Screenshot 2022-10-27 at 14.10.24.png b/images/Screenshot 2022-10-27 at 14.10.24.png
new file mode 100644
index 0000000..2528ceb
Binary files /dev/null and b/images/Screenshot 2022-10-27 at 14.10.24.png differ
diff --git a/images/Screenshot 2022-10-27 at 14.11.44.png b/images/Screenshot 2022-10-27 at 14.11.44.png
new file mode 100644
index 0000000..e741da3
Binary files /dev/null and b/images/Screenshot 2022-10-27 at 14.11.44.png differ
diff --git a/images/Screenshot 2022-10-27 at 14.12.48.png b/images/Screenshot 2022-10-27 at 14.12.48.png
new file mode 100644
index 0000000..73fd614
Binary files /dev/null and b/images/Screenshot 2022-10-27 at 14.12.48.png differ
diff --git a/images/Screenshot 2022-10-27 at 14.18.31.png b/images/Screenshot 2022-10-27 at 14.18.31.png
new file mode 100644
index 0000000..4d8eb12
Binary files /dev/null and b/images/Screenshot 2022-10-27 at 14.18.31.png differ
diff --git a/images/Screenshot 2022-10-27 at 18.45.46.png b/images/Screenshot 2022-10-27 at 18.45.46.png
new file mode 100644
index 0000000..436d726
Binary files /dev/null and b/images/Screenshot 2022-10-27 at 18.45.46.png differ
diff --git a/images/Screenshot 2022-10-27 at 18.58.15.png b/images/Screenshot 2022-10-27 at 18.58.15.png
new file mode 100644
index 0000000..2fed56f
Binary files /dev/null and b/images/Screenshot 2022-10-27 at 18.58.15.png differ
diff --git a/lib/elixir_auth_google.ex b/lib/elixir_auth_google.ex
index d544342..3ce8975 100644
--- a/lib/elixir_auth_google.ex
+++ b/lib/elixir_auth_google.ex
@@ -2,6 +2,7 @@ defmodule ElixirAuthGoogle do
@moduledoc """
Minimalist Google OAuth Authentication for Elixir Apps.
Extensively tested, documented, maintained and in active use in production.
+ It exposes two functions, `generate_oauth_url/2` and `get_profile/1`.
"""
@google_auth_url "https://accounts.google.com/o/oauth2/v2/auth?response_type=code"
@google_token_url "https://oauth2.googleapis.com/token"
@@ -36,18 +37,25 @@ defmodule ElixirAuthGoogle do
@doc """
`generate_redirect_uri/1` generates the Google redirect uri based on conn
"""
- @spec generate_redirect_uri(conn) :: String.t()
def generate_redirect_uri(conn) do
get_baseurl_from_conn(conn) <> get_app_callback_url()
end
+ @doc """
+ Same as `generate_oauth_url/2` with `state` query parameter
+ """
+
+ # def generate_oauth_url(conn, state) when is_binary(state) do
+ # params = URI.encode_query(%{state: state}, :rfc3986)
+ # generate_oauth_url(conn) <> "{params}"
+ # end
+
@doc """
`generate_oauth_url/1` creates the Google OAuth2 URL with client_id, scope and
redirect_uri which is the URL Google will redirect to when auth is successful.
This is the URL you need to use for your "Login with Google" button.
See step 5 of the instructions.
"""
- @spec generate_oauth_url(conn) :: String.t()
def generate_oauth_url(conn) do
query = %{
client_id: google_client_id(),
@@ -56,39 +64,82 @@ defmodule ElixirAuthGoogle do
}
params = URI.encode_query(query, :rfc3986)
-
"#{@google_auth_url}{params}"
end
- @doc """
- Same as `generate_oauth_url/1` with `state` query parameter
- """
- def generate_oauth_url(conn, state) when is_binary(state) do
- params = URI.encode_query(%{state: state}, :rfc3986)
- generate_oauth_url(conn) <> "{params}"
- end
-
@doc """
`get_token/2` encodes the secret keys and authorization code returned by Google
and issues an HTTP request to get a person's profile data.
**TODO**: we still need to handle the various failure conditions >> issues/16
"""
- @spec get_token(String.t(), conn) :: {:ok, map} | {:error, any}
- def get_token(code, conn) do
- body =
- Jason.encode!(%{
- client_id: google_client_id(),
- client_secret: google_client_secret(),
- redirect_uri: generate_redirect_uri(conn),
- grant_type: "authorization_code",
- code: code
- })
-
- inject_poison().post(@google_token_url, body)
- |> parse_body_response()
+ # <----- RM
+ # @spec get_token(String.t(), conn) :: {:ok, map} | {:error, any}
+ # def get_token(code, conn) do
+ # body =
+ # Jason.encode!(%{
+ # client_id: google_client_id(),
+ # client_secret: google_client_secret(),
+ # redirect_uri: generate_redirect_uri(conn),
+ # grant_type: "authorization_code",
+ # code: code
+ # })
+
+ # inject_poison().post(@google_token_url, body)
+ # |> parse_body_response()
+ # end
+
+ # <---- +
+ def get_profile(code, conn) do
+ Jason.encode!(%{
+ client_id: google_client_id(),
+ client_secret: google_client_secret(),
+ redirect_uri: generate_redirect_uri(conn),
+ grant_type: "authorization_code",
+ code: code
+ })
+ |> then(fn body ->
+ inject_poison().post(@google_token_url, body)
+ |> parse_status()
+ |> parse_response()
+ end)
+ end
+
+ # Note: failure status are 400, 401, 404 depending on which input is corrupted.
+ # all tested: client_id, secret, redirect_uri, code, @google_token_url, @google-user_profile, params
+ def parse_status({:ok, %{status_code: 200}} = response) do
+ parse_body_response(response)
+ end
+
+ def parse_status({:ok, _}), do: {:error, :bad_request}
+ # or more verbose with error status
+ # def parse_status({:ok, status}) do
+ # case status do
+ # %{status_code: 404} -> {:error, :wrong_url}
+ # %{status_code: 401} -> {:error, :unauthorized}
+ # %{status_code: 400} -> {:error, :wrong_code}
+ # _ -> {:error, :unknown_error}
+ # end
+ # end
+
+ # def parse_body_response({:error, err}), do: {:error, err}
+
+ def parse_body_response({:ok, %{body: nil}}), do: {:error, :no_body}
+
+ def parse_body_response({:ok, %{body: body}}) do
+ {:ok,
+ body
+ |> Jason.decode!()
+ |> convert()}
end
+ def convert(str_key_map) do
+ for {key, val} <- str_key_map, into: %{}, do: {String.to_atom(key), val}
+ end
+
+ def parse_response({:error, response}), do: {:error, response}
+ def parse_response({:ok, response}), do: get_user_profile(response.access_token)
+
@doc """
`get_user_profile/1` requests the Google User's userinfo profile data
providing the access_token received in the `get_token/1` above.
@@ -97,51 +148,67 @@ defmodule ElixirAuthGoogle do
**TODO**: we still need to handle the various failure conditions >> issues/16
At this point the types of errors we expect are HTTP 40x/50x responses.
"""
- @spec get_user_profile(String.t()) :: {:ok, map} | {:error, any}
- def get_user_profile(token) do
- params = URI.encode_query(%{access_token: token}, :rfc3986)
- "#{@google_user_profile}?#{params}"
- |> inject_poison().get()
- |> parse_body_response()
+ # @spec get_user_profile(String.t()) :: {:ok, map} | {:error, any}
+ # def get_user_profile(token) do
+ # params = URI.encode_query(%{access_token: token}, :rfc3986)
+
+ # "#{@google_user_profile}?#{params}"
+ # |> inject_poison().get()
+ # |> parse_body_response()
+ # end
+
+ # <---- CHANGED
+ def get_user_profile(access_token) do
+ access_token
+ |> encode()
+ |> then(fn params ->
+ (@google_user_profile <> "?" <> params)
+ |> inject_poison().get()
+ |> parse_status()
+ end)
end
+ # <---- +
+ def encode(token), do: URI.encode_query(%{access_token: token}, :rfc3986)
+
@doc """
`parse_body_response/1` parses the response returned by Google
so your app can use the resulting JSON.
"""
- @spec parse_body_response({atom, String.t()} | {:error, any}) :: {:ok, map} | {:error, any}
- def parse_body_response({:error, err}), do: {:error, err}
-
- def parse_body_response({:ok, response}) do
- body = Map.get(response, :body)
- # make keys of map atoms for easier access in templates
- if body == nil do
- {:error, :no_body}
- else
- {:ok, str_key_map} = Jason.decode(body)
- atom_key_map = for {key, val} <- str_key_map, into: %{}, do: {String.to_atom(key), val}
- {:ok, atom_key_map}
- end
-
- # https://stackoverflow.com/questions/31990134
- end
- defp google_client_id do
+ # @spec parse_body_response({atom, String.t()} | {:error, any}) :: {:ok, map} | {:error, any}
+ # def parse_body_response({:error, err}), do: {:error, err}
+
+ # def parse_body_response({:ok, response}) do
+ # body = Map.get(response, :body)
+ # # make keys of map atoms for easier access in templates
+ # if body == nil do
+ # {:error, :no_body}
+ # else
+ # {:ok, str_key_map} = Jason.decode(body)
+ # atom_key_map = for {key, val} <- str_key_map, into: %{}, do: {String.to_atom(key), val}
+ # {:ok, atom_key_map}
+ # end
+
+ # # https://stackoverflow.com/questions/31990134
+ # end
+
+ def google_client_id do
System.get_env("GOOGLE_CLIENT_ID") || Application.get_env(:elixir_auth_google, :client_id)
end
- defp google_client_secret do
+ def google_client_secret do
System.get_env("GOOGLE_CLIENT_SECRET") ||
Application.get_env(:elixir_auth_google, :client_secret)
end
- defp google_scope do
+ def google_scope do
System.get_env("GOOGLE_SCOPE") || Application.get_env(:elixir_auth_google, :google_scope) ||
@default_scope
end
- defp get_app_callback_url do
+ def get_app_callback_url do
System.get_env("GOOGLE_CALLBACK_PATH") ||
Application.get_env(:elixir_auth_google, :callback_path) || @default_callback_path
end
diff --git a/lib/httpoison_mock.ex b/lib/httpoison_mock.ex
index bf29853..4933c5d 100644
--- a/lib/httpoison_mock.ex
+++ b/lib/httpoison_mock.ex
@@ -8,14 +8,21 @@ defmodule ElixirAuthGoogle.HTTPoisonMock do
get/1 passing in the wrong_token is used to test failure in the auth process.
Obviously, don't invoke it from your App unless you want people to see fails.
"""
- def get("https://www.googleapis.com/oauth2/v3/userinfo?access_token=wrong_token") do
- {:error, :bad_request}
+ @wrong_url "https://www.googleapis.com/oauth2/v3/userinfo?access_token=wrong_token"
+ def get(@wrong_url) do
+ {:ok, %{status_code: 400}}
+ end
+
+ @bad_profile "https://www.googleapis.com/oauth2/v3/userinfo?access_token=bad_profile"
+ def get(@bad_profile) do
+ {:ok, {:error, :bad_request}}
end
# get/1 using a dummy _url to test body decoding.
def get(_url) do
{:ok,
%{
+ status_code: 200,
body:
Jason.encode!(%{
email: "nelson@gmail.com",
@@ -33,7 +40,24 @@ defmodule ElixirAuthGoogle.HTTPoisonMock do
@doc """
post/2 passing in dummy _url & _body to test return of access_token.
"""
- def post(_url, _body) do
- {:ok, %{body: Jason.encode!(%{access_token: "token1"})}}
+
+ def post(_url, body) do
+ case Jason.decode!(body) do
+ %{"code" => "body_nil"} ->
+ {:ok, %{status_code: 200, body: nil}}
+
+ %{"code" => "bad_profile"} ->
+ {:ok, %{status_code: 200, body: Jason.encode!(%{access_token: "bad_profile"})}}
+
+ %{"code" => "123"} ->
+ {:ok,
+ %{
+ status_code: 200,
+ body: Jason.encode!(%{access_token: "token1"})
+ }}
+
+ %{"code" => "wrong_token"} ->
+ {:ok, %{status_code: 400}}
+ end
end
end
diff --git a/mix.exs b/mix.exs
index 5b6670f..55dad5c 100644
--- a/mix.exs
+++ b/mix.exs
@@ -2,13 +2,13 @@ defmodule ElixirAuthGoogle.MixProject do
use Mix.Project
@description "Minimalist Google OAuth Authentication for Elixir Apps"
- @version "1.6.3"
+ @version "1.6.4"
def project do
[
app: :elixir_auth_google,
version: @version,
- elixir: ">= 1.11.0",
+ elixir: ">= 1.14.1",
start_permanent: Mix.env() == :prod,
deps: deps(),
description: @description,
@@ -33,17 +33,17 @@ defmodule ElixirAuthGoogle.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
- {:credo, "~> 1.5", only: [:dev, :test], runtime: false},
+ {:credo, "~> 1.6", only: [:dev, :test], runtime: false},
{:httpoison, "~> 1.8.0"},
- {:jason, "~> 1.2"},
+ {:jason, "~> 1.4"},
# tracking test coverage
- {:excoveralls, "~> 0.14.1", only: [:test, :dev]},
+ {:excoveralls, "~> 0.15", only: [:test, :dev]},
# mock stuffs in test
- {:mock, "~> 0.3.0", only: :test},
+ {:mock, "~> 0.3.7", only: :test},
# documentation
- {:ex_doc, "~> 0.25.3", only: :dev}
+ {:ex_doc, "~> 0.29", only: :dev}
]
end
diff --git a/mix.lock b/mix.lock
index 6979baa..f75fd83 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,24 +1,24 @@
%{
- "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
- "certifi": {:hex, :certifi, "2.8.0", "d4fb0a6bb20b7c9c3643e22507e42f356ac090a1dcea9ab99e27e0376d695eba", [:rebar3], [], "hexpm", "6ac7efc1c6f8600b08d625292d4bbf584e14847ce1b6b5c44d983d273e1097ea"},
- "credo": {:hex, :credo, "1.6.2", "2f82b29a47c0bb7b72f023bf3a34d151624f1cbe1e6c4e52303b05a11166a701", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "ae9dc112bc368e7b145c547bec2ed257ef88955851c15057c7835251a17211c6"},
+ "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
+ "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
+ "credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"},
"earmark": {:hex, :earmark, "1.4.9", "837e4c1c5302b3135e9955f2bbf52c6c52e950c383983942b68b03909356c0d9", [:mix], [{:earmark_parser, ">= 1.4.9", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "0d72df7d13a3dc8422882bed5263fdec5a773f56f7baeb02379361cb9e5b0d8e"},
- "earmark_parser": {:hex, :earmark_parser, "1.4.15", "b29e8e729f4aa4a00436580dcc2c9c5c51890613457c193cc8525c388ccb2f06", [:mix], [], "hexpm", "044523d6438ea19c1b8ec877ec221b008661d3c27e3b848f4c879f500421ca5c"},
- "ex_doc": {:hex, :ex_doc, "0.25.3", "3edf6a0d70a39d2eafde030b8895501b1c93692effcbd21347296c18e47618ce", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "9ebebc2169ec732a38e9e779fd0418c9189b3ca93f4a676c961be6c1527913f5"},
- "excoveralls": {:hex, :excoveralls, "0.14.4", "295498f1ae47bdc6dce59af9a585c381e1aefc63298d48172efaaa90c3d251db", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e3ab02f2df4c1c7a519728a6f0a747e71d7d6e846020aae338173619217931c1"},
+ "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"},
+ "ex_doc": {:hex, :ex_doc, "0.29.0", "4a1cb903ce746aceef9c1f9ae8a6c12b742a5461e6959b9d3b24d813ffbea146", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "f096adb8bbca677d35d278223361c7792d496b3fc0d0224c9d4bc2f651af5db1"},
+ "excoveralls": {:hex, :excoveralls, "0.15.0", "ac941bf85f9f201a9626cc42b2232b251ad8738da993cf406a4290cacf562ea4", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9631912006b27eca30a2f3c93562bc7ae15980afb014ceb8147dc5cdd8f376f1"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
- "hackney": {:hex, :hackney, "1.18.0", "c4443d960bb9fba6d01161d01cd81173089686717d9490e5d3606644c48d121f", [:rebar3], [{:certifi, "~>2.8.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "9afcda620704d720db8c6a3123e9848d09c87586dc1c10479c42627b905b5c5e"},
- "httpoison": {:hex, :httpoison, "1.8.0", "6b85dea15820b7804ef607ff78406ab449dd78bed923a49c7160e1886e987a3d", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "28089eaa98cf90c66265b6b5ad87c59a3729bea2e74e9d08f9b51eb9729b3c3a"},
+ "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
+ "httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
- "jason": {:hex, :jason, "1.2.2", "ba43e3f2709fd1aa1dce90aaabfd039d000469c05c56f0b8e31978e03fa39052", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "18a228f5f0058ee183f29f9eae0805c6e59d61c3b006760668d8d18ff0d12179"},
- "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
- "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"},
+ "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
+ "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
+ "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
"meck": {:hex, :meck, "0.9.2", "85ccbab053f1db86c7ca240e9fc718170ee5bda03810a6292b5306bf31bae5f5", [:rebar3], [], "hexpm", "81344f561357dc40a8344afa53767c32669153355b626ea9fcbc8da6b3045826"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"},
- "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"},
+ "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"poison": {:hex, :poison, "5.0.0", "d2b54589ab4157bbb82ec2050757779bfed724463a544b6e20d79855a9e43b24", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "11dc6117c501b80c62a7594f941d043982a1bd05a1184280c0d9166eb4d8d3fc"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
diff --git a/test/elixir_auth_google_test.exs b/test/elixir_auth_google_test.exs
index 50a9c4e..e28d1ec 100644
--- a/test/elixir_auth_google_test.exs
+++ b/test/elixir_auth_google_test.exs
@@ -16,8 +16,8 @@ defmodule ElixirAuthGoogleTest do
test "get_baseurl_from_conn(conn) detects the URL for production" do
conn = %{
- host: "dwyl.com",
- port: 80
+ host: "dwyl.com"
+ # port: 80
}
assert ElixirAuthGoogle.get_baseurl_from_conn(conn) == "https://dwyl.com"
@@ -44,63 +44,53 @@ defmodule ElixirAuthGoogleTest do
assert url =~ "http%3A%2F%2Flocalhost%3A4000"
end
- test "get Google login url with state" do
+ test "get_profile/1" do
conn = %{
host: "localhost",
port: 4000
}
- url = ElixirAuthGoogle.generate_oauth_url(conn, "state1")
- id = System.get_env("GOOGLE_CLIENT_ID")
- id_from_config = Application.get_env(:elixir_auth_google, :client_id)
-
- assert id == id_from_config
-
- expected =
- "https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=" <>
- id <>
- "&redirect_uri=http%3A%2F%2Flocalhost%3A4000%2Fauth%2Fgoogle%2Fcallback&scope=profile%20email&state=state1"
+ res = %{
+ 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"
+ }
- assert url == expected
+ assert ElixirAuthGoogle.get_profile("123", conn) == {:ok, res}
end
- test "get Google token" do
+ test "no_body" do
conn = %{
host: "localhost",
port: 4000
}
- {:ok, res} = ElixirAuthGoogle.get_token("ok_code", conn)
- assert res == %{access_token: "token1"}
+ assert ElixirAuthGoogle.get_profile("body_nil", conn) == {:error, :no_body}
end
- test "get Google token (config redirect uri)" do
+ test "return error with incorrect token" do
conn = %{
host: "localhost",
port: 4000
}
- {:ok, res} = ElixirAuthGoogle.get_token("ok_code", conn)
- assert res == %{access_token: "token1"}
- end
+ # fails at HTTP.GET for get_user_profile
+ assert ElixirAuthGoogle.get_profile("bad_profile", conn) ==
+ {:error, :bad_request}
- test "get_user_profile/1" do
- res = %{
- 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"
- }
-
- assert ElixirAuthGoogle.get_user_profile("123") == {:ok, res}
+ # fails at HTTP.POST
+ assert ElixirAuthGoogle.get_profile("wrong_token", conn) ==
+ {:error, :bad_request}
end
- test "return error with incorrect token" do
- assert ElixirAuthGoogle.get_user_profile("wrong_token") == {:error, :bad_request}
+ test "get_user_profile return error with incorrect token for the Mock tests" do
+ assert ElixirAuthGoogle.get_user_profile("wrong_token") ==
+ {:error, :bad_request}
end
test "generate_redirect_uri(conn) generate correct callback url" do
@@ -121,7 +111,7 @@ defmodule ElixirAuthGoogleTest do
mock_get_env = fn :elixir_auth_google, :callback_path -> "/special/callback" end
- with_mock Application, [get_env: mock_get_env] do
+ with_mock Application, get_env: mock_get_env do
assert ElixirAuthGoogle.generate_redirect_uri(conn) ==
"https://foobar.com/special/callback"
end
@@ -135,7 +125,7 @@ defmodule ElixirAuthGoogleTest do
mock_get_env = fn "GOOGLE_CALLBACK_PATH" -> "/very/special/callback" end
- with_mock System, [get_env: mock_get_env] do
+ with_mock System, get_env: mock_get_env do
assert ElixirAuthGoogle.generate_redirect_uri(conn) ==
"https://foobar.com/very/special/callback"
end