Authentication & Authorization in Apigility

March 26th, 2014 § 0 comments § permalink

Apigility takes a lightweight, layered, yet extensible approach to solving both problems of authentication and authorization. The infrastructure is already in place and ready to be configured to use, or for more advanced use cases: to be extended. Many of these feature can be easily explored through the Apigility user interface.

While much of the terminology might be similar, Authentication and Authorization IS NOT the same as HTTP allowed methods. These methods are labeled as _allowed_ in the sense that a particular REST or RPC service can respond to that method regardless of what Authentication/Authorization is configured, or which identity is present on any given request to that particular service. HTTP allowed methods have more to do with the semantic operation of the service in question, and little to do with Authentication and Authorization.

Authentication

Authentication is the process by which when an identity is presented to the application, the application can validate the identity is in fact who they say they are. In terms of API’s and Apigility, identities are delivered to the application from the client through the use of the ‘Authorization’ header. This header, if present, is parsed and utilized in one of the configured authentication schemes. If no header is present Apigility assigns a default identity known as a *Guest* identity. The important thing to note here is that Authentication is not something that needs to be turned on, it is always on, it just needs to be configured to handle when an identity is presented to Apigility. If no authentication scheme is configured, and an identity is presented in a way that Apigility cannot handle, or is not configured to handle, the Guest identity will be assigned.

Apigility delivers 3 methods to authenticate identities: HTTP basic authentication, HTTP digest authentication and OAuth2 by way of Brent Shaffer’s PHP OAuth2 package. For HTTP basic and HTTP digest authentication, these can be configured to be used with minimal tools.

Authentication is something that happens “pre-route”, meaning it is something that is configured per-application instead of per module/API that is configured. So if you need per-api groups of users for your API’s, it might make sense to either break your API’s out into their own Apigility powered applications or use a more advanced code driven extension to the authentication module.

To get started with any of the configurable authentication schemes, click “Settings”, then “Authentication”:

settings-authentication

Once here, you will be presented with the aforementioned authentication schemes to be configured.

HTTP Basic Configuration

HTTP basic authentication is the easiest to setup and requires only one outside tool that you are likely already familiar with: the `htpasswd` utility. This command line utility is generally delivered as part of an Apache web server installation. If this tool is not present on your system, there are a number of web based tools that will also produce a valid htpasswd file, simply google for “htpasswd generator”.

The first thing to do, before anything else, is to create an htpasswd file that contains at least one username and password. _(NOTE: It is important that this file exists before configuration as having a path to a non-existent file in the configuration could break the Apigility installation.)_
size-full
A good place to store this file would be in `data/users.htpasswd`.

console-htpasswd-create-file

Once the file has been created, it’s path can be used to configure the required htpasswd file input of the HTTP basic authentication configuration screen:

authentication-basic-htpasswd

Of the configuration entered into this screen, the generated configuration is split between two files in your local application: a global.php and a local.php. The sensitive information is stored in local.php, which is intended to not be checked into your version control system. The configuration information that is not sensitive will be placed in the global.php which will be checked into your version control syste. The intended purpose is to ensure that if an authentication scheme was using on your local development system, when pushed into production the system will still be configured to look for authentication, even if a user/password store is not available in your VCS. At this point, your production system should get a non-VCS user/password htpasswd file to ensure proper authenication of identities with HTTP basic is possible.

code-configuration-http-basic

At this point, HTTP basic authentication with the previously entered username and password is ready to use. A successfully authenticated identity will allow the user to access the given API:

console-http-basic-success

Whereas incorrect username or password should result in an 401 Unauthorized attempt:

console-http-basic-fail

Important notes:

  • Your client should be capable of properly encoding the HTTP basic Authorization header.
  • In production, ensure a htpasswd file can be utilized in the same relative location as in development, even if the htpasswd was not checked into your VCS.
  • No Authorization implies that the Guest identity will be used.

HTTP Digest Configuration

HTTP digest authentication is the next easiest strategy to setup and adds the benefits that come alone with HTTP Digest authentication, namely that passwords are not sent over the network in plain-text. The tool used to create a proper digest file also comes with the Apache installation: `htdigest`. If this tool is not present on your system, there are a number of web based tools that will also produce a valid htpasswd file, simply google for “htdigest generator”.

Like HTTP basic authentication, a digest file will need to exist before configuration of this authentication scheme takes place:

console-htdigest-create-file

Once the file has been created, it’s path can be used to configure the required htdigest file input of the HTTP basic authentication configuration screen:

authentication-digest

Again, like the HTTP basic configuration, sensitive information will be stored in your application’s local.php file, while the structure and non-sensitive parts are stored in global.php. This mean that this authentication strategy will become part of your application when it is deployed to production, you would simply need to provide it a digest file in your production local.php configuration file.

At this point, HTTP Digest authentication has been setup and is ready to use.

console-htdigest-success

And a failed attempt at authentication:

console-htdigest-failure

Important notes:

  • Your client should be capable of properly encoding the HTTP digest Authorization header, and fulfill the digest handshake.
  • In production, ensure a htdigest file can be utilized in the same relative location as in development, even if the htdigest was not checked into your VCS.
  • No Authorization implies that the Guest identity will be used.

OAuth2

