Explorar o código

#3753 Azure AD B2C Connect

Alex Trofimov %!s(int64=4) %!d(string=hai) anos
pai
achega
da76aea1aa

+ 21 - 0
modules/boonex/azure_b2c_con/classes/BxAzrB2CAlerts.php

@@ -0,0 +1,21 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    AzureB2CConnect Azure B2C Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAzrB2CAlerts extends BxBaseModConnectAlerts
+{
+    function __construct()
+    {
+        parent::__construct();
+        $this -> oModule = BxDolModule::getInstance('bx_azrb2c');
+    }
+}
+
+/** @} */

+ 54 - 0
modules/boonex/azure_b2c_con/classes/BxAzrB2CConfig.php

@@ -0,0 +1,54 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    AzureB2CConnect Azure B2C Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAzrB2CConfig extends BxBaseModConnectConfig
+{
+    public $sDomain;
+    public $sTenant;
+    public $sPolicy;
+
+    public $sTenantID = 'common';
+    public $sClientID;
+    public $sSecret;
+
+    public $sScope = 'openid';// 'openid offline_acces https://graph.microsoft.com/User.Read';
+    public $sLogoutUrl = 'https://login.microsoftonline.com/common/wsfederation?wa=wsignout1.0';
+
+    public $sPageStart;
+    public $sPageHandle;
+
+    function __construct($aModule)
+    {
+        parent::__construct($aModule);
+
+        $this -> sDomain = getParam('bx_azrb2c_domain');
+        $this -> sTenant = getParam('bx_azrb2c_tenant');
+        $this -> sPolicy = getParam('bx_azrb2c_policy');
+
+        $this -> sTenantID = getParam('bx_azrb2c_tenant_id');
+        $this -> sClientID = getParam('bx_azrb2c_client_id');
+        $this -> sScope = getParam('bx_azrb2c_client_id');
+        $this -> sSecret = getParam('bx_azrb2c_secret');
+
+        $this -> sEmailTemplatePasswordGenerated = 'bx_azrb2c_password_generated';
+        $this -> sDefaultTitleLangKey = '_bx_azrb2c';
+
+        $this -> sRedirectPage = getParam('bx_azrb2c_redirect_page');
+        $this -> sProfilesModule = getParam('bx_azrb2c_module');
+        $this -> isAlwaysConfirmEmail = (bool)getParam('bx_azrb2c_confirm_email'); 
+        $this -> isAlwaysAutoApprove = (bool)getParam('bx_azrb2c_approve');
+
+        $this -> sPageStart = BX_DOL_URL_ROOT . $this -> getBaseUri() . 'start';
+        $this -> sPageHandle = BX_DOL_URL_ROOT . $this -> getBaseUri() . 'handle';
+    }
+}
+
+/** @} */

+ 20 - 0
modules/boonex/azure_b2c_con/classes/BxAzrB2CDb.php

@@ -0,0 +1,20 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    AzureB2CConnect Azure B2C Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAzrB2CDb extends BxBaseModConnectDb
+{
+    function __construct(&$oConfig)
+    {
+        parent::__construct($oConfig);
+    }
+}
+
+/** @} */

+ 198 - 0
modules/boonex/azure_b2c_con/classes/BxAzrB2CModule.php

