Skip to content

manifest/pal

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Pragmatic Authentication Library

Build Status

The standardized multi-provider authentication library.

Introduction

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.

How to use

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.

How it works

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))).

Group

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 => <<"[email protected]">>,
%%       gender => <<"male">>,
%%       image => <<"https://lh3.googleusercontent.com/...">>,
%%       uri => <<"https://plus.google.com/...">>},
%%   access_token => <<"...">>,
%%   token_type => <<"Bearer">>,
%%   expires_in => 3599,
%%   id_token => <<"...">>,
%%   code => <<"...">>}

Overview

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}}).

pal-authenticate

Authentication Schema

  • 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 => <<"[email protected]">>}, ...]
  • 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.

List of workflows

Provider Workflow Description
Google pal_google_oauth2_authcode Google Login (OAuth2 Authorization Code Grant)
Google pal_google_openid_user Google OpenID Connect user's data
Google pal_google_plus_user Google+ user's profile data
Facebook pal_facebook_oauth2_authcode Facebook Login (OAuth2 Authorization Code Grant)
Facebook 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

License

The source code is provided under the terms of the MIT license.