p7s1-ctf/7pass-php-sdk

This package is abandoned and no longer maintained. No replacement package was suggested.

7Pass SDK for PHP

2.0.2 2017-10-24 11:03 UTC

This package is not auto-updated.

Last update: 2020-08-25 13:22:31 UTC


README

Latest Stable Version

7Pass PHP SDK is a PHP library for interacting with the 7Pass SSO service. You can use this library to implement authentication for your website and take advantage of the already existing features that 7Pass SSO offers.

Installation

Before you start working with the library, you need to make sure your system meets the required criteria. As of now, the library requires PHP 5.5.0 and newer with cURL support.

Note: Only TLS v1.2 connections are accepted on production and qa environments - please make sure your system's cURL library supports it, otherwise SSL connect error exception is thrown.

The library is distributed as a composer package. If you haven't already, please install Composer using the official instructions. Once Composer is installed, you can install the library as follows:

$ composer require p7s1-ctf/7pass-php-sdk

This will automatically add the library to the list of your application's dependencies.

Running the example application

To demonstrate the functionality of the library, there's an interactive tutorial / guide available. Before we get started setting it up, you need your web application's client. The client represents the entity which is associated with a service you want to authenticate to.

To obtain the client credentials, you first need to contact the 7Pass Tech Team.

Once you have the credentials available, you can go ahead and install the dependencies:

$ composer install

Next, you can go to the public_html directory and create the local configuration file:

$ cd public_html
$ cp config.local.php.example config.local.php

Edit the config.local.php file in your favorite editor and fill out the details. You should have all of the parameters at your disposal after your client is set up. For testing, keep the environment set to qa. Once that's done, you can start the application using the PHP's built-in server:

$ php -S localhost:8000

The example application should be now available at http://localhost:8000. The application will guide you through the most common use cases of the library and show you code examples and server responses along the way.

API Usage

You strongly encouraged to go over the example application first. It will show you the API calls with more comments and real values. It will also show you the real responses from the 7Pass SSO service as you progress.

To use the library, it's necessary to initialize it with the credentials of the client we want to use. If you don't have the credentials yet, please see above.

  • client_id (required)
  • client_secret (required)

If you're starting the development, it's always a good idea to work against a non live instance of the 7Pass SSO service. To specify the instance (the environment) against which you want to issue the requests, you can pass an additional key called environment to the configuration. There're currently two environments running: QA and production. Don't forget to switch to the production version before you release your application to the public.

$config = [
  'client_id' => 'YOUR_CLIENT_ID',
  'client_secret' => 'YOUR_CLIENT_SECRET',
  'environment' => 'qa' // Optional, defaults to 'production'
];

// Creates the configuration object
$ssoConfig = new P7\SSO\Configuration($config);

// Pass the configuration to the SSO object
$sso = new P7\SSO($ssoConfig);

For both environments there is a default host URL where 7Pass API endpoints can be reached on. For production environment it is https://sso.7pass.de and for qa a host is https://sso.qa.7pass.ctf.prosiebensat1.com. If needed, this can be overwritten by specifying host in the configuration as below:

$config = [
    'host' => 'https://mysubdomain.7pass.de'
    // ... other config values
];

Authentication flow

The authentication process is simple and the high level view is as follows: The user is redirected to the 7Pass SSO service using a specially crafted URL and he/she signs in (or signs up). Once the user has finished the process, he/she is redirected back to your application with a special code in the URL. The application will then use the code in order to get the user's details. The process may vary depending on the passed options.

1. Get a redirect URL

The library automatically handles the generation of the URL to which the user needs to be redirected. The only required parameter is the redirect_uri URL. The URL needs to be absolute and can be arbitrary (given that it is registered to the client) but will by convention lead to the same host and a route called "callback".

Use of state parameter is optional but recommended to avoid CSRF attacks - the value is usually stored in the session and is also used with $sso->authorization()->callback() method when handling the callback request.

$callbackUri = 'https://example.com/callback';
$options = [
  'redirect_uri' => $callbackUri, // Required.
  'scope' => 'openid profile email', // Optional, default value.
  'response_type' => 'code' // Optional, default value.
  'state' => $sessionState // Optional, but recommended to avoid CSRF attacks
];

$redirectUrl = $sso->authorization()->authorizeUri($options);

The library will automatically set the client_id and nonce (a unique request identifier) parameters automatically.

2. Redirect an user

Now it's time to redirect the user to the generated URL. In plain PHP, you can set the Location header:

header('Location: ' . $redirectUrl);
exit;

3. Handling 7Pass callback

After the user has finished with the sign in/up dialog, he/she has been redirected to the redirect_uri URL with the outcome of the sign in process. The user might have successfully authenticated but also might have decided to cancel the process or some other error might have happened. Therefore it's important have proper error handling.

Whenever an error occurs, there will be two query parameters present in the URL - error and error_description. The error parameter contains an error code and the error_description contains a human readable description of the error. Handle the error and display appropriate message to your end-user.

