View Source Ueberauth.Strategy behaviour (Überauth v0.10.8)
The Strategy is the work-horse of the system.
Strategies are implemented outside this library to meet your needs, the strategy provides a consistent API and behaviour.
Each strategy operates through two phases.
request phase
callback phase
These phases can be understood with the following psuedocode.
request-phase
Request Phase
request (for the request phase - default /auth/:provider)
|> relevant_strategy.handle_request!(conn)
|> continue with request plug pipeline
The request phase follows normal plug pipeline behaviour. The request will not continue if the strategy halted the connection.
callback-phase
Callback Phase
request (for a callback phase - default /auth/:provider/callback)
|> relevant_strategy.handle_auth!(conn)
if connection does not have ueberauth failure
|> set ueberauth auth with relevant_strategy.auth
|> cleanup from the strategy with relevant_strategy.handle_cleanup!
|> continue with plug pipeline
The callback phase is essentially a decorator and does not usually redirect or halt the request. Its result is that one of two cases will end up in your connections assigns when it reaches your controller.
- On Failure - An
Ueberauth.Failure
struct is available at:ueberauth_failure
- On Success - An
Ueberauth.Auth
struct is available at:ueberauth_auth
an-example
An example
The simplest example is an email/password strategy. This does not intercept
the request and just decorates it with the Ueberauth.Auth
struct. (it is
always successful)
defmodule Ueberauth.Strategies.Identity do
use Ueberauth.Strategy
alias Ueberauth.Auth.Credentials
alias Ueberauth.Auth.Extra
def uid(conn), do: conn.params["email"]
def extra(conn), do: struct(Extra, raw_info: conn.params)
def credentials(conn) do
%Credentials{
other: %{
password: conn.params["password"],
password_confirmation: conn.params["password_confirmation"]
}
}
end
end
After the strategy has run through the handle_callback!/1
function, since
there are no errors added, Ueberauth will add the constructed auth struct to
the connection.
The Auth struct is constructed like:
def auth(conn, strategy) do
%Auth{
provider: Ueberauth.Strategy.Helpers.strategy_name(conn),
strategy: strategy,
uid: strategy.uid(conn),
info: strategy.info(conn),
extra: strategy.extra(conn),
credentials: strategy.credentials(conn)
}
end
Each component of the struct is a separate function and receives the connection object. From this Ueberauth will construct and assign the struct for processing in your own controller.
redirecting-during-the-request-phase
Redirecting during the request phase
Many strategies may require a redirect (looking at you OAuth). To do this,
implement the handle_request!/1
function.
def handle_request!(conn) do
callback_url = callback_url(conn)
redirect!(conn, callback_url)
end
callback-phase-1
Callback phase
The callback phase may not do anything other than instruct the strategy where to get the information to construct the auth struct. In that case define the functions for the components of the struct and fetch the information from the connection struct.
In the case where you do need to take some other step, the handle_callback!/1
function is where its at.
def handle_callback!(conn) do
conn
|> call_external_service_and_assign_result_to_private
end
def uid(conn) do
fetch_from_my_private_area(conn, :username)
end
def handle_cleanup!(conn) do
remove_my_private_area(conn)
end
This provides a simplistic psuedocode look at what a callback + cleanup phase might look like. By setting the result of your call to the external service in the connections private assigns, you can use that to construct the auth struct in the auth component functions. Of course, as a good citizen you also cleanup the connection before the request continues.
cleanup-phase
Cleanup phase
The cleanup phase is provided for you to be a good citizen and clean up after your strategy. During the callback phase, you may need to temporarily store information in the private section of the conn struct. Once this is done, the cleanup phase exists to cleanup that temporary storage after the strategy has everything it needs.
Implement the handle_cleanup!/1
function and return the cleaned conn struct.
adding-errors-during-callback
Adding errors during callback
You have two options when you're in the callback phase. Either you can let the connection go through and Ueberauth will construct the auth hash for you, or you can add errors.
You should add errors before you leave your handle_callback!/1
function.
def handle_callback!(conn) do
errors = []
if (something_bad), do: errors = [error("error_key", "Some message") | errors]
if (length(errors) > 0) do
set_errors!(errors)
else
conn
end
end
Once you've set errors, Ueberauth will not set the auth struct in the connections
assigns at :ueberauth_auth
, instead it will set a Ueberauth.Failure
struct at
:ueberauth_failure
with the information provided detailing the failure.
Link to this section Summary
Callbacks
Provides the credentials for the user.
Returns the default options configuration of the strategy.
Provides the extra params for the user.
The callback phase implementation for your strategy.
The cleanup phase implementation for your strategy.
The request phase implementation for your strategy.
Provides the info for the user.
Provides the uid for the user.
Functions
When defining your own strategy you should use Ueberauth.Strategy.
Link to this section Callbacks
@callback credentials(Plug.Conn.t()) :: Ueberauth.Auth.Credentials.t()
Provides the credentials for the user.
This is one of the component functions that is used to construct the auth
struct. What you return here will be in the auth struct at the credentials
key.
@callback default_options() :: keyword()
Returns the default options configuration of the strategy.
@callback extra(Plug.Conn.t()) :: Ueberauth.Auth.Extra.t()
Provides the extra params for the user.
This is one of the component functions that is used to construct the auth
struct. What you return here will be in the auth struct at the extra
key.
You would include any additional information within extra that does not fit
in either info
or credentials
@callback handle_callback!(Plug.Conn.t()) :: Plug.Conn.t()
The callback phase implementation for your strategy.
In this function you should make any external calls you need, check for
errors etc. The result of this phase is that either a failure
(Ueberauth.Failure
) will be assigned to the connections assigns at
ueberauth_failure
or an Ueberauth.Auth
struct will be constrcted and
added to the assigns at :ueberauth_auth
.
@callback handle_cleanup!(Plug.Conn.t()) :: Plug.Conn.t()
The cleanup phase implementation for your strategy.
The cleanup phase runs after the callback phase and is present to provide a mechanism to cleanup any temporary data your strategy may have placed in the connection.
@callback handle_request!(Plug.Conn.t()) :: Plug.Conn.t()
The request phase implementation for your strategy.
Setup, redirect or otherwise in here. This is an information gathering phase and should provide the end user with a way to provide the information required for your application to authenticate them.
@callback info(Plug.Conn.t()) :: Ueberauth.Auth.Info.t()
Provides the info for the user.
This is one of the component functions that is used to construct the auth
struct. What you return here will be in the auth struct at the info
key.
@callback uid(Plug.Conn.t()) :: binary() | nil
Provides the uid for the user.
This is one of the component functions that is used to construct the auth
struct. What you return here will be in the auth struct at the uid
key.
Link to this section Functions
When defining your own strategy you should use Ueberauth.Strategy.
This provides default callbacks for all required callbacks to meet the
Ueberauth.Strategy behaviour and imports some helper functions found in
Ueberauth.Strategy.Helpers
imports
Imports
- Ueberauth.Stratgey.Helpers
- Plug.Conn
default-options
Default Options
When using the strategy you can pass a keyword list for default options:
defmodule MyStrategy do
use Ueberauth.Strategy, some: "options"
# …
end
MyStrategy.default_options # [ some: "options" ]
These options are made available to your strategy at YourStrategy.default_options
.
On a per usage level, other options can also be passed to the strategy to provide
customization.
cross-site-request-forgery
Cross-Site Request Forgery
By default strategies must implement https://tools.ietf.org/html/rfc6749#section-10.12
if you wish to disable this feature, you can use the :ignores_csrf_attack
option:
defmodule MyStrategy do
use Ueberauth.Strategy,
ignores_csrf_attack: true
# …
end
We strongly recommend never disabling this feature, unless you have some technical limitations that forces you to do so.
To change the SameSite attribute of the cookie holding the state parameter, you can use the :state_param_cookie_same_site
option:
defmodule MyStrategy do
use Ueberauth.Strategy,
state_param_cookie_same_site: "None"
# …
end