@@ -0,0 +1,198 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    AzureB2CConnect Azure B2C Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aPathInfo = pathinfo(__FILE__);
+require_once ($aPathInfo['dirname'] . '/../vendor/autoload.php');
+
+use Alancting\Microsoft\JWT\AzureAd\AzureAdConfiguration;
+use Alancting\Microsoft\JWT\AzureAd\AzureAdAccessTokenJWT;
+use Alancting\Microsoft\JWT\AzureAd\AzureAdIdTokenJWT;
+
+/**
+ * This Azude AD connect code was created with instructions from:
+ * https://www.sipponen.com/archives/4024
+ */
+class BxAzrB2CModule extends BxBaseModConnectModule
+{
+    function __construct(&$aModule)
+    {
+        parent::__construct($aModule);
+    }
+
+    /**
+     * Redirect to remote site login form
+     *
+     * @return n/a - redirect or HTML page in case of error
+     */
+    function actionStart()
+    {
+        if (isset($_GET["error"])) {
+            $this->_oTemplate->getPage(_t($this->_oConfig->sDefaultTitleLangKey), DesignBoxContent(_t($this->_oConfig->sDefaultTitleLangKey), MsgBox(bx_get('error'))));
+            exit;
+        }
+
+        if (isLogged()) {
+            $this->_redirect ($this -> _oConfig -> sDefaultRedirectUrl);
+        }
+
+        if (!$this->_oConfig->sClientID || !$this->_oConfig->sSecret) {
+            require_once(BX_DIRECTORY_PATH_INC . 'design.inc.php');
+            bx_import('BxDolLanguages');
+            $sCode =  MsgBox( _t('_bx_azrb2c_profile_error_api_keys') );
+            $this->_oTemplate->getPage(_t('_bx_azrb2c'), $sCode);
+        } 
+        else {
+
+            // First stage of the authentication process; This is just a simple redirect (first load of this page)
+
+            $verifier_bytes = random_bytes(64);
+            $code_verifier = rtrim(strtr(base64_encode($verifier_bytes), "+/", "-_"), "=");
+            BxDolSession::getInstance()->setValue('code_verifier', $code_verifier);
+            $challenge_bytes = hash("sha256", $code_verifier, true);
+            $code_challenge = rtrim(strtr(base64_encode($challenge_bytes), "+/", "-_"), "=");
+
+            $sBaseUrl = "https://" . $this->_oConfig->sDomain . "/" . $this->_oConfig->sTenant . "/" . $this->_oConfig->sPolicy;//B2C_1_login_una";
+            $sUrl = bx_append_url_params($sBaseUrl . "/oauth2/v2.0/authorize", [
+                'state' => $this->_genToken(), // This at least semi-random string is likely good enough as state identifier
+                'scope' => $this->_oConfig->sScope, 
+                'response_type' => 'code',
+                'response_mode' => 'query',
+                'prompt' => 'login',
+                'client_id' => $this->_oConfig->sClientID,
+                'redirect_uri' => $this->_oConfig->sPageHandle,
+                'client_info' => 1,
+                'code_challenge_method' => 'S256',
+                'code_challenge' => $code_challenge,
+            ]);
+            $this->_redirect($sUrl); // So off you go my dear browser and welcome back for round two after some redirects at Azure end
+        }
+    }
+
+    function actionHandle()
+    {
+        require_once(BX_DIRECTORY_PATH_INC . 'design.inc.php');
+
+        // check CSRF token
+        if ($this->_getToken() != bx_get('state')) {
+            $this->_oTemplate->getPage(_t('_Error'), MsgBox(_t('_sys_connect_state_invalid')));
+            return;
+        }
+
+        // check code
+        $sCode = bx_get('code');
+        if (!$sCode || bx_get('error')) {
+            $sErrorDescription = bx_get('error_description') ? bx_get('error_description') : _t('_error occured');
+            $this->_oTemplate->getPage(_t('_Error'), MsgBox($sErrorDescription));
+            return;
+        }
+
+        // Verifying the received tokens with Azure and finalizing the authentication part
+
+        $sBaseUrl = "https://" . $this->_oConfig->sDomain . "/" . $this->_oConfig->sTenant . "/" . $this->_oConfig->sPolicy;
+        $s = bx_file_get_contents($sBaseUrl . "/oauth2/v2.0/token", [
+            'grant_type'    => 'authorization_code',
+            'client_id'     => $this->_oConfig->sClientID,
+            'scope'         => $this->_oConfig->sScope,
+            'redirect_uri'  => $this->_oConfig->sPageHandle,
+            'code'          => $sCode,
+            'client_secret' => $this->_oConfig->sSecret,
+            'code_verifier' => BxDolSession::getInstance()->getValue('code_verifier'),
+        ], 'post', array ('Content-Type: application/x-www-form-urlencoded'));
+        $aAuthData = $this->_decodeResponseAndHandleError($s);
+
+        // get the data, especially id_token
+
+        $sIdToken = $aAuthData['id_token'];
+
+
+        // parse token to get the data
+        // 
+        // to make library to with B2C correctly please comment out the following lines in 
+        // vendor/alancting/php-microsoft-jwt/src/Base/MicrosoftConfiguration.php 
+        // file near ~240 line
+        // @code
+        // !array_key_exists('userinfo_endpoint', $data) 
+        // !array_key_exists('device_authorization_endpoint', $data)
+        // @endcode
+        // then further in the code add checking for not set values for these values
+
+    	$aConfigOptions = [
+            'tenant' => $this->_oConfig->sTenantID,
+            'tenant_id' => $this->_oConfig->sTenantID,
+  	    	'client_id' => $this->_oConfig->sClientID,
+            'config_uri' => "https://" . $this->_oConfig->sDomain . "/" . $this->_oConfig->sTenant . "/"  . $this->_oConfig->sPolicy . "/v2.0/.well-known/openid-configuration",
+	    ];
+        $oConfig = new AzureAdConfiguration($aConfigOptions);
+        $oIdTokenJWT = new AzureAdIdTokenJWT($oConfig, $sIdToken);
+        $aUserData = [
+            'id' => $oIdTokenJWT->get('oid'),
+            'name' => $oIdTokenJWT->get('given_name'),
+            'last_name' => $oIdTokenJWT->get('family_name'),
+            'email' => reset($oIdTokenJWT->get('emails')),
+        ];
+
+        $aRemoteProfileInfo = $aUserData;
+
+        if ($aRemoteProfileInfo) {
+
+            // check if user logged in before
+            $iLocalProfileId = $this->_oDb->getProfileId($aRemoteProfileInfo['id']);
+            
+            if ($iLocalProfileId && $oProfile = BxDolProfile::getInstance($iLocalProfileId)) {
+                // user already exists
+                $this->setLogged($oProfile ->id());
+            }             
+            else {  
+                // register new user
+                $this->_createProfile($aRemoteProfileInfo);
+            }
+        } 
+        else {
+            $this->_oTemplate->getPage(_t('_Error'), MsgBox(_t('_sys_connect_profile_error_info')));
+        }
+    }
+
+    /**
+     * @param $aProfileInfo - remote profile info
+     * @param $sAlternativeName - suffix to add to NickName to make it unique
+     * @return profile array info, ready for the local database
+     */
+    protected function _convertRemoteFields($aProfileInfo, $sAlternativeName = '')
+    {
+        $aProfileFields = $aProfileInfo;
+
+        $aProfileFields['fullname'] = !empty($aProfileInfo['last_name']) ? $aProfileInfo['name'] . ' ' . $aProfileInfo['last_name'] : $aProfileFields['name'];
+        $aProfileFields['picture'] = ''; // isset($aProfileInfo['picture']) ? $aProfileInfo['picture'] : '';
+        $aProfileFields['allow_view_to'] = getParam('bx_azrb2c_privacy');
+
+        return $aProfileFields;
+    }
+
+    protected function _decodeResponseAndHandleError($s, $bDisplayErrorPage = true)
+    {
+        if (!$s || NULL === ($aData = json_decode($s, true)) || !$aData || isset($aData['error'])) {
+            if (is_array($aData['error']) && !empty($aData['error']['message']))
+                $sErrorDescription = $aData['error']['message'];
+            else
+                $sErrorDescription = isset($aData['error_description']) ? $aData['error_description'] : _t('_error occured');
+            if ($bDisplayErrorPage) {
+                $this->_oTemplate->getPage(_t('_Error'), MsgBox($sErrorDescription));
+                exit;
+            }
+            else {  
+                return false;
+            }
+        }
+        return $aData;
+    }
+}
+
+/** @} */

