Glue API - Customer Account Management feature integration

Edit on GitHub

This feature integration guide expects the basic feature to be in place. It only adds the following functionalities:

  • Oauth
  • Auth Rest API
  • Customers Rest API

Prerequisites

Install the required features:

FEATURE VERSION INSTALLATION GUIDE
Glue API: Spryker Core 202404.0 Install the Spryker Core Glue API
Glue API: Glue Application 202404.0 Install the Glue Application Glue API
Glue API: Shipment 202404.0 Install the Shipment Glue API
Glue API: Checkout 202404.0 Install the Checkout Glue API
Customer Account Management 202404.0 Install the Customer Account Management feature

1) Install the required modules

Install the required modules using Composer:

composer require "spryker/customers-rest-api":"^1.22.0" "spryker/auth-rest-api":"^2.12.0" "spryker/oauth":"^1.6.0" "spryker/oauth-revoke":"^1.0.0" "spryker/oauth-customer-connector:"^1.4.0" --update-with-dependencies
Verification

Make sure the following modules have been installed:

MODULE EXPECTED DIRECTORY
Oauth vendor/spryker/oauth
OauthRevoke vendor/spryker/oauth-revoke
OauthCustomerConnector vendor/spryker/oauth-customer-connector
AuthRestApi vendor/spryker/auth-rest-api
CustomersRestApi vendor/spryker/customers-rest-api

2) Set up configuration

Set the required OAuth config:

config/Shared/config_default.php

$config[OauthConstants::PRIVATE_KEY_PATH] = 'file://path/to/private.key';
$config[OauthConstants::PUBLIC_KEY_PATH] = 'file://path/to/public.key';
$config[OauthConstants::ENCRYPTION_KEY] = 'generated-encryption-key';
$config[OauthConstants::OAUTH_CLIENT_IDENTIFIER] = 'client-identifier';
$config[OauthConstants::OAUTH_CLIENT_SECRET] = 'client-secret';

For more details on key generation, see OAuth 2.0 Server Installation .

Configure the cleanup of expired refresh OAuth tokens

To override the default interval of storing refresh tokens in the system after they expire, extend Spryker\Shared\Oauth\OauthConfig on the project level. The format of the value should be acceptable by the DateInterval object.

src/Pyz/Zed/Shared/OauthConfig.php

<?php

namespace Pyz\Shared\Oauth;

use Spryker\Shared\Oauth\OauthConfig as SprykerOauthConfig;

class OauthConfig extends SprykerOauthConfig
{
    /**
     * @return string
     */
    public function getRefreshTokenRetentionInterval(): string
    {
        return '{{interval}}';
    }
}

Configure password validation rules

Configure password validation rules for CustomerRestApi endpoints:

src/Pyz/Glue/CustomersRestApi/Validation/customers.validation.yml

customers:
    post:
        password:
            - NotBlank
            - Length:
                  min: 8
                  max: 64
        confirmPassword:
            - NotBlank
            - Length:
                  min: 8
                  max: 64

customer-password:
    patch:
        password:
            - NotBlank
        newPassword:
            - NotBlank
            - Length:
                  min: 8
                  max: 64
        confirmPassword:
            - NotBlank
            - Length:
                  min: 8
                  max: 64

customer-restore-password:
    patch:
        password:
            - NotBlank
            - Length:
                  min: 8
                  max: 64
        confirmPassword:
            - NotBlank
            - Length:
                  min: 8
                  max: 64

Configure checkout validation rules

Configure checkout-data and checkout validation rules for CheckoutRestApi endpoints:

src/Pyz/Glue/CustomersRestApi/Validation/checkout.validation.yaml

checkout-data:
   post:
      billingAddress:
         - Optional:
              constraints:
                 - Collection:
                      fields:
                         salutation:
                            - Optional:
                                 constraints:
                                    - NotBlank
                         firstName:
                            - Optional:
                                 constraints:
                                    - NotBlank
                         lastName:
                            - Optional:
                                 constraints:
                                    - NotBlank
                         address1:
                            - Optional:
                                 constraints:
                                    - NotBlank
                         address2:
                            - Optional:
                                 constraints:
                                    - NotBlank
                         zipCode:
                            - Optional:
                                 constraints:
                                    - NotBlank
                         city:
                            - Optional:
                                 constraints:
                                    - NotBlank
                                    - Length:
                                         min: 3
                         iso2Code:
                            - Optional:
                                 constraints:
                                    - NotBlank