OAuth2 has a more complex setup, and also requires some understanding of the Brent Shaffer OAuth2 library. Enrico Zimuel has described setup of that component here: (http://www.zimuel.it/oauth2-apigility/)

Authorization

Authorization is the process by which a system can take a *validated identity* (or lack of identity) and *determine if that identity has access to a given resource*. In terms of APIs and Apigility, the identity that is pass in via the ‘Authorization’ header, which is then validated during authentication, is then passed into the process that determines if the request/resource can be accessed by that identity.

With Apigility, the information presented through the Authorization header is then converted to either a `ZF\MvcAuth\Identity\AuthenticatedIdentity` or `ZF\MvcAuth\Identity\GuestIdentity`. The implementation of authorization uses Zend\Permission\Acl as a model of an access control list. This list is built in the Apigility Admin UI. By default, everything is accessible to all authenticated identities and guest identities. Apigility does not, by default, give you the ability to create user groups, or assign specific permissions to specific authenticated users.

Authorization happens post-route, but before dispatch. This is what allows ZF\MvcAuth to be able to determine if a paritcular identity has access to the requested resource without having to start the initialization dispatch of any particular controller in the application.

What is unique to Apigility is that with REST resources you have the ability to assign permissions for each method that is allowed (HTTP allowed) for each collection method or entity method. With RPC services, you have the ability to assign permissions for each HTTP allowed method to the RPC controller.

Since the granularity of configuration is specific to APIs, you will be able to find an ‘Authorization’ navigation item under each API you have created in the Apigility application. Both REST and RPC services will be listed in the matrix, a checkbox denotes that the particular method in question requires authentication. Methods that are not HTTP allowed, will not be activated to be checked:

authorization

Advanced Authentication & Authorization Information

Modules & Components

Authentication is accomplished through 1 primary module, and consumption of the Zend\AuthenticationService component. The module that delivers the MVC bindings is called zf-mvc-auth, and is officially located here: https://github.com/zfcampus/zf-mvc-auth/. This module’s primary purpose is deliver a generalized solution that adds events, services, and models into the ZF2 MVC lifecycle that can be utilized to simplify both authentication and authorization.

Events

In order to acheive integration to the ZF2 MVC lifecycle for Authentication, zf-mvc-auth wires in 4 listeners that then propagate their own events. Each of these listeners are registered at MvcEvent::EVENT_ROUTE time at various priorities. The table below describes the new event names:

ZF\MvcAuth\MvcAuthEvent::EVENT_AUTHENTICATION (Zend\Mvc\MvcEvent::EVENT_ROUTE, 500)
ZF\MvcAuth\MvcAuthEvent::EVENT_AUTHENTICATION_POST (Zend\Mvc\MvcEvent::EVENT_ROUTE, 499)

As you can tell from their Zend\Mvc\MvcEvent::EVENT_ROUTE priorities, authentication happens *before* routing. There are effectively 2 listeners that deal with Authentication related workflows:

  • `ZF\MvcAuth\Authentication\DefaultAuthenticationListener` is registered to listen at `ZF\MvcAuth\MvcAuthEvent::EVENT_AUTHENTICATION` time. This listener is generally responsible for determining the kind of authentication that needs to take place: whether it be basic, digest, or oauth2, then authenticating that identity. It then assigns that identity (see `ZF\MvcAuth\AuthenticatedIdentity`, or `GuestIdentity` if one was not provided) to the MvcAuthEvent.
  • `ZF\MvcAuth\Authentication\DefaultPostAuthenticationListener` is registered to listen at `ZF\MvcAuth\MvcAuthEvent::EVENT_AUTHENTICATION_POST` time. This listener is responsible for determining if some identity was presented by the client, and if it was successfully authenticated. If it was not, this listener will assign a 401 Unauthorized code to the current HTTP response object.
ZF\MvcAuth\MvcAuthEvent::EVENT_AUTHORIZATION (Zend\Mvc\MvcEvent::EVENT_ROUTE, -600)
ZF\MvcAuth\MvcAuthEvent::EVENT_AUTHORIZATION_POST (Zend\Mvc\MvcEvent::EVENT_ROUTE, -601)

As you can tell from their EVENT_ROUTE priorities, authorization happens *after* routing. There are effectively 3 listeners that deal with Authorization related workflows:

  • `ZF\MvcAuth\Authorization\DefaultResourceResolverListener` is registered at `ZF\MvcAuth\MvcAuthEvent::EVENT_AUTHORIZATION` (1000 priority) time. This listener is generally responsible for taking the matched route and determining the Resource name which is later used for checking against the ACL.
  • `ZF\MvcAuth\Authorization\DefaultAuthorizationListener` is registered at `ZF\MvcAuth\MvcAuthEvent::EVENT_AUTHORIZATION` time. This listener is generally responsible for taking information from the ZF\MvcAuth\MvcAuthEvent and determining if the current request’s route match is an authorized request for the known identity.
  • `ZF\MvcAuth\Authorization\DefaultAuthorizationPostListener` is registered at `ZF\MvcAuth\MvcAuthEvent::EVENT_AUTHORIZATION_POST` time. This listener is generally responsible for checking is the current request is unauthorized, and if so, it will assign a 403 Unauthorized code to the HTTP response object.

Services & Models

The following table describes services and models that are accessible through the Service Manager:

api-identity ZF\MvcAuth\Identity\IdentityInterface (either GuestIdentity or an AuthenticatedIdentity)
authentication Zend\Authentication\AuthenticationService
authorization ZF\MvcAuth\Authorization\AuthorizationInterface (likely a ZF\MvcAuth\Authorization\AclAuthorization)