You might choose to handle an error manually as below. Otherwise in case of an error, calling $sso->authorization()->callback() method would throw an AuthorizeCallbackException.

if(!empty($_GET['error'])) {
  $error = $_GET['error'];
  $errorDescription = $_GET['error_description'];

  // Handle the error and display appropriate message to your end-user.
}

The library handles retrieving the tokens on its own, you just need to provide the redirect_uri and also query parameters from the request which should include code. This allows you to retrieve the tokens which can be used to fetch the actual information about the user later. These tokens are specific to the particular user and are private. You need to keep them secured and do not share them with anybody.

$sessionState parameter is optional but same should be provided if used with $sso->authorization()->authorizeUri() method. The AuthorizeCallbackException is thrown when state value provided and the one retrieved from query parameters $_GET does not match.

$tokens = $sso->authorization()->callback($callbackUri, $_GET, $sessionState);

The received response will have the following structure. Run the example application to see the real values.

P7\SSO\TokenSet(
    [access_token] => ACCESS_TOKEN
    [token_type] => 'Bearer'
    [refresh_token] => REFRESH_TOKEN
    [expires_in] => 7200
    [id_token] => JWT_STRING
    [id_token_decoded] => DECODED_JWT,
    [received_at] => RECEIVED_AT_TIMESTAMP
)

Note: The id_token_decoded value is decoded from the id_token field and verified. If token verification fails, the P7\SSO\Exception\TokenVerificationException exception is thrown.

Further, the call might throw P7\SSO\Exception\ApiException in case the code has already been used or is otherwise invalid.

4. Caching an access token

The tokens are represented in a single object of type P7\SSO\TokenSet. You can serialize the object directly, however, we recommend that you first convert the TokenSet into a simpler array object and serialize it instead. As soon as you need the TokenSet again, you can pass the array object into its constructor.

// Serialize the TokenSet object and store it in e.g. the current session
$_SESSION['tokens'] = $tokens->getArrayCopy();

// Deserialize and get the TokenSet object again
$tokens = new \P7\SSO\TokenSet($_SESSION['tokens']);

Access tokens are valid for certain amount of time specified in seconds in the expires_in field. Once the access token expires, it can no longer be used. You can obtain a new one using the refresh token as follows:

if($tokens->isAccessTokenExpired()) {
  $tokens = $sso->authorization()->refresh([
    'refresh_token' => $tokens->refresh_token
  ]);
}

Note: refresh() method above also accepts P7\SSO\TokenSet object as an argument.

5. Calling our API endpoints

Now that we're sure the tokens are up to date, we can start making requests to the 7Pass SSO service to get the user data.

Same as with the previous example, run the example application to see the real server response.

$accountClient = $sso->accountClient($tokens);
$response = $accountClient->get('me');

The 7Pass SSO service offers quite a few of these endpoints. To learn more about them, you can go to the official documentation's overview.

Client Credentials requests

The library supports multiple types of "clients". These clients generally differ in the required configuration parameters and afterwards in the functionality they provide. The "client credentials" client is a special kind of client which is not associated with a user account and can be only used to call the "clients" APIs.

You can see all of the available endpoints in the documentation with the accessType parameter equal to client.

$config = [
  'environment' => 'qa', // Optional, defaults to 'production',
  'client_id' => 'CLIENT_ID',
  'client_secret' => 'CLIENT_SECRET'
];

// Creates the configuration object
$ssoConfig = new P7\SSO\Configuration($config);

// Pass the configuration to the SSO object
$sso = new P7\SSO($ssoConfig);

// Get the tokens using the client credentials grant type
$tokens = $sso->authorization()->clientCredentials();

// Use the client
$client = $sso->clientCredentialsClient($tokens);
$response = $client->post('checkPassword', [
  'password' => 'PASSWORD'
]);

Device flow

With device flow people can easily and safely log into apps and services with their 7Pass account on devices with limited input or display capabilities. This includes Smart TVs, digital photo frames, or Internet of Things devices.

The device (your application) instructs the end-user to use another computer or device and connect to 7Pass to approve the access request. Since your application cannot receive incoming requests, it polls the 7Pass authorization server repeatedly until the end-user completes the approval process.

Your application initiates the flow by requesting a set of verification codes from the authorization server by making an HTTP POST request to the token endpoint.

$deviceCodeResponse = $sso->authorization()->deviceCode();

$deviceCodeResponse would be stdClass object like the following:

stdClass(
    [code] => CODE         // used along with poll requests (see below)
    [user_code] => Q9CFLH  // the code which user should enter on LINK page 
    [expires_in] => 600    // expiration of the code (in seconds) 
    [interval] => 5        // recommended interval for repeating poll requests (in seconds)
    [link] => LINK_URL     // verification URL user should use to authenticate
    [link_qr] => IMAGE_URL // image URL of QR code encoded link
)

The user should now be prompted to visit link URL on another device to enter user_code. link_qr is URL of an image representing QR code encoded link URL. This could be used by users using mobile devices, they can scan it and have link URL opened in their mobile browsers without manually typing it themselves.