checkout:
   post:
      billingAddress:
         - Collection:
              fields:
                 salutation:
                    - Optional:
                         constraints:
                            - NotBlank
                 firstName:
                    - Optional:
                         constraints:
                            - NotBlank
                 lastName:
                    - Optional:
                         constraints:
                            - NotBlank
                 address1:
                    - Optional:
                         constraints:
                            - NotBlank
                 address2:
                    - Optional:
                         constraints:
                            - NotBlank
                 zipCode:
                    - Optional:
                         constraints:
                            - NotBlank
                 city:
                    - Optional:
                         constraints:
                            - NotBlank
                            - Length:
                                 min: 3
                 iso2Code:
                    - Optional:
                         constraints:
                            - NotBlank

3) Set up database schema and transfer objects

Apply database changes and generate entity and transfer changes:

console propel:install
console transfer:generate
Verification

Make sure the following changes have occurred in the database:

DATABASE ENTITY TYPE EVENT
spy_customer_address.uuid column created
spy_customer_address.spy_customer_address-unique-uuid index created
spy_oauth_access_token table created
spy_oauth_client table created
spy_oauth_scope table created
spy_oauth_refresh_token table created
Verification

Make sure the following changes have occurred in transfer objects:

TRANSFER TYPE EVENT PATH
AddressTransfer.uuid column created src/Generated/Shared/Transfer/AddressTransfer.php
RestCustomersAttributesTransfer class created src/Generated/Shared/Transfer/RestCustomersAttributesTransfer.php
RestCustomersResponseAttributesTransfer class created src/Generated/Shared/Transfer/RestCustomersResponseAttributesTransfer.php
RestCustomersRegisterAttributesTransfer class created src/Generated/Shared/Transfer/RestCustomersRegisterAttributesTransfer.php
RestAddressAttributesTransfer class created src/Generated/Shared/Transfer/RestAddressAttributesTransfer.php
RestCustomerPasswordAttributesTransfer class created src/Generated/Shared/Transfer/RestCustomerPasswordAttributesTransfer.php
RestCustomerForgottenPasswordAttributesTransfer class created src/Generated/Shared/Transfer/RestCustomerForgottenPasswordAttributesTransfer.php
RestCustomerRestorePasswordAttributesTransfer class created src/Generated/Shared/Transfer/RestCustomerRestorePasswordAttributesTransfer.php
RestAccessTokensAttributesTransfer class created src/Generated/Shared/Transfer/RestAccessTokensAttributesTransfer.php
RestRefreshTokensAttributesTransfer class created src/Generated/Shared/Transfer/RestRefreshTokensAttributesTransfer.php
RestTokenResponseAttributesTransfer class created src/Generated/Shared/Transfer/RestTokenResponseAttributesTransfer.php
CustomerIdentifierTransfer class created src/Generated/Shared/Transfer/CustomerIdentifierTransfer.php
OauthRequestTransfer class created src/Generated/Shared/Transfer/OauthRequestTransfer.php
OauthResponseTransfer class created src/Generated/Shared/Transfer/OauthResponseTransfer.php
OauthAccessTokenValidationResponseTransfer class created src/Generated/Shared/Transfer/OauthAccessTokenValidationResponseTransfer.php
OauthErrorTransfer class created src/Generated/Shared/Transfer/OauthErrorTransfer.php
OauthAccessTokenValidationRequestTransfer class created src/Generated/Shared/Transfer/OauthAccessTokenValidationRequestTransfer.php
OauthUserTransfer class created src/Generated/Shared/Transfer/OauthUserTransfer.php
OauthScopeRequestTransfer class created src/Generated/Shared/Transfer/OauthScopeRequestTransfer.php
OauthScopeTransfer class created src/Generated/Shared/Transfer/OauthScopeTransfer.php
OauthClientTransfer class created src/Generated/Shared/Transfer/OauthClientTransfer.php
OauthGrantTypeConfigurationTransfer class created src/Generated/Shared/Transfer/OauthGrantTypeConfigurationTransfer.php
OauthAccessTokenDataTransfer class created src/Generated/Shared/Transfer/OauthAccessTokenDataTransfer.php
JwtTokenTransfer class created src/Generated/Shared/Transfer/JwtTokenTransfer.php
OauthRefreshTokenTransfer class created src/Generated/Shared/Transfer/OauthRefreshTokenTransfer.php
OauthRefreshTokenCollectionTransfer class created src/Generated/Shared/Transfer/OauthRefreshTokenCollectionTransfer.php
RevokeRefreshTokenRequestTransfer class created src/Generated/Shared/Transfer/RevokeRefreshTokenRequestTransfer.php
RevokeRefreshTokenResponseTransfer class created src/Generated/Shared/Transfer/RevokeRefreshTokenResponseTransfer.php
OauthTokenCriteriaFilterTransfer class created src/Generated/Shared/Transfer/OauthTokenCriteriaFilterTransfer.php
RestCheckoutDataTransfer class created src/Generated/Shared/Transfer/RestCheckoutDataTransfer.php
CheckoutErrorTransfer class created src/Generated/Shared/Transfer/CheckoutErrorTransfer.php
CheckoutResponseTransfer class created src/Generated/Shared/Transfer/CheckoutResponseTransfer.php
CheckoutDataTransfer class created src/Generated/Shared/Transfer/CheckoutDataTransfer.php
RestShipmentsTransfer class created src/Generated/Shared/Transfer/RestShipmentsTransfer.php