+ 31 - 0
modules/boonex/azure_b2c_con/classes/BxAzrB2CStudioPage.php

@@ -0,0 +1,31 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    AzureB2CConnect Azure B2C Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAzrB2CStudioPage extends BxTemplStudioModule
+{
+    function __construct($sModule, $mixedPageName, $sPage = "")
+    {
+        parent::__construct($sModule, $mixedPageName, $sPage);
+
+        $this->aMenuItems = array(
+            'settings' => array('name' => 'settings', 'icon' => 'cogs', 'title' => '_adm_lmi_cpt_settings'),
+            'help' => array('name' => 'help', 'icon' => 'question', 'title' => '_sys_connect_information'),
+        );
+    }
+    
+    function getHelp ()
+    {
+        $oModule = BxDolModule::getInstance('bx_azrb2c'); 
+        return _t('_bx_azrb2c_information_block', BX_DOL_URL_ROOT . $oModule -> _oConfig -> getBaseUri() . 'handle');
+    }
+}
+
+/** @} */

+ 20 - 0
modules/boonex/azure_b2c_con/classes/BxAzrB2CTemplate.php

@@ -0,0 +1,20 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    AzureB2CConnect Azure B2C Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAzrB2CTemplate extends BxBaseModConnectTemplate
+{
+    function __construct(&$oConfig, &$oDb)
+    {
+        parent::__construct($oConfig, $oDb);
+    }
+}
+
+/** @} */

+ 5 - 0
modules/boonex/azure_b2c_con/composer.json

@@ -0,0 +1,5 @@
+{
+    "require": {
+        "alancting/php-microsoft-jwt": "^1.2"
+    }
+}

+ 794 - 0
modules/boonex/azure_b2c_con/composer.lock

@@ -0,0 +1,794 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "06ce0601addee5c523c13737da20a144",
+    "packages": [
+        {
+            "name": "alancting/php-microsoft-jwt",
+            "version": "1.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/alancting/php-microsoft-jwt.git",
+                "reference": "23fe44378118e0fa10f6fa62e7e0edbbd46554d7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/alancting/php-microsoft-jwt/zipball/23fe44378118e0fa10f6fa62e7e0edbbd46554d7",
+                "reference": "23fe44378118e0fa10f6fa62e7e0edbbd46554d7",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1.3",
+                "symfony/cache": "^4.4 || ^5.0"
+            },
+            "require-dev": {
+                "dg/bypass-finals": "^1.3",
+                "mockery/mockery": "^1.3",
+                "php-coveralls/php-coveralls": "^2.3",
+                "phpunit/phpunit": "^9.5"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Alancting\\Microsoft\\JWT\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Neuman Vong",
+                    "email": "neuman+pear@twilio.com",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Anant Narayanan",
+                    "email": "anant@php.net",
+                    "role": "Developer"
+                },
+                {
+                    "name": "alancting",
+                    "homepage": "https://github.com/alancting"
+                }
+            ],
+            "description": "A simple library to validate and decode Microsoft Azure Active Directory (Azure AD), Microsoft Active Directory Federation Services (ADFS) JSON Web Tokens (JWT) in PHP, conforming to RFC 7519",
+            "homepage": "https://github.com/alancting/php-microsoft-jwt",
+            "keywords": [
+                "OpenId",
+                "ad",
+                "adfs",
+                "azure",
+                "jwt",
+                "microsoft",
+                "php"
+            ],
+            "support": {
+                "issues": "https://github.com/alancting/php-microsoft-jwt/issues",
+                "source": "https://github.com/alancting/php-microsoft-jwt/tree/v1.2.1"
+            },
+            "time": "2021-04-11T06:50:33+00:00"
+        },
+        {
+            "name": "psr/cache",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/cache.git",
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+                "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Cache\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for caching libraries",
+            "keywords": [
+                "cache",
+                "psr",
+                "psr-6"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/cache/tree/master"
+            },
+            "time": "2016-08-06T20:24:11+00:00"
+        },
+        {
+            "name": "psr/container",
+            "version": "1.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf",
+                "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "support": {
+                "issues": "https://github.com/php-fig/container/issues",
+                "source": "https://github.com/php-fig/container/tree/1.1.1"
+            },
+            "time": "2021-03-05T17:36:06+00:00"
+        },
+        {
+            "name": "psr/log",
+            "version": "1.1.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/log.git",
+                "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+                "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Log\\": "Psr/Log/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "https://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for logging libraries",
+            "homepage": "https://github.com/php-fig/log",
+            "keywords": [
+                "log",
+                "psr",
+                "psr-3"
+            ],
+            "support": {
+                "source": "https://github.com/php-fig/log/tree/1.1.4"
+            },
+            "time": "2021-05-03T11:20:27+00:00"
+        },
+        {
+            "name": "symfony/cache",
+            "version": "v5.4.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/cache.git",
+                "reference": "4c6747cf7e56c6b8e3094dd24852bd3e364375b1"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/cache/zipball/4c6747cf7e56c6b8e3094dd24852bd3e364375b1",
+                "reference": "4c6747cf7e56c6b8e3094dd24852bd3e364375b1",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "psr/cache": "^1.0|^2.0",
+                "psr/log": "^1.1|^2|^3",
+                "symfony/cache-contracts": "^1.1.7|^2",
+                "symfony/deprecation-contracts": "^2.1|^3",
+                "symfony/polyfill-php73": "^1.9",
+                "symfony/polyfill-php80": "^1.16",
+                "symfony/service-contracts": "^1.1|^2|^3",
+                "symfony/var-exporter": "^4.4|^5.0|^6.0"
+            },
+            "conflict": {
+                "doctrine/dbal": "<2.13.1",
+                "symfony/dependency-injection": "<4.4",
+                "symfony/http-kernel": "<4.4",
+                "symfony/var-dumper": "<4.4"
+            },
+            "provide": {
+                "psr/cache-implementation": "1.0|2.0",
+                "psr/simple-cache-implementation": "1.0|2.0",
+                "symfony/cache-implementation": "1.0|2.0"
+            },
+            "require-dev": {
+                "cache/integration-tests": "dev-master",
+                "doctrine/cache": "^1.6|^2.0",
+                "doctrine/dbal": "^2.13.1|^3.0",
+                "predis/predis": "^1.1",
+                "psr/simple-cache": "^1.0|^2.0",
+                "symfony/config": "^4.4|^5.0|^6.0",
+                "symfony/dependency-injection": "^4.4|^5.0|^6.0",
+                "symfony/filesystem": "^4.4|^5.0|^6.0",
+                "symfony/http-kernel": "^4.4|^5.0|^6.0",
+                "symfony/messenger": "^4.4|^5.0|^6.0",
+                "symfony/var-dumper": "^4.4|^5.0|^6.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Cache\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Provides an extended PSR-6, PSR-16 (and tags) implementation",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "caching",
+                "psr6"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/cache/tree/v5.4.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-26T13:19:20+00:00"
+        },
+        {
+            "name": "symfony/cache-contracts",
+            "version": "v2.5.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/cache-contracts.git",
+                "reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/64be4a7acb83b6f2bf6de9a02cee6dad41277ebc",
+                "reference": "64be4a7acb83b6f2bf6de9a02cee6dad41277ebc",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "psr/cache": "^1.0|^2.0|^3.0"
+            },
+            "suggest": {
+                "symfony/cache-implementation": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.5-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\Cache\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to caching",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/cache-contracts/tree/v2.5.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-01-02T09:53:40+00:00"
+        },
+        {
+            "name": "symfony/deprecation-contracts",
+            "version": "v2.5.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/deprecation-contracts.git",
+                "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
+                "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.5-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "function.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "A generic function and convention to trigger deprecation notices",
+            "homepage": "https://symfony.com",
+            "support": {
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-01-02T09:53:40+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php73",
+            "version": "v1.25.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php73.git",
+                "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5",
+                "reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.23-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php73\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php73/tree/v1.25.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2021-06-05T21:20:04+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.25.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c",
+                "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.23-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "bootstrap.php"
+                ],
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "ion.bazan@gmail.com"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-03-04T08:16:47+00:00"
+        },
+        {
+            "name": "symfony/service-contracts",
+            "version": "v2.5.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/service-contracts.git",
+                "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
+                "reference": "24d9dc654b83e91aa59f9d167b131bc3b5bea24c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "psr/container": "^1.1",
+                "symfony/deprecation-contracts": "^2.1|^3"
+            },
+            "conflict": {
+                "ext-psr": "<1.1|>=2"
+            },
+            "suggest": {
+                "symfony/service-implementation": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "2.5-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\Service\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to writing services",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/service-contracts/tree/v2.5.1"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-03-13T20:07:29+00:00"
+        },
+        {
+            "name": "symfony/var-exporter",
+            "version": "v5.4.8",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/var-exporter.git",
+                "reference": "7e132a3fcd4b57add721b4207236877b6017ec93"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/var-exporter/zipball/7e132a3fcd4b57add721b4207236877b6017ec93",
+                "reference": "7e132a3fcd4b57add721b4207236877b6017ec93",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.2.5",
+                "symfony/polyfill-php80": "^1.16"
+            },
+            "require-dev": {
+                "symfony/var-dumper": "^4.4.9|^5.0.9|^6.0"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\VarExporter\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Allows exporting any serializable PHP data structure to plain PHP code",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "clone",
+                "construct",
+                "export",
+                "hydrate",
+                "instantiate",
+                "serialize"
+            ],
+            "support": {
+                "source": "https://github.com/symfony/var-exporter/tree/v5.4.8"
+            },
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2022-04-26T13:19:20+00:00"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": [],
+    "plugin-api-version": "2.3.0"
+}

+ 68 - 0
modules/boonex/azure_b2c_con/install/config.php

@@ -0,0 +1,68 @@
+<?php 
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    AzureB2CConnect Azure B2C Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+$aConfig = array(
+    /**
+     * Main Section.
+     */
+    'type' => BX_DOL_MODULE_TYPE_MODULE,
+    'name' => 'bx_azrb2c',
+    'title' => 'Azure B2C Connect',
+    'note' => 'Join the site using Azure B2C account.',
+    'version' => '13.0.0.DEV',
+    'vendor' => 'UNA Inc',
+    'help_url' => 'http://feed.una.io/?section={module_name}',
+
+    'compatible_with' => array(
+        '13.0.x'
+    ),
+
+    /**
+     * 'home_dir' and 'home_uri' - should be unique. Don't use spaces in 'home_uri' and the other special chars.
+     */
+    'home_dir' => 'boonex/azure_b2c_con/',
+    'home_uri' => 'azrb2c',
+
+    'db_prefix' => 'bx_azrb2c_',
+    'class_prefix' => 'BxAzrB2C',
+
+    /**
+     * Category for language keys.
+     */
+    'language_category' => 'Azure B2C Connect',
+
+    /**
+     * Installation/Uninstallation Section.
+     */
+    'install' => array(
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    ),
+    'uninstall' => array (
+        'execute_sql' => 1,
+        'update_languages' => 1,
+    ),
+    'enable' => array(
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+    'disable' => array (
+        'execute_sql' => 1,
+        'clear_db_cache' => 1,
+    ),
+
+    /**
+     * Dependencies Section
+     */
+    'dependencies' => array(),
+);
+
+/** @} */

+ 20 - 0
modules/boonex/azure_b2c_con/install/installer.php

@@ -0,0 +1,20 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    AzureB2CConnect Azure B2C Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxAzrB2CInstaller extends BxDolStudioInstaller
+{
+    function __construct($aConfig)
+    {
+        parent::__construct($aConfig);
+    }
+}
+
+/** @} */

+ 36 - 0
modules/boonex/azure_b2c_con/install/langs/en.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources name="en" flag="gb" title="English">
+	<string name="_sys_module_azrb2c"><![CDATA[Azure AD B2C Connect]]></string>
+	<string name="_bx_azrb2c"><![CDATA[Azure AD B2C Connect]]></string>
+	<string name="_bx_azrb2c_settings"><![CDATA[Azure AD B2C Connect]]></string>
+	<string name="_bx_azrb2c_information_block"><![CDATA[
+        <p>This module can be used to login to this site using Azure AD B2C accounts.</p>
+        <p>To make it work make new registration in <a target="_blank" href="https://portal.azure.com/">Azure portal</a> then goto Azure AD B2C service. <br />
+        When asked specify the following Redirect URI:<br />
+            <ul>
+                <li>Redirect URL: <strong>{0}</strong></li>
+            </ul>
+            When registration is complete go to "Overview"  to copy Application (client) ID and Directory (tenant) ID. <br /> 
+            Client secret can be generated in "Certificates & secrets". <br />
+            For more info please refer to <a target="_blank" href="https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app">official documentation</a>. <br />
+            Domain and Tenant can be found in Application Overview > Endpoints, Tenant is first part of URI in any B2C endpoints URLs. 
+        
+        </p>]]></string>
+	<string name="_bx_azrb2c_profile_error_api_keys"><![CDATA[Please configure the module in admin panel (can't define Client ID & Secret)]]></string>
+	<string name="_bx_azrb2c_auth_title"><![CDATA[Login with Azure AD]]></string>
+
+    <string name="_bx_azrb2c_adm_stg_cpt_type"><![CDATA[Azure AD Connect]]></string>
+    <string name="_bx_azrb2c_option_domain"><![CDATA[Domain]]></string>
+    <string name="_bx_azrb2c_option_tenant"><![CDATA[Tenant Name]]></string>
+    <string name="_bx_azrb2c_option_policy"><![CDATA[Policy (User Flow)]]></string>
+    <string name="_bx_azrb2c_option_tenant_id"><![CDATA[Directory (Tenant) ID]]></string>
+    <string name="_bx_azrb2c_option_client_id"><![CDATA[Application (Client) ID]]></string>
+    <string name="_bx_azrb2c_option_secret"><![CDATA[Client Secret Value]]></string>
+
+	<string name="_bx_azrb2c_et_password_generated"><![CDATA[Azure AD connect temporary password]]></string>
+	<string name="_bx_azrb2c_et_password_generated_subject"><![CDATA[Temporary password]]></string>
+	<string name="_bx_azrb2c_et_password_generated_body"><![CDATA[{email_header}
+<p>Temporary password - <b>{password}</b></p>
+<p>Change it after logging-in.</p>
+{email_footer}]]></string>
+</resources>

+ 25 - 0
modules/boonex/azure_b2c_con/install/sql/disable.sql

@@ -0,0 +1,25 @@
+
+-- Email template
+
+DELETE FROM `sys_email_templates` WHERE `Name` = 'bx_azrb2c_password_generated';
+
+-- Auth objects
+
+DELETE FROM `sys_objects_auths` WHERE `Name` = 'bx_azrb2c';
+
+-- Alerts
+
+SET @iHandlerId := (SELECT `id` FROM `sys_alerts_handlers`  WHERE `name`  =  'bx_azrb2c');
+
+DELETE FROM `sys_alerts_handlers` WHERE `id` = @iHandlerId;
+DELETE FROM `sys_alerts` WHERE `handler_id` =  @iHandlerId;
+
+-- Options
+
+DELETE FROM `top`, `toc`, `to` USING `sys_options_types` AS `top` LEFT JOIN `sys_options_categories` AS `toc` ON `top`.`id`=`toc`.`type_id` LEFT JOIN `sys_options` AS `to` ON `toc`.`id`=`to`.`category_id` WHERE `top`.`name` = 'bx_azrb2c';
+
+-- Pages
+
+DELETE FROM `sys_objects_page` WHERE `module` = 'bx_azrb2c';
+DELETE FROM `sys_pages_blocks` WHERE `module` = 'bx_azrb2c';
+

+ 53 - 0
modules/boonex/azure_b2c_con/install/sql/enable.sql

@@ -0,0 +1,53 @@
+
+-- Email template
+
+INSERT INTO `sys_email_templates` (`Module`, `NameSystem`, `Name`, `Subject`, `Body`) VALUES
+('bx_azrb2c', '_bx_azrb2c_et_password_generated', 'bx_azrb2c_password_generated', '_bx_azrb2c_et_password_generated_subject', '_bx_azrb2c_et_password_generated_body');
+
+-- Auth objects
+
+INSERT INTO `sys_objects_auths` (`Name`, `Title`, `Link`, `Icon`, `Style`) VALUES
+('bx_azrb2c', '_bx_azrb2c_auth_title', 'modules/?r=azrb2c/start', 'fab microsoft', 'a:1:{s:7:".bx-btn";a:1:{s:10:"background";s:18:"#0078d4 !important";}}');
+
+-- Alerts
+
+INSERT INTO `sys_alerts_handlers` SET `name` = 'bx_azrb2c', `class` = 'BxAzrB2CAlerts', `file` = 'modules/boonex/azure_b2c_con/classes/BxAzrB2CAlerts.php';
+
+SET @iHandlerId := (SELECT `id` FROM `sys_alerts_handlers`  WHERE `name` = 'bx_azrb2c');
+
+INSERT INTO `sys_alerts` (`unit`, `action`, `handler_id`) VALUES
+('account', 'logout', @iHandlerId),
+('profile', 'delete', @iHandlerId),
+('profile', 'add', @iHandlerId);
+
+-- Options
+
+SET @iTypeOrder = (SELECT MAX(`order`) FROM `sys_options_types` WHERE `group` = 'modules');
+INSERT INTO `sys_options_types` (`group`, `name`, `caption`, `icon`, `order`) VALUES 
+('modules', 'bx_azrb2c', '_bx_azrb2c_adm_stg_cpt_type', 'bx_azrb2c@modules/boonex/azure_b2c_con/|std-icon.svg', IF(NOT ISNULL(@iTypeOrder), @iTypeOrder + 1, 1));
+SET @iTypeId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options_categories` (`type_id`, `name`, `caption`, `order` )  
+VALUES (@iTypeId, 'bx_azrb2c_general', '_sys_connect_adm_stg_cpt_category_general', 1);
+SET @iCategId = LAST_INSERT_ID();
+
+INSERT INTO `sys_options` (`name`, `value`, `category_id`, `caption`, `type`, `check`, `check_error`, `order`, `extra`) VALUES
+('bx_azrb2c_domain', '', @iCategId, '_bx_azrb2c_option_domain', 'digit', '', '', 10, ''),
+('bx_azrb2c_tenant', '', @iCategId, '_bx_azrb2c_option_tenant', 'digit', '', '', 12, ''),
+('bx_azrb2c_policy', '', @iCategId, '_bx_azrb2c_option_policy', 'digit', '', '', 14, ''),
+('bx_azrb2c_tenant_id', '', @iCategId, '_bx_azrb2c_option_tenant_id', 'digit', '', '', 20, ''),
+('bx_azrb2c_client_id', '', @iCategId, '_bx_azrb2c_option_client_id', 'digit', '', '', 22, ''),
+('bx_azrb2c_secret', '', @iCategId, '_bx_azrb2c_option_secret', 'digit', '', '', 24, ''),
+('bx_azrb2c_redirect_page', 'index', @iCategId, '_sys_connect_option_redirect', 'select', '', '', 40, 'join,settings,dashboard,index'),
+('bx_azrb2c_module', 'bx_persons', @iCategId, '_sys_connect_option_module', 'select', '', '', 50, 'a:2:{s:6:"module";s:9:"bx_azrb2c";s:6:"method";s:20:"get_profiles_modules";}'),
+('bx_azrb2c_privacy', '3', @iCategId, '_sys_connect_option_privacy', 'select', '', '', 54, 'a:2:{s:6:"module";s:12:"bx_azrb2c";s:6:"method";s:18:"get_privacy_groups";}'),
+('bx_azrb2c_confirm_email', 'on', @iCategId, '_sys_connect_option_confirm_email', 'checkbox', '', '', 70, ''),
+('bx_azrb2c_approve', '', @iCategId, '_sys_connect_option_approve', 'checkbox', '', '', 80, '');
+
+-- Pages
+
+INSERT INTO `sys_objects_page`(`object`, `uri`, `title_system`, `title`, `module`, `layout_id`, `visible_for_levels`, `visible_for_levels_editable`, `url`, `meta_description`, `meta_keywords`, `meta_robots`, `cache_lifetime`, `cache_editable`, `deletable`, `override_class_name`, `override_class_file`) VALUES 
+('bx_azrb2c_error', 'azrb2c-error', '_bx_azrb2c_error', '_bx_azrb2c_error', 'bx_azrb2c', 5, 2147483647, 0, '', '', '', '', 0, 1, 0, 'BxAzrB2CPage', 'modules/boonex/azure_b2c_con/classes/BxAzrB2CPage.php');
+
+INSERT INTO `sys_pages_blocks`(`object`, `cell_id`, `module`, `title`, `designbox_id`, `visible_for_levels`, `type`, `content`, `deletable`, `copyable`, `active`, `order`) VALUES 
+('bx_azrb2c_error', 1, 'bx_azrb2c', '_bx_azrb2c_error', 11, 2147483647, 'service', 'a:2:{s:6:\"module\";s:9:\"bx_azrb2c\";s:6:\"method\";s:10:\"last_error\";}', 0, 0, 1, 1);

+ 21 - 0
modules/boonex/azure_b2c_con/install/sql/install.sql

@@ -0,0 +1,21 @@
+
+CREATE TABLE `bx_azrb2c_accounts` (
+  `local_profile` int(10) unsigned NOT NULL,
+  `remote_profile` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
+  PRIMARY KEY (`local_profile`),
+  KEY `remote_profile` (`remote_profile`)
+);
+
+-- Studio page and widget
+
+INSERT INTO `sys_std_pages`(`index`, `name`, `header`, `caption`, `icon`) VALUES
+(3, 'bx_azrb2c', '_bx_azrb2c', '_bx_azrb2c', 'bx_azrb2c@modules/boonex/azure_b2c_con/|std-icon.svg');
+SET @iPageId = LAST_INSERT_ID();
+
+SET @iParentPageId = (SELECT `id` FROM `sys_std_pages` WHERE `name` = 'home');
+SET @iParentPageOrder = (SELECT MAX(`order`) FROM `sys_std_pages_widgets` WHERE `page_id` = @iParentPageId);
+INSERT INTO `sys_std_widgets` (`page_id`, `module`, `type`, `url`, `click`, `icon`, `caption`, `cnt_notices`, `cnt_actions`) VALUES
+(@iPageId, 'bx_azrb2c', 'integrations', '{url_studio}module.php?name=bx_azrb2c', '', 'bx_azrb2c@modules/boonex/azure_b2c_con/|std-icon.svg', '_bx_azrb2c', '', 'a:4:{s:6:"module";s:6:"system";s:6:"method";s:11:"get_actions";s:6:"params";a:0:{}s:5:"class";s:18:"TemplStudioModules";}');
+INSERT INTO `sys_std_pages_widgets` (`page_id`, `widget_id`, `order`) VALUES
+(@iParentPageId, LAST_INSERT_ID(), IF(ISNULL(@iParentPageOrder), 1, @iParentPageOrder + 1));
+

+ 8 - 0
modules/boonex/azure_b2c_con/install/sql/uninstall.sql

@@ -0,0 +1,8 @@
+
+DROP TABLE IF EXISTS `bx_azrb2c_accounts`;
+
+
+-- Studio page and widget
+DELETE FROM `tp`, `tw`, `twb`, `tpw` 
+USING `sys_std_pages` AS `tp` LEFT JOIN `sys_std_widgets` AS `tw` ON `tp`.`id` = `tw`.`page_id` LEFT JOIN `sys_std_widgets_bookmarks` AS `twb` ON `tw`.`id` = `twb`.`widget_id` LEFT JOIN `sys_std_pages_widgets` AS `tpw` ON `tw`.`id` = `tpw`.`widget_id`
+WHERE  `tp`.`name` = 'bx_azrb2c';

+ 18 - 0
modules/boonex/azure_b2c_con/request.php

@@ -0,0 +1,18 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    AzureB2CConnect Azure B2C Connect
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+require_once(BX_DIRECTORY_PATH_INC . 'profiles.inc.php');
+
+check_logged();
+
+BxDolRequest::processAsAction($GLOBALS['aModule'], $GLOBALS['aRequest']);
+
+/** @} */

+ 57 - 0
modules/boonex/azure_b2c_con/template/images/icons/std-icon.svg

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="606px" height="606px" viewBox="0 0 606 606" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 40.2 (33826) - http://www.bohemiancoding.com/sketch -->
+    <title>Azure</title>
+    <desc>Created with Sketch.</desc>
+    <defs>
+        <linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="linearGradient-1">
+            <stop stop-color="#FFFFFF" stop-opacity="0.2" offset="0%"></stop>
+            <stop stop-color="#000000" stop-opacity="0.2" offset="100%"></stop>
+        </linearGradient>
+        <path d="M552.268958,552.268958 C600,504.537813 600,431.458333 600,300 C600,168.541667 600,95.4621875 552.268958,47.7310417 C504.537813,0 431.458333,0 300,0 C168.541667,0 95.4621875,0 47.7310417,47.7310417 C0,95.4621875 0,168.541667 0,300 C0,431.458333 0,504.537813 47.7310417,552.268958 C95.4621875,600 168.541667,600 300,600 C431.458333,600 504.537813,600 552.268958,552.268958 Z" id="path-2"></path>
+        <path d="M385.455827,163.35 L362.781955,163.35 L288.411654,163.35 L199.530075,163.35 L199.530075,240.4875 L306.550752,240.4875 C296.574248,289.4925 254.854323,317.625 199.530075,317.625 C134.229323,317.625 81.6259398,264.99 81.6259398,199.65 C81.6259398,134.31 134.229323,81.675 199.530075,81.675 C227.645677,81.675 253.040414,91.6575 272.993421,107.9925 L331.038534,49.9125 C295.667293,19.0575 250.319549,0 199.530075,0 C88.8815789,0 0,88.935 0,199.65 C0,310.365 88.8815789,399.3 199.530075,399.3 C299.295113,399.3 389.990602,326.7 389.990602,199.65 C389.990602,187.8525 388.176692,175.1475 385.455827,163.35 L385.455827,163.35 Z" id="path-3"></path>
+        <path d="M385.455827,163.35 L362.781955,163.35 L288.411654,163.35 L199.530075,163.35 L199.530075,240.4875 L306.550752,240.4875 C296.574248,289.4925 254.854323,317.625 199.530075,317.625 C134.229323,317.625 81.6259398,264.99 81.6259398,199.65 C81.6259398,134.31 134.229323,81.675 199.530075,81.675 C227.645677,81.675 253.040414,91.6575 272.993421,107.9925 L331.038534,49.9125 C295.667293,19.0575 250.319549,0 199.530075,0 C88.8815789,0 0,88.935 0,199.65 C0,310.365 88.8815789,399.3 199.530075,399.3 C299.295113,399.3 389.990602,326.7 389.990602,199.65 C389.990602,187.8525 388.176692,175.1475 385.455827,163.35 L385.455827,163.35 Z" id="path-5"></path>
+        <path d="M385.455827,163.35 L362.781955,163.35 L288.411654,163.35 L199.530075,163.35 L199.530075,240.4875 L306.550752,240.4875 C296.574248,289.4925 254.854323,317.625 199.530075,317.625 C134.229323,317.625 81.6259398,264.99 81.6259398,199.65 C81.6259398,134.31 134.229323,81.675 199.530075,81.675 C227.645677,81.675 253.040414,91.6575 272.993421,107.9925 L331.038534,49.9125 C295.667293,19.0575 250.319549,0 199.530075,0 C88.8815789,0 0,88.935 0,199.65 C0,310.365 88.8815789,399.3 199.530075,399.3 C299.295113,399.3 389.990602,326.7 389.990602,199.65 C389.990602,187.8525 388.176692,175.1475 385.455827,163.35 L385.455827,163.35 Z" id="path-7"></path>
+        <path d="M385.455827,163.35 L362.781955,163.35 L288.411654,163.35 L199.530075,163.35 L199.530075,240.4875 L306.550752,240.4875 C296.574248,289.4925 254.854323,317.625 199.530075,317.625 C134.229323,317.625 81.6259398,264.99 81.6259398,199.65 C81.6259398,134.31 134.229323,81.675 199.530075,81.675 C227.645677,81.675 253.040414,91.6575 272.993421,107.9925 L331.038534,49.9125 C295.667293,19.0575 250.319549,0 199.530075,0 C88.8815789,0 0,88.935 0,199.65 C0,310.365 88.8815789,399.3 199.530075,399.3 C299.295113,399.3 389.990602,326.7 389.990602,199.65 C389.990602,187.8525 388.176692,175.1475 385.455827,163.35 L385.455827,163.35 Z" id="path-9"></path>
+    </defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="google" transform="translate(3.000000, 3.000000)">
+            <g id="Squircle2-Copy-6">
+                <g id="Shape">
+                    <use fill="#FFFFFF" xlink:href="#path-2"></use>
+                    <use stroke="#000000" stroke-width="4.04" fill="url(#linearGradient-1)" xlink:href="#path-2"></use>
+                </g>
+            </g>
+
+<!-- FIGURE [START] -->
+<g transform="translate(60,50)">
+<g transform="scale(5.0, 5.0)">
+    <defs>
+        <linearGradient id="e399c19f-b68f-429d-b176-18c2117ff73c" x1="-1032.172" x2="-1059.213" y1="145.312" y2="65.426" gradientTransform="matrix(1 0 0 -1 1075 158)" gradientUnits="userSpaceOnUse">
+            <stop offset="0" stop-color="#114a8b"/>
+            <stop offset="1" stop-color="#0669bc"/>
+        </linearGradient>
+        <linearGradient id="ac2a6fc2-ca48-4327-9a3c-d4dcc3256e15" x1="-1023.725" x2="-1029.98" y1="108.083" y2="105.968" gradientTransform="matrix(1 0 0 -1 1075 158)" gradientUnits="userSpaceOnUse">
+            <stop offset="0" stop-opacity=".3"/>
+            <stop offset=".071" stop-opacity=".2"/>
+            <stop offset=".321" stop-opacity=".1"/>
+            <stop offset=".623" stop-opacity=".05"/>
+            <stop offset="1" stop-opacity="0"/>
+        </linearGradient>
+        <linearGradient id="a7fee970-a784-4bb1-af8d-63d18e5f7db9" x1="-1027.165" x2="-997.482" y1="147.642" y2="68.561" gradientTransform="matrix(1 0 0 -1 1075 158)" gradientUnits="userSpaceOnUse">
+            <stop offset="0" stop-color="#3ccbf4"/>
+            <stop offset="1" stop-color="#2892df"/>
+        </linearGradient>
+    </defs>
+    <path fill="url(#e399c19f-b68f-429d-b176-18c2117ff73c)" d="M33.338 6.544h26.038l-27.03 80.087a4.152 4.152 0 0 1-3.933 2.824H8.149a4.145 4.145 0 0 1-3.928-5.47L29.404 9.368a4.152 4.152 0 0 1 3.934-2.825z"/>
+    <path fill="#0078d4" d="M71.175 60.261h-41.29a1.911 1.911 0 0 0-1.305 3.309l26.532 24.764a4.171 4.171 0 0 0 2.846 1.121h23.38z"/>
+    <path fill="url(#ac2a6fc2-ca48-4327-9a3c-d4dcc3256e15)" d="M33.338 6.544a4.118 4.118 0 0 0-3.943 2.879L4.252 83.917a4.14 4.14 0 0 0 3.908 5.538h20.787a4.443 4.443 0 0 0 3.41-2.9l5.014-14.777 17.91 16.705a4.237 4.237 0 0 0 2.666.972H81.24L71.024 60.261l-29.781.007L59.47 6.544z"/>
+    <path fill="url(#a7fee970-a784-4bb1-af8d-63d18e5f7db9)" d="M66.595 9.364a4.145 4.145 0 0 0-3.928-2.82H33.648a4.146 4.146 0 0 1 3.928 2.82l25.184 74.62a4.146 4.146 0 0 1-3.928 5.472h29.02a4.146 4.146 0 0 0 3.927-5.472z"/>
+</g>
+</g>
+
+<!-- FIGURE [ END ] -->
+
+        </g>
+    </g>
+</svg>

+ 3 - 3
modules/boonex/azure_connect/install/config.php

@@ -16,13 +16,13 @@ $aConfig = array(
     'type' => BX_DOL_MODULE_TYPE_MODULE,
     'name' => 'bx_azrcon',
     'title' => 'Azure AD Connect',
-    'note' => 'Join the site using LinkedIn account.',
-    'version' => '12.0.1.DEV',
+    'note' => 'Join the site using Azure AD account.',
+    'version' => '13.0.0.DEV',
     'vendor' => 'Boonex',
     'help_url' => 'http://feed.una.io/?section={module_name}',
 
     'compatible_with' => array(
-        '12.0.x'
+        '13.0.x'
     ),
 
     /**