While user tries to authenticate on link URL, your application polls 7Pass server repeatedly. Recommended interval is specified in interval value.

$code in the example below can be either $deviceCodeResponse object received from $sso->authorization()->deviceCode() method call, array with code item or simply a string with code value.

// repeat every [interval] seconds.
try {
    $response = $sso->authorization()->deviceCodePoll($code);
    if($response instanceof \P7\SSO\TokenSet) {
        // stop polling - user has authorized from another device successfully
    }
    // $reponse === false in case 'authorization_pending' response is received
} catch(ApiException $e) {
    // handle the error appropriately
    // $e->getError() is set to one of error codes 'code_expired', 'slow_down', 'invalid_grant'
}

Backoffice requests

The library can also be used to perform "backoffice" requests. These requests are initiated without direct involvement of users and are meant for administrative purposes. Your client needs to have the backoffice_code grant type allowed. You also need to know the ID of the user you want to work with.

Backoffice requests are used to make an API calls on behalf of other users. To get access token for these requests you need to use special grant type 'backoffice_code' providing an account_id. Upon successful authentication you get a same token set as using standard flow described above.

$config = [
  'environment' => 'qa' // Optional, defaults to 'production',
  'service_id' => 'SERVICE_ID', // Required for backoffice access
  'backoffice_key' => 'BACKOFFICEC_KEY' // Required for backoffice access
];

// Creates the configuration object
$ssoConfig = new P7\SSO\Configuration($config);

// Pass the configuration to the SSO object
$sso = new P7\SSO($ssoConfig);

// Get the tokens using the backoffice
$tokens = $sso->authorization()->backoffice([
  'account_id' => 'account_id' // Required, the ID of the user you want to access
]);

// Use the client as you normally would when using the standard access
$accountClient = $sso->accountClient($tokens);
$response = $accountClient->get('me');

The response will be as usual. Once you get the tokens, the 7Pass SSO service will act as if the access token has been obtained using the "standard" way.

Client/backoffice registration

When you register new users using the client or backoffice registration API, you might want to bounce them to the 7Pass SSO service so that the user's session is created and the user logged in.

This SDK provides a method called autologinUri() which can be used to generate the redirect (bounce) URL. The method accepts a TokenSet as its first parameter. You can retrieve the user's tokens when using the registration API by providing the scope parameter. See the registration API documentation for more details. response_type defaults to none but can be set to any other supported type if needed so.

// $client is an instance of ApiClient created using $sso->clientCredentialsClient() or $sso->backofficeClient() methods.
$response = $client->post('registration', [
    'scope' => 'openid profile email'
    // other parameters
]);
$tokens = TokenSet::receiveTokens($response);

$callbackUri = 'https://example.com/callback';

$uri = $sso->authorization()->autologinUri($tokens, [
    'redirect_uri' => $callbackUri, // Required
    'state' => $sessionState // Optional but recommended to use
], [
    'remember_me' => true // Default value: false
])

// Redirect user to $uri

As with other ordinary authorize requests, you can use Authorization::callback() method to handle a callback request. The method returns null if response_type was set to none.

$tokens = $sso->authorization()->callback($callbackUri, $_GET, $sessionState);

Caching

Before the library can work properly, it needs to fetch the OpenID configuration from the configured instance of the 7Pass SSO service. To make sure the information is not downloaded every time, the library uses a configurable caching mechanism.

The default expiration time is set to 1 hour and can be customized by passing cache_config_openid_ttl value to the $config settings (in seconds).

The library internally computes a hash out of all of the parameters passed into the Configuration's constructor and uses it as the cache key for the configuration. This means the library will automatically rediscover the configuration in case the configuration changes.

Be aware however that in case the configuration is changed back to some previously used state and the computed configuration's key corresponds to an existing cache item, you might get a stale configuration. If you expect the remote OpenID configuration to change, you can either purge the cache and let the library rediscover it itself or you can conditionally run the rediscover() method.

Under the hood, it uses the Stash Caching Library library and tries to use the Apc driver first. If not avaible, it will use the Filesystem driver instead.

If desired, you can configure your own cache driver as follows:

$config = [
    // ... other configuration settings
    'cache_config_openid_ttl' => 3600 // sets cache expiration time to 1 hour
];
$ssoConfig = new P7\SSO\Configuration($config);

$driver = new Stash\Driver\Memcache(['servers' => ['127.0.0.1', '11211']]);
$ssoConfig->setCachePool(new Stash\Pool($driver));

$sso = new P7\SSO($ssoConfig);

To manually refresh the cache use the rediscover method. This is not needed under normal circumstances.

$sso->getConfig()->rediscover();

If you have any questions or something's not working as expected, please do not hesitate to contact the 7Pass Tech Team.

Running the tests

The library uses PHPUnit for testing. The recommended version is 4.8 although the tests may run successfully on an older version of the 4 series as well.

$ composer install
$ ./vendor/bin/phpunit