4) Add translations

  1. Append glossary according to your configuration:
>checkout.validation.customer_address.not_found,Customer address with ID %id% not found.,en_US
checkout.validation.customer_address.not_found, Kundenaddresse mit ID %id% wurde nicht gefunden.,de_DE
checkout.validation.customer_address.not_applicable,Customer addresses are applicable only for customers.,en_US
checkout.validation.customer_address.not_applicable, Kundenaddressen sind nur für die Kunden anzuwenden.,de_DE
  1. Import data:
console data:import glossary
Verification

Make sure that, in the database, the configured data has been added to the spy_glossary table.

5) Set up behavior

Set up the following behaviors.

Migrate data to the database

Generate UUIDs for the existing entities in the spy_customer_address database table:

console uuid:generate Customer spy_customer_address
Verification

To verify the UUID field has been populated for all the records in the spy_customer_address table, run the SQL query:

SELECT COUNT(*) FROM spy_customer_address WHERE uuid IS NULL;

The result should be 0 records.

Configure Jenkins

Enable Jenkins to find and delete expired refresh tokens:

config/Zed/cronjobs/jenkins.php

<?php

/**
 * Notes:
 *
 * - jobs[]['name'] must not contains spaces or any other characters, that have to be urlencode()'d
 * - jobs[]['role'] default value is 'admin'
 */
$stores = require(APPLICATION_ROOT_DIR . '/config/Shared/stores.php');
$allStores = array_keys($stores);

...


/* Oauth */
$jobs[] = [
    'name' => 'remove-expired-refresh-tokens',
    'command' => '$PHP_BIN vendor/bin/console oauth:refresh-token:remove-expired',
    'schedule' => '*/5 * * * *',
    'enable' => true,
    'stores' => $allStores,
];

Enable resources and relationships

For information on how to protect resources, see Security and authentication.

  1. Activate the following plugins:
