The standardized multi-provider authentication library.
PAL is designed to simplify an integration of authentication into web application. It can be used with any HTTP server and any developer can extend the library, create their own authentication workflow for everything from Facebook to LDAP. PAL is inspired by OmniAuth, Friend and Passport.
If you prefer to study the code rather documentation,
the example below will show how to use the implementation of Google Login.
You also can find the complete example using PAL and Cowboy HTTP server here.
At first, you need to create the workflow.
Workflow may have an required and optional options.
We pass them and a name of the module with workflow implementation to the pal:new/2
function.
Options =
#{client_id => <<"...">>,
client_secret => <<"...">>,
redirect_uri => <<"https://localhost/...">>}.
Workflow =
pal:new(
pal_google_oauth2_authcode,
Options).
When our workflow was created, the half an job had been done.
All we now need, parse the request and pass that data to the pal:authenticate/2
function.
pal:authenticate(Data, Workflow).
%% #{access_token => <<"...">>,
%% token_type => <<"Bearer">>,
%% expires_in => 3599,
%% id_token => <<"...">>,
%% code => <<"...">>}
That's all.
When a user come to us first time, the request doesn't contain any data. We have to redirect them to an authentication provider and we do it:
pal:authenticate(#{}, Workflow).
%% {stop,{resp,303, [{<<"location">>, <<"https://accounts.google.com/...">>}], <<>>}}
For Google Login (OAuth2 Authorization Code Grant) workflow
we need to retrieve code
, state
and error
fields from the query string of request
if any of them appears, and pass that data to the pal:authenticate/2
function.
pal:authenticate(#{code => <<"...">>}, Workflow).
%% #{access_token => <<"...">>,
%% token_type => <<"Bearer">>,
%% expires_in => 3599,
%% id_token => <<"...">>,
%% code => <<"...">>}
Cowboy specific implementation parsing the data of request:
Data =
lists:foldl(
fun({Key, Val}, M) ->
maps:put(binary_to_existing_atom(Key, utf8), Val, M)
end,
#{},
pt_kvlist:with(
[<<"code">>, <<"state">>, <<"error">>],
cowboy_req:parse_qs(Req))).
We can combine more than one workflow in the sequence.
Below, we are using the access_token
(result of the first workflow execution)
to obtain the Google+ profile information.
Workflow =
pal:group(
[pal_google_oauth2_authcode, pal_google_plus_user],
Options),
pal:authenticate(Data, Workflow).
%% #{uid => <<"...">>,
%% info =>
%% #{name => <<"John Doe">>,
%% first_name => <<"John">>,
%% last_name => <<"Doe">>,
%% email => <<"john@example.com">>,
%% gender => <<"male">>,
%% image => <<"https://lh3.googleusercontent.com/...">>,
%% uri => <<"https://plus.google.com/...">>},
%% access_token => <<"...">>,
%% token_type => <<"Bearer">>,
%% expires_in => 3599,
%% id_token => <<"...">>,
%% code => <<"...">>}
Workflow is a fundamental unit of the library. It have to be defined as a module
implementing at least the pal_workflow
behaviour. Note that it is highly recommended to
also implement the pal_authentication
behaviour because it is responsible for the authentication schema creation flow.
Any workflow has a state. The pal:init/2
and pal:group/2
functions are responsible for its initialization.
They expect in the arguments: a name of the module (list of module names in case of group)
with the workflow implementation and an initial options of the workflow.
The workflow can be executed by calling pal:authenticate/{2,3}
function.
It expects in the arguments: parsed data were received with the request,
optional data from any other source (server-side data) and the previously created state of workflow.
The result would contain data representing the authentication scheme {ok, NewData}
or error with the reason {error, Reason}
or a HTTP response {stop, HttpResp}
must be passed back to a user to continue an authentication process.
The error reason is represented by the tuple with the type and the error data
in the workflow specific format (for instance, {oauth2, #{error => access_denied}}
).
uid
- An identifier unique to the given workflow. Should be stored as a binary string.- any credentials are passed through here (on the root level)
If the authenticating service provides some kind of access token or other credentials upon authentication, these are passed through here. Follow to the protocol specification in naming of keys (For instance, for OAuth2:access_token
,token_type
,expires_in
, etc. according to RFC 6749) info
- A map containing primary information about the user:name
- The best display name known to the workflow. Usually a concatenation of first and last name, but may also be an arbitrary designator or nickname for some workflows.email
- The e-mail of the authenticating user.nickname
- The username of an authenticating user (such as your @-name from Twitter or GitHub account name).first_name
last_name
gender
- The user's gender as a binary string. Possible values include, but are not limited to, the following values:<<"male">>
,<<"female">>
.location
- The general location of the user, usually a city and state.description
- A short description of the authenticating user.image
- A URI representing a profile image of the authenticating user. Where possible, should be specified to a square, roughly 50x50 pixel image.phone
- The telephone number of the authenticating user (no formatting is enforced).uri
- The URI of this user's profile.
extra
- A map containing extra information returned from the authentication provider. Lists of maps are prefered in this section. For instance, if more than one email of a user were available we could put them here and provide additional information for each of them.[ #{type => <<"work">>, value => <<"john@example.com">>}, ...]
rules
- Any rules, like ACL or user roles. For instance, a user might have an 'admin' role or read/write access to some files.
All keys of authentication schema are optional, but it is important to follow this structure always when it's possible.
Provider | Workflow | Description |
---|---|---|
pal_google_oauth2_authcode |
Google Login (OAuth2 Authorization Code Grant) | |
pal_google_openid_user |
Google OpenID Connect user's data | |
pal_google_plus_user |
Google+ user's profile data | |
pal_facebook_oauth2_authcode |
Facebook Login (OAuth2 Authorization Code Grant) | |
pal_facebook_user |
Facebook user's profile data | |
VK | pal_vk_oauth2_authcode |
VKontakte Login (OAuth2 Authorization Code Grant) |
VK | pal_vk_user |
VKontakte user's profile data |
OK | pal_ok_oauth2_authcode |
Odnoklassniki Login (OAuth2 Authorization Code Grant) |
OK | pal_ok_user |
Odnoklassniki user's profile data |
OAuth2 | pal_oauth2_authcode |
OAuth2 Authorization Code Grant, RFC 6749 |
Behaviour | pal_authentication |
Behaviour of PAL workflow |
The source code is provided under the terms of the MIT license.