This project is a simple sample web server for the Polar Mobile Paywall API.
NOTE: You may be tempted to adapt this source code to work with your existing infrastructure. This strategy is certifiably a bad idea as this example was not designed to scale. A proper implementation requires a complete rewrite of this example.
- Definitions: Definitions of bolded terms used throughout this document.
- Architecture
- Business
- Overview: An overview of the system as a whole.
- Objective
- Moving Parts
- API
- Client to Server
- Server to Publisher
- Sample Publisher: An overview of this project.
- Minimum Requirements
- Installation
- Moving Parts
- utils.py
- errors.py
- auth.py
- validate.py
- model.py
- constants.py
- test.py
- runserver.py
- setup.py
- Deployment: A brief description of how the server should be deployed.
- Errors: A description of error reporting.
- Error Reports
- id
- code
- resource
- Example
- Error Reports
- Creating Users: How to create test users and products.
This section describes the definitions used throughout this document.
Definitions related to the systems architecture.
- Client := Client devices, like iPhones, Androids and Blackberries.
- Server := Polar Mobile's server.
- Publisher := The publishers server.
Definitions specific to this API.
- Client Error := An error message that is proxied to the client.
- Server Error := An error message that is stored on Polar's server.
Definitions related to the business domain.
- Entitilement Mechanism := A way users can access protected content.
- Subscription := An entitilement mechanism where the user pays a recurring fee.
- Proxy Subscription := Subscriptions managed by the Publisher.
- Subscription := An entitilement mechanism where the user pays a recurring fee.
- Product := Content that is protected.
- Seller := A legal entity that provides the content.
- Product Association := Association between Entitlement Mechanism and Product.
- Product Allocation := When a user gains access to a Product.
This section gives a broad overview of the paywall system as a whole.
The point of this API is to allow publishers to password protect their publications.
The basic operation is as follows.
- First the user logs in.
- The client uses those credentials to ask the server for a session key.
- The server will then ask the publisher for the key.
- The publisher will send the key to the server
- The server will send it back to the client.
Next, when a client wants to access protected content, they will send the key with the request, which will get proxied by the server to the publisher. If the client has access to the content, the publisher will return successfully and the server will deliver the content to the client.
This section is an overview of the communication that happens between the client and the server.
Note that this part of the API is not implemented by the publisher's server. They are described for completeness.
- Listing: Obtain a listing of all products.
- Authenticate: Send user credentials to the server and get a key back.
- Product Access: Request content from the server with a key.
- Authenticate: Send user credentials to the publisher and receive a key back.
- Product Access: Request permission to serve content to the client.
This project is a sample implementation of a publisher server. It is written in Python and meant to serve as a reference for implementation. The sample is not fully functional and takes several shortcuts; as such, it should not be used in production.
This section provides an overview of the sample and the contents of the various files used in its implementation.
In order to run this sample, your system should have Python 2.6 or greater installed and accessible from your command prompt. You will also need the itty package, version 0.8.0 or greater.
Note that installation is not required in order to test or run this sample. In order to run this sample, issue the following command to your command prompt:
python server.py
In order to install this sample, run the following command:
python setup.py install
The sample implementation contains three main moving parts:
- auth.py := A request handler that authorizes the user.
- validate.py := A request handler that grants the user access to a product.
- model.py := A data store used to store and fetch usernames and passwords.
All other files simply provide supporting functionality to achieve these three main operations.
This file contains two functions; report_error and check_base_url. report_error formats error messages in a way expected by the Polar Server. check_base_url validates the common parts of the url for all API entry points.
This file contains default handlers for two common http errors; 500 and 404. While it is not mandatory to implement these handlers, having them will make deployment more secure and easier to debug.
The auth function in this file is a handler used to authenticate a user, using their supplied authentication credentials. It also supplies two additional functions (check_device, and check_auth_params) that check the validity of the parameters passed to the auth function.
The validate function in this file is a handler used to attempt an authorization for a product using supplied session key. This API call is used periodically to validate that the session is still valid and the user should continue to be allowed to access protected resources.
In order to simplify the design of this sample, the state of the system is stored in memory, as opposed to a database. The server is then run in a single process with multiple threads. The model class contains a singleton that stores the state of the server.
A file used to store constant values used in the server's implementation. This file stores the url regexes, the length in hours of a session key's validity, and most importantly the test users that the server supports.
A series of unit tests used during the development of this project. To run the unit test suite, issue the following command on your terminal:
python test.py
A script that is used to run the sample server on port 8080. Note that this script should only ever be used for testing purposes and not in production. To run this script directly, issue the following command on your terminal:
python server.py
You can optionally specify the hostname and port number you want to run the server on:
python server.py 0.0.0.0 9090
Used to install the sample server. Note that it is not necessary to install the same server in order to test with it. The server.py script can be called directly as long as the stated requirements are met.
The publisher server is deployed as a HTTPS web server. It should under no circumstances be exposed as an HTTP server as it may allow a third party to obtain login credentials.
Note that routing rules should only allow traffic between Polar's server and the Publisher's server over HTTPS (port 443).
For Client Errors (400-series) and Server Errors (500-series), an error report should be returned. Note that error messages will be presented to the client as printable text. It is up to the publisher to ensure the quality of the content of these messages.
Errors are encoded using json. The body of the error is a json map with a single key called "error". The "error" value is another map with the following parameters:
- "code"
- "message"
- "resource"
Optionally, another object called "debug" can be included with the report. The only required response parameter of this object is a "message", which is logged as a header.
This is a string error code that both Polar's server and the client can use to easily triage an error. Each entry point in this project contains documentation on the error codes it may return.
A description of the error. Note that this description is always returned to the user.
The resource URI the request was attempting to access.
Example Request:
GET /paywallproxy/v1.0.0/json/auth/60c2c670ea6b3847b HTTP/1.1
Example Error Response:
HTTP/1.1 404 NOT FOUND
Content-Type: application/json
Accept-Language: en
{
"error": {
"code": "InvalidProduct",
"message": "The specified article does not exist.",
"resource": "/paywallproxy/v1.0.0/json/auth/60c2c670ea6b3847b"
},
"debug": {
"message": "The article was removed on date 01/01/10."
}
}
This section describes how to create test users and products in this reference implementation. To add a new user or set of products to the test system, modify the users dictionary in constants.py.