Storefront API security and authentication

Edit on GitHub

Spryker Storefront API protects endpoints with the OAuth 2.0 protocol; for the authentication concept, token lifetimes, and error handling, see Authenticating and authorization. This document describes the Glue infrastructure implementation: the modules that provide authentication, user scopes, database tables, and extension points. The OAuth modules, scopes, and tokens described here are shared by both infrastructures; however, for endpoints served by API Platform, the code-level patterns differ—user access and endpoint protection are handled through security expressions and the ApiUser object, as described in API Platform security.

Authentication workflow:

authentication-workflow.PNG

Modules

Authentication and authorization are provided by the following modules:

NAME PURPOSE
league/oauth2-server Third-party OAuth server, PhpLeague Oauth Server, integrated into Spryker
Oauth Integrates PhpLeague Oauth Server with Spryker and also provides the necessary extension points.
OauthExtension Provides extension point and plugin interfaces for the Oauth module.
OauthCustomerConnector Provides authentication plugins for OAuth modules necessary to validate user credentials and scopes.
AuthRestApi Provides authentication resources to the Storefront API.

User scopes

The API has scopes defined for different groups of users. A scope defines, which resources specifically users can access.

Note

By default, all Spryker customers are assigned to the customer scope.

To identify, which user has made a request, you can use the getRestUser() function of RestRequestInterface, for example:

class MyResourceHandler implements MyResourceInterface
    /**
     * @param \Spryker\Glue\GlueApplication\Rest\Request\Data\RestRequestInterface $restRequest
     *
     * @return \Generated\Shared\Transfer\CustomerTransfer
     */
    protected function getCustomerTransfer(RestRequestInterface $restRequest): CustomerTransfer
    {
        return (new CustomerTransfer())->setCustomerReference($restRequest->getRestUser()->getNaturalIdentifier());
    }

To identify the user, you can use the getSurrogateIdentifier and getNaturalIdentifier functions:

$restRequest->getRestUser()->getSurrogateIdentifier();
$restRequest->getRestUser()->getNaturalIdentifier();

Company user scope

In the B2B scenario, a user can be associated with an additional scope, company_user. This scope is added in the following cases:

  • the user has impersonated as a Company User via the /company-user-access-tokens endpoint;
  • the user is associated with a single Company User account;
  • the user is associated with several Company User accounts and there is a default one.

Using this additional scope, you can perform additional checks to identify whether a resource should be available to a user. For this purpose, you can identify which Company User account is currently active, and also what company and business unit it belongs to. This can be done using the following helper methods:

$restRequest->getRestUser()->getIdCompanyUser();
$restRequest->getRestUser()->getIdCompanyBusinessUnit();
$restRequest->getRestUser()->getIdCompany();
Info

B2B functionality is available in Spryker Storefront API since version 201907.0.

Endpoint protection

In addition to user scopes, each endpoint can be secured individually. For this purpose, you need to configure the routing of your Resource Feature Module. The Route Plugins of each module define which verbs are supported by the corresponding endpoint. This is done via the config function of the plugin class. The verbs are passed to it as a set of functions that should be called when the corresponding verb is passed.

For details, see Resource Routing.

For each function in the set, the second parameter determines, whether the corresponding verb requires authentication to use (the parameter value is true) or not (the value is false). If the parameter is not passed, the verb requires authentication.

In the following example, the PUT and DELETE verbs require authentication, and the GET verb can be called anonymously.

...
class MyResourceRoutePlugin extends AbstractPlugin implements ResourceRoutePluginInterface
{
    public function configure(ResourceRouteCollectionInterface $resourceRouteCollection): ResourceRouteCollectionInterface
    {
        $resourceRouteCollection->addPost('post')
            ->addDelete('delete', true)
            ->addGet('get', false);
        ...

Database and extension points

All data related to API authentication functionality is stored in the following tables:

TABLE PURPOSE
spy_oauth_access_token Stores all issued tokens.
The table is not used for token verification, it’s added for audit purposes only.
spy_oauth_client Contains a list of clients that are currently using OAuth, one record for each frontend customer. The is_confidental field identifies whether a specific client must provide a password.
spy_oauth_scope Stores user scopes.

The OAuth and OAuthExtension modules also provides the following extension points:

EXTENSION POINT METHOD INTERFACE
User provider plugins getUserProviderPlugins() \Spryker\Zed\OauthExtension\Dependency\Plugin\OauthUserProviderPluginInterface
Scope provider plugins getScopeProviderPlugins() \Spryker\Zed\OauthExtension\Dependency\Plugin\OauthScopeProviderPluginInterface