Current section
Files
Jump to
Current section
Files
lib/ueberauth/strategy/linkedin.ex
defmodule Ueberauth.Strategy.LinkedIn do
@moduledoc """
LinkedIn Strategy for Überauth.
"""
use Ueberauth.Strategy,
uid_field: :id,
default_scope: "r_basicprofile r_emailaddress"
alias Ueberauth.Auth.Info
alias Ueberauth.Auth.Credentials
alias Ueberauth.Auth.Extra
@doc """
Handles initial request for LinkedIn authentication.
"""
def handle_request!(conn) do
scopes = conn.params["scope"] || option(conn, :default_scope)
opts = [ scope: scopes ]
if conn.params["state"] do
opts = Keyword.put(opts, :state, conn.params["state"])
end
opts = Keyword.put(opts, :redirect_uri, callback_url(conn))
redirect!(conn, Ueberauth.Strategy.LinkedIn.OAuth.authorize_url!(opts))
end
@doc """
Handles the callback from LinkedIn.
"""
def handle_callback!(%Plug.Conn{ params: %{ "code" => code } } = conn) do
opts = [redirect_uri: callback_url(conn)]
token = Ueberauth.Strategy.LinkedIn.OAuth.get_token!([code: code], opts)
if token.access_token == nil do
token_error = token.other_params["error"]
token_error_description = token.other_params["error_description"]
set_errors!(conn, [error(token_error, token_error_description)])
else
fetch_user(conn, token)
end
end
@doc false
def handle_callback!(conn) do
set_errors!(conn, [error("missing_code", "No code received")])
end
@doc false
def handle_cleanup!(conn) do
conn
|> put_private(:linkedin_user, nil)
|> put_private(:linkedin_token, nil)
end
@doc """
Fetches the uid field from the response.
"""
def uid(conn) do
uid_field =
conn
|> option(:uid_field)
|> to_string
conn.private.linkedin_user[uid_field]
end
@doc """
Includes the credentials from the linkedin response.
"""
def credentials(conn) do
token = conn.private.linkedin_token
%Credentials{
expires: !!token.expires_at,
expires_at: token.expires_at,
refresh_token: token.refresh_token,
token: token.access_token
}
end
@doc """
Fetches the fields to populate the info section of `Ueberauth.Auth` struct.
"""
def info(conn) do
user = conn.private.linkedin_user
%Info{
email: user["emailAddress"],
first_name: user["firstName"],
image: user["pictureUrl"],
last_name: user["lastName"]
}
end
@doc """
Stores the raw information (including the token) obtained from
the linkedin callback.
"""
def extra(conn) do
%Extra{
raw_info: %{
token: conn.private.linkedin_token,
user: conn.private.linkedin_user
}
}
end
defp skip_url_encode_option, do: [hackney: [path_encode_fun: fn(a) -> a end]]
defp user_query do
"/v1/people/~:(id,picture-url,email-address,firstName,lastName)?format=json"
end
defp fetch_user(conn, token) do
conn = put_private(conn, :linkedin_token, token)
resp = OAuth2.AccessToken.get(token, user_query, [], skip_url_encode_option)
case resp do
{ :ok, %OAuth2.Response{status_code: 401, body: _body}} ->
set_errors!(conn, [error("token", "unauthorized")])
{ :ok, %OAuth2.Response{status_code: status_code, body: user} }
when status_code in 200..399 ->
put_private(conn, :linkedin_user, user)
{ :error, %OAuth2.Error{reason: reason} } ->
set_errors!(conn, [error("OAuth2", reason)])
end
end
defp option(conn, key) do
Dict.get(options(conn), key, Dict.get(default_options, key))
end
end