PLUGIN SPECIFICATION PREREQUISITES NAMESPACE
SetCustomerBeforeActionPlugin Adds customer data to the session. Expects the user field to be set in REST requests. Spryker\Glue\CustomersRestApi\Plugin
CustomersResourceRoutePlugin Registers the customers resource. Spryker\Glue\CustomersRestApi\Plugin
AddressesResourceRoutePlugin Registers the addresses resource. Spryker\Glue\CustomersRestApi\Plugin
CustomerForgottenPasswordResourceRoutePlugin Registers the customer-forgotten-password resource. Spryker\Glue\CustomersRestApi\Plugin
CustomerRestorePasswordResourceRoutePlugin Registers the customer-restore-password resource. Spryker\Glue\CustomersRestApi\Plugin
CustomerPasswordResourceRoutePlugin Registers the customer-password resource. Spryker\Glue\CustomersRestApi\Plugin
CustomersToAddressesRelationshipPlugin Adds the addresses resource as a relationship to the customers resource. Spryker\Glue\CustomersRestApi\Plugin
AccessTokensResourceRoutePlugin Registers the access-token resource. Spryker\Glue\AuthRestApi\Plugin
RefreshTokensResourceRoutePlugin Registers the refresh-tokens resource. Spryker\Glue\AuthRestApi\Plugin
TokenResourceRoutePlugin Registers the token resource. Spryker\Glue\AuthRestApi\Plugin\GlueApplication
CustomerConfirmationResourceRoutePlugin Registers the customer-confirmation resource. Spryker\Glue\CustomersRestApi\Plugin\GlueApplication
AccessTokenRestRequestValidatorPlugin Validates authentication tokens in the requests to protected resources. Spryker\Glue\AuthRestApi\Plugin
FormatAuthenticationErrorResponseHeadersPlugin Adds an authentication error to the header of requests with an invalid authentication token. Spryker\Glue\AuthRestApi\Plugin
RestUserFinderByAccessTokenPlugin Finds a REST user based on the provided REST request data. Spryker\Glue\AuthRestApi\Plugin
OauthClientInstallerPlugin Populates the database with OAuth client data. Spryker\Zed\Oauth\Communication\Plugin\Installer
OauthCustomerScopeInstallerPlugin Installs OAuth customer scope data. Spryker\Zed\OauthCustomerConnector\Communication\Plugin\Installer
CustomerOauthUserProviderPlugin Provides a customer OAuth user. Spryker\Zed\OauthCustomerConnector\Communication\Plugin
CustomerOauthScopeProviderPlugin Provides a list of customer scopes. Spryker\Zed\OauthCustomerConnector\Communication\Plugin
OauthExpiredRefreshTokenRemoverPlugin Removes expired refresh tokens based on the provided criteria transfer. Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth
OauthRefreshTokenCheckerPlugin Checks if a refresh token is revoked. Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth
OauthRefreshTokenReaderPlugin Finds a refresh token by the provided criteria transfer. Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth
OauthRefreshTokenRevokerPlugin Revokes a refresh token. Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth
OauthRefreshTokenPersistencePlugin Saves a refresh token. Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth
OauthRefreshTokensReaderPlugin Gets refresh tokens by the provided criteria. Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth
OauthRefreshTokensRevokerPlugin Revokes all refresh tokens. Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth
AddressByCheckoutDataResourceRelationshipPlugin Adds the addresses resource as a relationship. Spryker\Glue\CustomersRestApi\Plugin\GlueApplication
CustomerAddressSourceCheckerPlugin Checks if a customer address ID is provided in the address attributes. Spryker\Glue\CustomersRestApi\Plugin\ShipmentsRestApi
CustomerAddressCheckoutDataValidatorPlugin Collects shipping address UUIDs from checkoutDataTransfer.shipments. If the authenticated customer does not own the provided customer address, returns CheckoutResponseTransfer with an error. Spryker\Zed\CustomersRestApi\Communication\Plugin\CheckoutRestApi
CustomerAddressProviderStrategyPlugin Finds a customer address based on the UUID provided in RestAddressTransfer.id. If the address is found, returns it. Spryker\Zed\CustomersRestApi\Communication\Plugin\ShipmentsRestApi
BillingAddressCheckoutRequestAttributesValidatorPlugin Validates RestCheckoutRequestAttributesTransfer.billingAddress mandatory fields. Spryker\Glue\CustomersRestApi\Plugin\CheckoutRestApi
OauthTokenConsole Remove expired refresh tokens from the database. Spryker\Zed\Oauth\Communication\Console
ScopeCacheCollectorConsole Creates a cache file for collecting all existing scopes. Spryker\Zed\Oauth\Communication\Console
src/Pyz/Glue/GlueApplication/GlueApplicationDependencyProvider.php
<?php

namespace Pyz\Glue\GlueApplication;

use Spryker\Glue\AuthRestApi\Plugin\AccessTokensResourceRoutePlugin;
use Spryker\Glue\AuthRestApi\Plugin\FormatAuthenticationErrorResponseHeadersPlugin;
use Spryker\Glue\AuthRestApi\Plugin\GlueApplication\AccessTokenRestRequestValidatorPlugin;
use Spryker\Glue\AuthRestApi\Plugin\GlueApplication\TokenResourceRoutePlugin;
use Spryker\Glue\AuthRestApi\Plugin\RefreshTokensResourceRoutePlugin;
use Spryker\Glue\AuthRestApi\Plugin\RestUserFinderByAccessTokenPlugin;
use Spryker\Glue\CustomersRestApi\CustomersRestApiConfig;
use Spryker\Glue\CustomersRestApi\Plugin\AddressesResourceRoutePlugin;
use Spryker\Glue\CustomersRestApi\Plugin\CustomerForgottenPasswordResourceRoutePlugin;
use Spryker\Glue\CustomersRestApi\Plugin\CustomerPasswordResourceRoutePlugin;
use Spryker\Glue\CustomersRestApi\Plugin\CustomerRestorePasswordResourceRoutePlugin;
use Spryker\Glue\CustomersRestApi\Plugin\CustomersResourceRoutePlugin;
use Spryker\Glue\CustomersRestApi\Plugin\CustomersToAddressesRelationshipPlugin;
use Spryker\Glue\CustomersRestApi\Plugin\GlueApplication\CustomerConfirmationResourceRoutePlugin;
use Spryker\Glue\CustomersRestApi\Plugin\SetCustomerBeforeActionPlugin;
use Spryker\Glue\GlueApplication\GlueApplicationDependencyProvider as SprykerGlueApplicationDependencyProvider;
use Spryker\Glue\GlueApplicationExtension\Dependency\Plugin\ResourceRelationshipCollectionInterface;
use Spryker\Glue\CompanyBusinessUnitAddressesRestApi\Plugin\GlueApplication\CompanyBusinessUnitAddressByCheckoutDataResourceRelationshipPlugin;
use Spryker\Glue\CustomersRestApi\Plugin\GlueApplication\AddressByCheckoutDataResourceRelationshipPlugin;

class GlueApplicationDependencyProvider extends SprykerGlueApplicationDependencyProvider
{
    /**
     * @return array
     */
    protected function getResourceRoutePlugins(): array
    {
        return [
            new CustomersResourceRoutePlugin(),
            new CustomerForgottenPasswordResourceRoutePlugin(),
            new CustomerRestorePasswordResourceRoutePlugin(),
            new CustomerPasswordResourceRoutePlugin(),
            new AddressesResourceRoutePlugin(),
            new AccessTokensResourceRoutePlugin(),
            new RefreshTokensResourceRoutePlugin(),
            new TokenResourceRoutePlugin(),
            new CustomerConfirmationResourceRoutePlugin(),
        ];
    }

    /**
     * @return \Spryker\Glue\GlueApplicationExtension\Dependency\Plugin\ValidateRestRequestPluginInterface[]
     */
    protected function getValidateRestRequestPlugins(): array
    {
        return [
            new AccessTokenRestRequestValidatorPlugin(),
        ];
    }

    /**
     * @return \Spryker\Glue\GlueApplicationExtension\Dependency\Plugin\FormatResponseHeadersPluginInterface[]
     */
    protected function getFormatResponseHeadersPlugins(): array
    {
        return [
            new FormatAuthenticationErrorResponseHeadersPlugin(),
        ];
    }

    /**
     * @return array
     */
    protected function getControllerBeforeActionPlugins(): array
    {
        return [
            new SetCustomerBeforeActionPlugin(),
        ];
    }

    /**
     * @param \Spryker\Glue\GlueApplicationExtension\Dependency\Plugin\ResourceRelationshipCollectionInterface $resourceRelationshipCollection
     *
     * @return \Spryker\Glue\GlueApplicationExtension\Dependency\Plugin\ResourceRelationshipCollectionInterface
     */
    protected function getResourceRelationshipPlugins(
        ResourceRelationshipCollectionInterface $resourceRelationshipCollection
    ): ResourceRelationshipCollectionInterface {
        $resourceRelationshipCollection->addRelationship(
            CustomersRestApiConfig::RESOURCE_CUSTOMERS,
            new CustomersToAddressesRelationshipPlugin()
        );
        $resourceRelationshipCollection->addRelationship(
            CheckoutRestApiConfig::RESOURCE_CHECKOUT_DATA,
            new AddressByCheckoutDataResourceRelationshipPlugin()
        );

        return $resourceRelationshipCollection;
    }

    /**
     * @return \Spryker\Glue\GlueApplicationExtension\Dependency\Plugin\RestUserFinderPluginInterface[]
     */
    protected function getRestUserFinderPlugins(): array
    {
        return [
            new RestUserFinderByAccessTokenPlugin(),
        ];
    }
}
src/Pyz/Zed/Oauth/OauthDependencyProvider.php
<?php

namespace Pyz\Zed\Oauth;

use Spryker\Zed\Oauth\OauthDependencyProvider as SprykerOauthDependencyProvider;
use Spryker\Zed\OauthCustomerConnector\Communication\Plugin\Oauth\CustomerOauthScopeProviderPlugin;
use Spryker\Zed\OauthCustomerConnector\Communication\Plugin\Oauth\CustomerOauthUserProviderPlugin;
use Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth\OauthExpiredRefreshTokenRemoverPlugin;
use Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth\OauthRefreshTokenCheckerPlugin;
use Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth\OauthRefreshTokenPersistencePlugin;
use Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth\OauthRefreshTokenReaderPlugin;
use Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth\OauthRefreshTokenRevokerPlugin;
use Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth\OauthRefreshTokensReaderPlugin;
use Spryker\Zed\OauthRevoke\Communication\Plugin\Oauth\OauthRefreshTokensRevokerPlugin;

class OauthDependencyProvider extends SprykerOauthDependencyProvider
{
    /**
     * @return \Spryker\Zed\OauthExtension\Dependency\Plugin\OauthUserProviderPluginInterface[]
     */
    protected function getUserProviderPlugins(): array
    {
        return [
            new CustomerOauthUserProviderPlugin(),
        ];
    }

    /**
     * @return \Spryker\Zed\OauthExtension\Dependency\Plugin\OauthScopeProviderPluginInterface[]
     */
    protected function getScopeProviderPlugins(): array
    {
        return [
            new CustomerOauthScopeProviderPlugin(),
        ];
    }

    /**
     * @return \Spryker\Zed\OauthExtension\Dependency\Plugin\OauthRefreshTokenRevokerPluginInterface[]
     */
    protected function getOauthRefreshTokenRevokerPlugins(): array
    {
        return [
            new OauthRefreshTokenRevokerPlugin(),
        ];
    }

    /**
     * @return \Spryker\Zed\OauthExtension\Dependency\Plugin\OauthRefreshTokensRevokerPluginInterface[]
     */
    protected function getOauthRefreshTokensRevokerPlugins(): array
    {
        return [
            new OauthRefreshTokensRevokerPlugin(),
        ];
    }

    /**
     * @return \Spryker\Zed\OauthExtension\Dependency\Plugin\OauthRefreshTokenSaverPluginInterface[]
     */
    protected function getOauthRefreshTokenSaverPlugins(): array
    {
        return [
            new OauthRefreshTokenPersistencePlugin(),
        ];
    }

    /**
     * @return \Spryker\Zed\OauthExtension\Dependency\Plugin\OauthRefreshTokenCheckerPluginInterface[]
     */
    protected function getOauthRefreshTokenCheckerPlugins(): array
    {
        return [
            new OauthRefreshTokenCheckerPlugin(),
        ];
    }

    /**
     * @return \Spryker\Zed\OauthExtension\Dependency\Plugin\OauthExpiredRefreshTokenRemoverPluginInterface[]
     */
    protected function getOauthExpiredRefreshTokenRemoverPlugins(): array
    {
        return [
            new OauthExpiredRefreshTokenRemoverPlugin(),
        ];
    }

    /**
     * @return \Spryker\Zed\OauthExtension\Dependency\Plugin\OauthRefreshTokenReaderPluginInterface[]
     */
    protected function getOauthRefreshTokenReaderPlugins(): array
    {
        return [
            new OauthRefreshTokenReaderPlugin(),
        ];
    }

    /**
     * @return \Spryker\Zed\OauthExtension\Dependency\Plugin\OauthRefreshTokensReaderPluginInterface[]
     */
    protected function getOauthRefreshTokensReaderPlugins(): array
    {
        return [
            new OauthRefreshTokensReaderPlugin(),
        ];
    }
}

src/Pyz/Zed/Installer/InstallerDependencyProvider.php

<?php

namespace Pyz\Zed\Installer;

use Spryker\Zed\Installer\InstallerDependencyProvider as SprykerInstallerDependencyProvider;
use Spryker\Zed\Oauth\Communication\Plugin\Installer\OauthClientInstallerPlugin;
use Spryker\Zed\OauthCustomerConnector\Communication\Plugin\Installer\OauthCustomerScopeInstallerPlugin;

class InstallerDependencyProvider extends SprykerInstallerDependencyProvider
{
    /**
     * @return \Spryker\Zed\Installer\Dependency\Plugin\InstallerPluginInterface[]
     */
    public function getInstallerPlugins()
    {
        return [
            new OauthClientInstallerPlugin(),
            new OauthCustomerScopeInstallerPlugin(),
        ];
    }
}

src/Pyz/Glue/ShipmentsRestApi/ShipmentsRestApiDependencyProvider.php

<?php

namespace Pyz\Glue\ShipmentsRestApi;

use Spryker\Glue\CustomersRestApi\Plugin\ShipmentsRestApi\CustomerAddressSourceCheckerPlugin;
use Spryker\Glue\ShipmentsRestApi\ShipmentsRestApiDependencyProvider as SprykerShipmentsRestApiDependencyProvider;

class ShipmentsRestApiDependencyProvider extends SprykerShipmentsRestApiDependencyProvider
{
    /**
     * @return \Spryker\Glue\ShipmentsRestApiExtension\Dependency\Plugin\AddressSourceCheckerPluginInterface[]
     */
    protected function getAddressSourceCheckerPlugins(): array
    {
        return [
            new CustomerAddressSourceCheckerPlugin(),
        ];
    }
}

src/Pyz/Zed/CheckoutRestApi/CheckoutRestApiDependencyProvider.php

<?php

namespace Pyz\Zed\CheckoutRestApi;

use Spryker\Zed\CheckoutRestApi\CheckoutRestApiDependencyProvider as SprykerCheckoutRestApiDependencyProvider;
use Spryker\Zed\CustomersRestApi\Communication\Plugin\CheckoutRestApi\CustomerAddressCheckoutDataValidatorPlugin;

class CheckoutRestApiDependencyProvider extends SprykerCheckoutRestApiDependencyProvider
{
    /**
     * @return \Spryker\Zed\CheckoutRestApiExtension\Dependency\Plugin\CheckoutDataValidatorPluginInterface[]
     */
    protected function getCheckoutDataValidatorPlugins(): array
    {
        return [
            new CustomerAddressCheckoutDataValidatorPlugin(),
        ];
    }
}

src/Pyz/Zed/ShipmentsRestApi/ShipmentsRestApiDependencyProvider.php

<?php

namespace Pyz\Zed\ShipmentsRestApi;

use Spryker\Zed\CustomersRestApi\Communication\Plugin\ShipmentsRestApi\CustomerAddressProviderStrategyPlugin;
use Spryker\Zed\ShipmentsRestApi\ShipmentsRestApiDependencyProvider as SprykerShipmentsRestApiDependencyProvider;

/**
 * @method \Spryker\Zed\ShipmentsRestApi\ShipmentsRestApiConfig getConfig()
 */
class ShipmentsRestApiDependencyProvider extends SprykerShipmentsRestApiDependencyProvider
{
    /**
     * @return \Spryker\Zed\ShipmentsRestApiExtension\Dependency\Plugin\AddressProviderStrategyPluginInterface[]
     */
    protected function getAddressProviderStrategyPlugins(): array
    {
        return [
            new CustomerAddressProviderStrategyPlugin(),
        ];
    }
}

src/Pyz/Zed/CheckoutRestApi/CheckoutRestApiDependencyProvider.php

<?php

namespace Pyz\Zed\CheckoutRestApi;

use Spryker\Zed\CheckoutRestApi\CheckoutRestApiDependencyProvider as SprykerCheckoutRestApiDependencyProvider;
use Spryker\Zed\CustomersRestApi\Communication\Plugin\CheckoutRestApi\AddressQuoteMapperPlugin;
use Spryker\Zed\CustomersRestApi\Communication\Plugin\CheckoutRestApi\CustomerAddressCheckoutDataValidatorPlugin;
use Spryker\Zed\CustomersRestApi\Communication\Plugin\CheckoutRestApi\CustomerQuoteMapperPlugin;

class CheckoutRestApiDependencyProvider extends SprykerCheckoutRestApiDependencyProvider
{
    /**
     * @return \Spryker\Zed\CheckoutRestApiExtension\Dependency\Plugin\QuoteMapperPluginInterface[]
     */
    protected function getQuoteMapperPlugins(): array
    {
        return [
            new CustomerQuoteMapperPlugin(),
            new AddressQuoteMapperPlugin(),
        ];
    }

    /**
     * @return \Spryker\Zed\CheckoutRestApiExtension\Dependency\Plugin\CheckoutDataValidatorPluginInterface[]
     */
    protected function getCheckoutDataValidatorPlugins(): array
    {
        return [
            new CustomerAddressCheckoutDataValidatorPlugin(),
        ];
    }
}

src/Pyz/Glue/CheckoutRestApi/CheckoutRestApiDependencyProvider.php

<?php

namespace Pyz\Glue\CheckoutRestApi;

use Spryker\Glue\CheckoutRestApi\CheckoutRestApiDependencyProvider as SprykerCheckoutRestApiDependencyProvider;
use Spryker\Glue\CustomersRestApi\Plugin\CheckoutRestApi\BillingAddressCheckoutRequestAttributesValidatorPlugin;

class CheckoutRestApiDependencyProvider extends SprykerCheckoutRestApiDependencyProvider
{
    /**
     * @return array<\Spryker\Glue\CheckoutRestApiExtension\Dependency\Plugin\CheckoutRequestAttributesValidatorPluginInterface>
     */
    protected function getCheckoutRequestAttributesValidatorPlugins(): array
    {
        return [
            new BillingAddressCheckoutRequestAttributesValidatorPlugin(),
        ];
    }
}

src/Pyz/Zed/Console/ConsoleDependencyProvider.php

<?php

namespace Pyz\Zed\Console;

use Spryker\Zed\Console\ConsoleDependencyProvider as SprykerConsoleDependencyProvider;
use Spryker\Zed\Kernel\Container;
use Spryker\Zed\Oauth\Communication\Console\OauthTokenConsole;
use Spryker\Zed\Oauth\Communication\Console\ScopeCacheCollectorConsole;

class ConsoleDependencyProvider extends SprykerConsoleDependencyProvider
{
    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return array<\Symfony\Component\Console\Command\Command>
     */
    protected function getConsoleCommands(Container $container): array
    {
        $commands = [
            new OauthTokenConsole(),
            new ScopeCacheCollectorConsole(),
        ];

        return $commands;
    }
}
  1. Set up the OAuth client:
console setup:init-db
Verification

To verify that the OAuth client has been added to the spy_oauth_client table, run the SQL query:

SELECT * FROM spy_oauth_client WHERE identifier = 'some-client-identifier';

The output should contain one record.

Verification

Make sure the following endpoints are available:

  • https://glue.mysprykershop.com/customers
  • https://glue.mysprykershop.com/addresses
  • https://glue.mysprykershop.com/customer-password
  • https://glue.mysprykershop.com/customer-forgotten-password
  • https://glue.mysprykershop.com/customer-restore-password
  • https://glue.mysprykershop.com/access-tokens
  • https://glue.mysprykershop.com/refresh-tokens
  • https://glue.mysprykershop.com/token
  • https://glue.mysprykershop.com/customer-confirmation
Verification

Verify that the addresses resource relationship is registered correctly:

  1. Add a customer address.

  2. Send the request: GET https://glue.mysprykershop.com/customers/{{customer_id}}?include=addresses.

Make sure the response contains the relationships to the addresses resource.

Verification

To verify that AccessTokenRestRequestValidatorPlugin is set up correctly, send the POST https://glue.mysprykershop.com/refresh-tokens request without the Authorization header or with an outdated or incorrect authentication token.

Make sure one of the following errors is returned:

{
    "errors": [
        {
            "detail": "Invalid access token.",
            "status": 401,
            "code": "001"
        }
    ]
}
{
    "errors": [
        {
            "detail": "Missing access token.",
            "status": 403,
            "code": "002"
        }
    ]
}
Verification

Verify that FormatAuthenticationErrorResponseHeadersPlugin is set up:

  1. Retrieve an authentication token for a user.

  2. Check that you can access an endpoint that requires this token only when you add it to the request.

Verification

Verify that RestUserFinderByAccessTokenPlugin is set up:

  1. Retrieve an authentication token for a user.

  2. Send a request to an endpoint that requires the data of the current customer, like /carts or /customer/{{customerReference}}.

  3. Check that the system detects the current customer correctly: you should be able to retrieve all the data you can see on the Storefront, like cart items or prices.

Verification

To verify that OauthRefreshTokenSaverPlugin is set up, send the request: POST https://glue.mysprykershop.com/access-tokens.

Make sure the following applies:

  • The response contains access-token and refresh-token sections.

  • A new record has been added to the spy_oauth_refresh_token table. To check it, run the SQL query:

    SELECT * FROM spy_oauth_refresh_token WHERE customer_reference = 'authenticated-customer-reference';
    
Verification

Verify that OauthRefreshTokenReaderPlugin and OauthRefreshTokenRevokerPlugin are set up:

  1. Send the request: DELETE https://glue.mysprykershop.com/refresh-tokens/{{refresh_token}.
  2. To check that the refresh token has been revoked, run the following SQL query:
SELECT * FROM spy_oauth_refresh_token WHERE revoked_at IS NOT NULL;

Make sure the spy_oauth_refresh_token::revoked_at database field is not empty.

Verification

Verify that OauthRefreshTokensReaderPlugin and OauthRefreshTokensRevokerPlugin are set up:

  1. Prepare several refresh tokens.

  2. Send the request: DELETE https://glue.mysprykershop.com/refresh-tokens/mine.

  3. Run the following SQL query:

    SELECT * FROM spy_oauth_refresh_token WHERE customer_reference = 'authenticated-customer-reference';
    

Check that the spy_oauth_refresh_token::revoked_at values of all the records related to the authenticated customer are not empty.

Verification

To verify that OauthRefreshTokenCheckerPlugin is set up, send the following request with a revoked refresh token:

POST https://glue.mysprykershop.com/refresh-tokens

{
    "data": {
        "type": "refresh-tokens",
        "attributes": {
            "refreshToken": "{{revoked_refresh_token}}"
        }
    }
}

Make sure you get the following response:

{
    "errors": [
        {
            "code": "004",
            "status": 401,
            "detail": "Failed to refresh token."
        }
    ]
}
Verification

To verify OauthRefreshTokenRemoverPlugin is set up, delete expired refresh tokens:

console oauth:refresh-token:remove-expired

Make sure all the expired refresh tokens older than defined by the removal interval you’ve configured in Spryker\Shared\Oauth\OauthConfig::getRefreshTokenRetentionInterval() have been deleted.

Verification

To verify AddressByCheckoutDataResourceRelationshipPlugin is set up, send the POST https://glue.mysprykershop.com/checkout-data?include=addresses request. Make sure the response contains the information from the addresses resource.

Verification

Make sure the following console commands are available:

  • Create a cache file for all existing scopes:
console oauth:scope-collection-file:generate
  • Remove expired refresh tokens from the database:
console oauth:refresh-token:remove-expired