adexos/m2-oauth2-league-bridge

There is no license information available for the latest version (v1.0.4) of this package.

Oauth2 League Magento2 bridge

Installs: 277

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 2

Forks: 1

Open Issues: 0

Type:magento2-module

v1.0.4 2024-03-09 20:42 UTC

This package is auto-updated.

Last update: 2024-04-09 21:01:38 UTC


README

This module is intended to be a bridge client connection from OAuth 2 implementation for Magento 2.

Installation

You can install it by typing : composer require adexos/m2-oauth2-league-bridge

How to use ?

Create your own client extending the bridge one :

<?php

declare(strict_types=1);

namespace Adexos\MyIdentityOauthModule\Client;

use Adexos\Oauth2LeagueBridge\Client\OauthClientFactory;

class MyOauthIdentity extends OauthClientFactory
{

}

This class can be empty, it should only be created for the purpose of the di.xml.

Then you can add your own configuration in the di.xml :

<type name="Adexos\MyIdentityOauthModule\Client\MyOauthIdentity">
    <arguments>
        <argument name="baseUrlConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_BASE_URL
        </argument>
        <argument name="clientIdConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_CLIENT_ID
        </argument>
        <argument name="clientSecretConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_CLIENT_SECRET
        </argument>
        <argument name="authorizeEndpointConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_AUTHORIZE_ENDPOINT
        </argument>
        <argument name="accessTokenEndpointConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_ACCESS_TOKEN_ENDPOINT
        </argument>
        <argument name="resourceOwnerDetailsEndpointConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_RESOURCE_OWNER_DETAILS_ENDPOINT_CONFIG_PATH
        </argument>
        <argument name="persistence" xsi:type="object">
            Adexos\Oauth2LeagueBridge\Persistence\SessionPersistence
        </argument>
    </arguments>
</type>

The paths refer to your configuration. It's up to you to create your own system.xml depending on the path you give.

Optional : You can set the backend_model entry in your config.xml and system.xml as Magento\Config\Model\Config\Backend\Encrypted if you want to store your client secret in an encrypted way.

Example :

<!-- config.xml -->

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
    <default>
        <adexos>
            <oauth2>
                <client_secret backend_model="Magento\Config\Model\Config\Backend\Encrypted"/>
            </oauth2>
        </adexos>
    </default>
</config>
<!-- system.xml -->

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="adexos_tab" translate="label" sortOrder="145">
            <label>Adexos</label>
        </tab>

        <section id="adx" translate="label" sortOrder="300" showInDefault="1" showInWebsite="1" showInStore="1">
            <class>separator-top</class>
            <label>Reach 5</label>
            <tab>adeo_tab</tab>
            <resource>Magento_Config::config</resource>
            <group id="oauth2_identity" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="1"
                   showInStore="1">
                <field id="client_secret" translate="label" type="obscure" sortOrder="40" showInDefault="1"
                       showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Client Secret</label>
                    <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
                </field>
            </group>
        </section>
    </system>
</config>

Here is a template as an example :

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <tab id="adexos_tab" translate="label" sortOrder="145">
            <label>Adexos</label>
        </tab>

        <section id="adx" translate="label" sortOrder="300" showInDefault="1" showInWebsite="1" showInStore="1">
            <class>separator-top</class>
            <label>Reach 5</label>
            <tab>adeo_tab</tab>
            <resource>Magento_Config::config</resource>
            <group id="oauth2_identity" translate="label" type="text" sortOrder="0" showInDefault="1" showInWebsite="1"
                   showInStore="1">
                <label>[ADX Identity] Configuration</label>
                <field id="is_enabled" translate="label" type="select" sortOrder="1" showInDefault="1" showInWebsite="1"
                       showInStore="1">
                    <label>Is enabled</label>
                    <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
                </field>
                <field id="base_url" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1"
                       showInStore="1" canRestore="1">
                    <label>Base Endpoint URL</label>
                </field>
                <field id="client_id" translate="label" type="obscure" sortOrder="30" showInDefault="1"
                       showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Client ID</label>
                    <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
                </field>
                <field id="client_secret" translate="label" type="obscure" sortOrder="40" showInDefault="1"
                       showInWebsite="1" showInStore="1" canRestore="1">
                    <label>Client Secret</label>
                    <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
                </field>
                <field id="public_key_jwt_token" translate="label" type="textarea" sortOrder="50" showInDefault="1"
                       showInWebsite="1" showInStore="1" canRestore="1">
                    <label>RSA Public Key to decode JWT Token properly and ensure security</label>
                </field>
                <field id="authorize_endpoint" translate="label" type="text" sortOrder="60" showInDefault="1"
                       showInWebsite="1"
                       showInStore="1" canRestore="1">
                    <label>Authorize Endpoint</label>
                </field>
                <field id="access_token_endpoint" translate="label" type="text" sortOrder="70" showInDefault="1"
                       showInWebsite="1"
                       showInStore="1" canRestore="1">
                    <label>Access Token Endpoint</label>
                </field>
            </group>
        </section>
    </system>
</config>

Please do not forget to create a config.xml as well.

Finally, you can use it this way :

<?php

declare(strict_types=1);

namespace Adexos\MyIdentityOauthModule\Http\Token;

use Adexos\MyIdentityOauthModule\Client\MyOauthIdentity;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use Magento\Framework\Exception\LocalizedException;

class IdentityTokenStrategy
{
    private MyOauthIdentity $myOauthIdentity;

    public function __construct(MyOauthIdentity $myOauthIdentity)
    {
        $this->myOauthIdentity = $myOauthIdentity;
    }

    /**
     * @throws LocalizedException
     * @throws IdentityProviderException
     */
    public function find(): ?string
    {
        $accessToken = $this->myOauthIdentity->getAccessTokenWithPersistence(
              new Password(),
              'custom_identifier'
              ['username' => 'a', 'password' => 'b', 'scope' => 'custom_scopes']
        );

        if ($accessToken === null) {
            return null;
        }

        return $accessToken->getToken();
    }
}

Of course you need to update the grant_type and the options depending on your needs. The identifier set as a second parameter will be used to persist the token somewhere based on your configuration.

If you now want to retrieve your token and update it with the refresh token if it exists, you can simply call :

$accessToken = $this->myOauthIdentity->getCurrentTokenWithRefresh('custom_identifier');

Cache systems

There is two major cache systems used in this module to store your OAuth tokens :

  • Session cache
  • Magento native cache that drives file, Redis and database cache

Session cache

This cache is especially useful to use with Identity tokens (login) that must be stored within the customer session data, here is how you can implement it :

<type name="Adexos\MyIdentityOauthModule\Client\MyOauthIdentity">
    <arguments>
        <argument name="baseUrlConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_BASE_URL
        </argument>
        <argument name="clientIdConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_CLIENT_ID
        </argument>
        <argument name="clientSecretConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_CLIENT_SECRET
        </argument>
        <argument name="authorizeEndpointConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_AUTHORIZE_ENDPOINT
        </argument>
        <argument name="accessTokenEndpointConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_ACCESS_TOKEN_ENDPOINT
        </argument>
        <argument name="resourceOwnerDetailsEndpointConfigPath" xsi:type="string">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_RESOURCE_OWNER_DETAILS_ENDPOINT_CONFIG_PATH
        </argument>
        <argument name="persistence" xsi:type="object">
            Adexos\Oauth2LeagueBridge\Persistence\SessionPersistence
        </argument>
    </arguments>
</type>

Classic cache

It uses Magento native system, so you can use any cache that Magento supports : file, Redis or database.

This cache is more useful when you don't need to store the token for the current session but in a more global context such as a Management token.

Here is how you can implement it :

<type name="Adexos\MyManagementOauthModule\Client\MyOauthManagement">
    <arguments>
        <argument name="baseUrlConfigPath" xsi:type="const">
            Adexos\MyManagementOauthModule\Model\Config::XML_PATH_BASE_URL
        </argument>
        <argument name="clientIdConfigPath" xsi:type="const">
            Adexos\MyManagementOauthModule\Model\Config::XML_PATH_CLIENT_ID
        </argument>
        <argument name="clientSecretConfigPath" xsi:type="const">
            Adexos\MyManagementOauthModule\Model\Config::XML_PATH_CLIENT_SECRET
        </argument>
        <argument name="authorizeEndpointConfigPath" xsi:type="const">
            Adexos\MyManagementOauthModule\Model\Config::XML_PATH_AUTHORIZE_ENDPOINT
        </argument>
        <argument name="accessTokenEndpointConfigPath" xsi:type="const">
            Adexos\MyManagementOauthModule\Model\Config::XML_PATH_ACCESS_TOKEN_ENDPOINT
        </argument>
        <argument name="resourceOwnerDetailsEndpointConfigPath" xsi:type="string">
            Adexos\MyManagementOauthModule\Model\Config::XML_PATH_RESOURCE_OWNER_DETAILS_ENDPOINT_CONFIG_PATH
        </argument>
        <argument name="persistence" xsi:type="object">AdexosManagementOauthPersistence</argument>
    </arguments>
</type>

<virtualType name="AdexosManagementOauthPersistence" type="Adexos\Oauth2LeagueBridge\Persistence\CachePersistence">
    <arguments>
        <argument name="cache" xsi:type="object">AdexosManagementOauthDataCache</argument>
    </arguments>
</virtualType>

<virtualType name="AdexosManagementOauthDataCache" type="Magento\Framework\App\Cache">
    <arguments>
        <argument name="cacheIdentifier" xsi:type="string">adexos_management_oauth_data_cache</argument>
    </arguments>
</virtualType>

Please note that the cacheIdentifier will be used in the env.php file to determine how you are storing your tokens :

Database

env.php

    'cache' => [
        'frontend' => [
            'adexos_management_oauth_data_cache' => [
                'backend' => \Magento\Framework\Cache\Backend\Database::class,
                'backend_options' => []
            ]
        ]
    ]
Redis

env.php

'cache' => [
        'frontend' => [
            'adexos_management_oauth_data_cache' => [
                'backend' => \Magento\Framework\Cache\Backend\Redis::class,
                'backend_options' => [
                    'server' => 'redis',
                    'database' => '3',
                    'port' => '6379',
                    'compress_data' => '1'
                ]
            ]
        ]
    ]

/!\ Please use a specific database to the cache, do not use the same database as the session.

File

env.php

    'cache' => [
        'frontend' => [
            'adexos_management_oauth_data_cache' => [
                'backend' => 'database',
                'backend_options' => []
            ]
        ]
    ]

Cache lifetime

If you need to add a specific lifetime for the cache which is 1 hour by default, you can override in your di.xml the specificLifetime property :

<virtualType name="AdexosManagementOauthPersistence" type="Adexos\Oauth2LeagueBridge\Persistence\CachePersistence">
    <arguments>
        <argument name="cache" xsi:type="object">AdexosManagementOauthDataCache</argument>
        <!-- 2 days -->
        <argument name="specificLifetime" xsi:type="number">172800</argument>
    </arguments>
</virtualType>

Tags

Cache is regrouped by tags. You may want to specify tags to clean specific global area of the cache you created.

In the default configuration, the tag is : adx_oauth2_league_bridge

You can override it by updating your di.xml through the property tags :

<virtualType name="AdexosManagementOauthPersistence" type="Adexos\Oauth2LeagueBridge\Persistence\CachePersistence">
    <arguments>
        <argument name="cache" xsi:type="object">AdexosManagementOauthDataCache</argument>
        <!-- 2 days -->
        <argument name="specificLifetime" xsi:type="number">172800</argument>
        <argument name="tags" xsi:type="array">
            <item name="fstTag" xsi:type="string">my_first_tag</item>
            <item name="sndTag" xsi:type="string">my_second_tag</item>
        </argument>
    </arguments>
</virtualType>

You can add many tags you want.

Decoding the token

You can decode the token after you fetched it, it will also do the verification with the public key to ensure the integrity of the received token :

$jwtResponse = $this->client->getAccessTokenWithPersistence(
    new Password(),
    IdentityTokenStrategy::OAUTH_TOKEN_CACHE_IDENTIFIER,
    ['username' => $username, 'password' => $password, 'scope' => self::TOKEN_SCOPE]
);

$this->jwtDecoder->decode(
    $token->getValues()['id_token'],
    IdTokenDTO::class,
    $this->scopeConfig->getValue(Config::XML_PATH_PUBLIC_KEY_JWT_TOKEN)
);

The jwtDecoder class is Adexos\Oauth2LeagueBridge\Decoder\JwtDecoder

As a second parameter of the decode method, you must pass a DTO implementing Adexos\Oauth2LeagueBridge\Decoder\Model\JwtResultInterface which is the content representation of the token. After that, you can manipulate the token result through an object and do whatever you want.

Alternative option provider (HttpBasicAuth/PostAuth)

The default option provider for the credentials concerning the access token is the PostAuthOptionProvider.

However, there are some systems when you need to instead use a HttpBasicAuthOptionProvider or even a custom one.

You simply need to add it in the di.xml of your client :

<type name="Adexos\MyIdentityOauthModule\Client\MyOauthIdentity">
    <arguments>
        <argument name="baseUrlConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_BASE_URL
        </argument>
        <argument name="clientIdConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_CLIENT_ID
        </argument>
        <argument name="clientSecretConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_CLIENT_SECRET
        </argument>
        <argument name="authorizeEndpointConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_AUTHORIZE_ENDPOINT
        </argument>
        <argument name="accessTokenEndpointConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_ACCESS_TOKEN_ENDPOINT
        </argument>
        <argument name="resourceOwnerDetailsEndpointConfigPath" xsi:type="const">
            Adexos\MyIdentityOauthModule\Model\Config::XML_PATH_RESOURCE_OWNER_DETAILS_ENDPOINT_CONFIG_PATH
        </argument>
        <argument name="persistence" xsi:type="object">
            Adexos\Oauth2LeagueBridge\Persistence\SessionPersistence
        </argument>
        <argument name="optionProvider" xsi:type="object">
            League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider
        </argument>
    </arguments>
</type>

If you need to implement your own auth provider, simply create a class implementing League\OAuth2\Client\OptionProvider\OptionProviderInterface.