Loading repository data…
Loading repository data…
npr / repository
A PHP-based server-side proxy for interacting with the NPR One API's authorization server
A PHP-based server-side proxy for interacting with the NPR One API's authorization server. Use this proxy to secure your OAuth2 credentials.
The NPR One API provides a lightweight REST/Hypermedia interface to power an NPR One experience. To secure our API, we have implemented an authorization server based on the OAuth 2.0 protocol, a well-accepted Internet standard.
Third-party developers have two primary methods for obtaining the access tokens required by our API to interact with any of our other micro-services:
authorization_code grantdevice_code grant (a custom grant based on Google's proposed spec for OAuth2 for Limited Input Devices)The NPR One authorization server does not currently accept the implicit grant type described in the OAuth2 spec due to security concerns.
Both the device_code and authorization_code grant types require an OAuth2 client_secret to generate an access token. However, since the source code for web applications written in a client-side language (like Javascript) cannot be kept private, a server-side proxy is required to safely make calls to the authorization server and ensure the security of your OAuth2 credentials.
In order to make this requirement less painful for third-party developers, we are providing this PHP-based proxy as an open-source package to help you get up-and-running quickly and prevent NPR One client credentials from being compromised in public source code.
This project is designed to be executed in a server environment with Apache HTTP Server or Nginx.
A recent version of PHP, equal to or greater than 7.3.0 is required.
The default EncryptionProvider class provided in this package relies on the OpenSSL extension. If OpenSSL is unavailable, the consumer has the option to implement a custom EncryptionProvider class that implements our EncryptionInterface. (For more information, see the EncryptionProvider section.)
Usage of NPR's authorization server requires a registered developer account with the NPR One Developer Center. If you do not already have a Dev Center account, you can register for a personal account and get started immediately.
This project is intended to be run as a sub-module (or dependency) of a larger project and should be installed using Composer (an open-source dependency manager for PHP projects):
[sudo] composer install npr/npr-one-backend-proxy
If you do not already have a Composer project set up, you can start one quickly with:
composer init --require="npr/npr-one-backend-proxy" -n
The following 2 PHP classes must be created to integrate this package into your project:
Additionally, if you are using the authorization_code grant, a StorageProvider class will be required. Examples of each can be found in the examples folder.
Create a router which calls the relevant public methods in either AuthCodeController or DeviceCodeController, depending on which grant type will be used (authorization_code or device_code, respectively).
All consumers, regardless of grant type, MUST implement a route that maps to the generateNewAccessTokenFromRefreshToken() function in the RefreshTokenController class. This route allows your server to seamlessly request a new access token when the original one has expired. If you do not implement this route, your users will automatically be logged out after 2 weeks and required to log back in to resume listening, which is not the desired user experience.
Similarly, all consumers (assuming they provide some kind of 'Logout' or 'Disconnect from NPR One' functionality) SHOULD implement a route that maps to the deleteAccessAndRefreshTokens() function in the LogoutController class. This route allows your app to ensure that all persistent data related to a logged-in state (such as access tokens and refresh tokens) are removed from NPR's authorization server, as well as ensuring that the refresh_token is removed from the secure storage layer. This function takes in an access token but it can also work without any input, assuming that refresh tokens are being stored persistently in the secure storage layer.
The Router.php file in the examples folder provides a hypothetical Laravel-esque example of what this might look like. Please note, this code is intended only as an example to provide guidance on how to get started and has not been tested. This example includes code for both the authorization_code and device_code grant types, but in your actual implementation, include only the code relevant to whichever grant type you are using in your application.
Create a ConfigProvider class that implements our ConfigInterface to power your controller classes. The ConfigProvider class will encapsulate the consumer-specific variables (your client ID and client secret) needed to power this OAuth2 proxy.
There is a sample ConfigProvider.php in the examples folder to help you get started. This class does not need to be complicated and can mostly just return hard-coded strings. However, do not include your client secret (or your encryption salt) in any files that will appear in public repositories, as this could compromise your application. We assume that you either plan to keep your code private or you have some form of private secrets file that is not included in any public repositories or publicly-accessible locations.
If you are using the authorization_code grant (and thereby the AuthCodeController), create a StorageProvider class which implements our StorageInterface. The StorageProvider is required to validate the OAuth2 state param.
You will find a sample StorageProvider.php file in the examples folder. The example utilizes Predis, a PHP Redis client, but there are many other options, including Memcached and PHP sessions. MySQL is also an option, but not recommended because it is likely to be much slower. We picked Predis for demonstration purposes because the syntax is very simple and applicable to many other storage layers.
The Controller classes will save the refresh token and access token in a cookie by default. In order to keep those refresh tokens secure, we encrypt them before saving and decrypt them when we need to retrieve them. To make this process less cumbersome, a default EncryptionProvider has been provided. However, this particular EncryptionProvider relies on the OpenSSL extension being available, which may not be an option for all developers. If OpenSSL is unavailable, or if you want to use a different method of encryption, you can use a custom encryption provider that implements our EncryptionInterface.
If you choose to implement a custom encryption provider, use the default implementation as your example. The syntax for including your own custom encryption provider is as follows:
authorization_code grant type:
use NPR\One\Controllers\AuthCodeController;
use Your\Package\Here\ConfigProvider;
use Your\Package\Here\EncryptionProvider;
use Your\Package\Here\StorageProvider;
$controller = (new AuthCodeController())
->setConfigProvider(new ConfigProvider())
->setStorageProvider(new StorageProvider())
->setEncryptionProvider(new EncryptionProvider());
device_code grant type:
use NPR\One\Controllers\DeviceCodeController;
use Your\Package\Here\ConfigProvider;
use Your\Package\Here\EncryptionProvider;
$controller = (new DeviceCodeController())
->setConfigProvider(new ConfigProvider())
->setEncryptionProvider(new EncryptionProvider());
As explained above, encrypted cookies are used to store refresh tokens across sessions. However, cookies are not the only possible storage method: Redis and Memcached are good options (as long as you have a mechanism for identifying the user across sessions, which may still require cookies). If you are considering using PHP's session storage, you may want to take a look at PHP-Secure-Session, which provides an extra layer of security through encryption.
All of the Controller classes are configured to use the SecureCookieProvider as the default secure storage layer, but you can easily override this using the setSecureStorageProvider() function:
authorization_code grant type:
use NPR\One\Controllers\AuthCodeController;
use Your\Package\Here\ConfigProvider;
use Your\Package\Here\SecureStorageProvider;
use Your\Package\Here\StorageProvider;
$controller = (new AuthCodeController())
->setConfigProvider(n