Skip to content

Instantly share code, notes, and snippets.

@danielberkompas
Created March 4, 2017 20:44
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danielberkompas/9822197a8cec66ff011d24ab32e687cf to your computer and use it in GitHub Desktop.
Save danielberkompas/9822197a8cec66ff011d24ab32e687cf to your computer and use it in GitHub Desktop.
An example of how to hack together Ueberauth.Auth structs
defmodule MyApp.Auth do
@moduledoc """
Creates `Ueberauth.Auth` structs from OAuth responses.
This module is an ugly hack which is necessary because `Ueberauth` doesn't provide
the necessary hooks to get such a struct without giving it control of the whole
callback phase. We can't do this in the API because all the mobile app can give us
is the OAuth token.
Most of the code was lifted from Ueberauth, with minor changes as needed.
"""
def new(type, token, secret \\ nil)
def new(:facebook, token, _secret) do
{_module, config} = Application.get_env(:ueberauth, Ueberauth)[:providers][:facebook]
client = Ueberauth.Strategy.Facebook.OAuth.client
token = OAuth2.AccessToken.new(token, client)
case OAuth2.AccessToken.get(token, "/me?fields=#{config[:profile_fields]}") do
{:ok, %OAuth2.Response{status_code: status_code, body: user}} when status_code in 200..399 ->
{:ok, parse(:facebook, user, token)}
{:ok, %OAuth2.Response{status_code: 401}} ->
{:error, "Not authorized."}
{:error, %OAuth2.Error{reason: reason}} ->
{:error, reason}
_other ->
{:error, "An unknown error occurred."}
end
end
def new(:twitter, token, secret) do
params = [include_entities: false, skip_status: true, include_email: true]
case Ueberauth.Strategy.Twitter.OAuth.get("/1.1/account/verify_credentials.json", params, {token, secret}) do
{:ok, {{_, status_code, _}, _, body}} when status_code in 200..399 ->
user =
body
|> List.to_string
|> Poison.decode!
{:ok, parse(:twitter, user, {token, secret})}
{:ok, {{_, 401, _}, _, _}} ->
{:error, "Not authorized."}
{:ok, {_, _, body}} ->
body =
body
|> List.to_string
|> Poison.decode!
error = List.first(body["errors"])
{:error, error}
end
end
def new(:google, token, _secret) do
client = Ueberauth.Strategy.Google.OAuth.client
token = OAuth2.AccessToken.new(token, client)
case OAuth2.AccessToken.get(token, "https://www.googleapis.com/plus/v1/people/me/openIdConnect") do
{:ok, %OAuth2.Response{status_code: status_code, body: user}} when status_code in 200..399 ->
{:ok, parse(:google, user, token)}
{:ok, %OAuth2.Response{status_code: 401, body: _body}} ->
{:error, "Not authorized."}
{:error, %OAuth2.Error{reason: reason}} ->
{:error, reason}
end
end
defp parse(:facebook, user, token) do
scopes = token.other_params["scope"] || ""
scopes = String.split(scopes, ",")
%Ueberauth.Auth{
provider: :facebook,
strategy: Ueberauth.Strategy.Facebook,
uid: user["id"],
info: %Ueberauth.Auth.Info{
description: user["bio"],
email: user["email"],
first_name: user["first_name"],
image: "http://graph.facebook.com/#{user["id"]}/picture?type=square",
last_name: user["last_name"],
name: user["name"],
urls: %{
facebook: user["link"],
website: user["website"]
}
},
extra: %Ueberauth.Auth.Extra{
raw_info: %{
token: token,
user: user
}
},
credentials: %Ueberauth.Auth.Credentials{
expires: token.expires_at != nil,
expires_at: token.expires_at,
scopes: scopes,
token: token.access_token
}
}
end
defp parse(:twitter, user, {token, secret}) do
%Ueberauth.Auth{
provider: :twitter,
strategy: Ueberauth.Strategy.Twitter,
uid: user["id_str"],
info: %Ueberauth.Auth.Info{
email: user["email"],
image: user["profile_image_url"],
name: user["name"],
nickname: user["screen_name"],
description: user["description"],
urls: %{
Twitter: "https://twitter.com/#{user["screen_name"]}",
Website: user["url"]
}
},
extra: %Ueberauth.Auth.Extra{
raw_info: %{
token: token,
user: user
}
},
credentials: %Ueberauth.Auth.Credentials{
token: token,
secret: secret
}
}
end
defp parse(:google, user, token) do
scopes = String.split(token.other_params["scope"] || "", ",")
%Ueberauth.Auth{
provider: :google,
strategy: Ueberauth.Strategy.Google,
uid: user["sub"],
info: %Ueberauth.Auth.Info{
email: user["email"],
first_name: user["given_name"],
image: user["picture"],
last_name: user["family_name"],
name: user["name"],
urls: %{
profile: user["profile"],
website: user["hd"]
}
},
extra: %Ueberauth.Auth.Extra{
raw_info: %{
token: token,
user: user
}
},
credentials: %Ueberauth.Auth.Credentials{
expires: token.expires_at != nil,
expires_at: token.expires_at,
scopes: scopes,
refresh_token: token.refresh_token,
token: token.access_token
}
}
end
end
@yordis
Copy link

yordis commented Mar 6, 2017

I am getting this function OAuth2.AccessToken.get/2 is undefined or private

@scrogson
Copy link

scrogson commented Mar 7, 2017

I am getting this function OAuth2.AccessToken.get/2 is undefined or private

@yordis you are mostly likely using a more recent version of OAuth2. Use OAuth2.Client.get instead.

@mushu8
Copy link

mushu8 commented Oct 23, 2020

Hi,
I'm a beginner in elixir and trying to integrate this workflow :

  1. a client authenticate with a social provider and get a social auth token
  2. the client sends its social auth token to the elixir backend for signin
  3. the backend refreshes the social auth token by calling the social provider "refresh" endpoint
  4. the backend issues a jwt token and sends it as answer to its client.

Could you update this gist with a recent version of OAuth2 ?

@danielberkompas
Copy link
Author

You can check the latest version of the uberauth docs for how to do part of what you’re describing. https://hexdocs.pm/ueberauth/Ueberauth.html

For the rest, it would be quite a bit of work to update this in the way you’re asking, and I don’t have time, unfortunately. I recommend you look through uberauth docs and blog posts for help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment