unloq - Version 1.0.0

Version Notes

Updated the API calls

Download this release

Release Info

Developer Adrian Bunta
Extension unloq
Version 1.0.0
Comparing to
See all releases


Version 1.0.0

app/code/local/Unloq/Login/Block/Account.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Unloq_Login_Block_Account extends Mage_Core_Block_Template
3
+ {
4
+
5
+ public function __construct() {
6
+ require_once(Mage::getBaseDir('lib') . '/Unloq/UnloqApi.php');
7
+ }
8
+ /**
9
+ * Returns the public plugin URL
10
+ * */
11
+ public function getPluginUrl() {
12
+ return UnloqApi::PLUGIN_URL;
13
+ }
14
+
15
+ public function getApiKey() {
16
+ $key = Mage::getStoreConfig('unloq_login/api/key');
17
+ return $key;
18
+ }
19
+ public function getTheme() {
20
+ $theme = Mage::getStoreConfig('unloq_login/api/theme');
21
+ return $theme;
22
+ }
23
+
24
+ public function _toHtml() {
25
+ $active = Mage::getStoreConfig('unloq_login/status/active');
26
+ if($active != '1') {
27
+ return '';
28
+ }
29
+ return parent::_toHtml();
30
+ }
31
+
32
+ }
app/code/local/Unloq/Login/Helper/Data.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ class Unloq_Login_Helper_Data extends Mage_Core_Helper_Abstract
4
+ {
5
+ }
app/code/local/Unloq/Login/Model/Observer.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Unloq_Login_Model_Observer {
4
+
5
+ public function __construct() {
6
+ require_once(Mage::getBaseDir('lib') . '/Unloq/UnloqApi.php');
7
+ }
8
+
9
+ /**
10
+ * Verifies that the API credentials are correct and sets the
11
+ * login/logout webhooks in place.
12
+ */
13
+ public function handle_optionsChanged() {
14
+ $config = Mage::getStoreConfig('unloq_login/api');
15
+ $status = Mage::getStoreConfig('unloq_login/status');
16
+ if($status['active'] != "1") { // we only setup the hook when it is enabled.
17
+ return;
18
+ }
19
+ $api = new UnloqApi($config['key'], $config['secret']);
20
+ $res = $api->updateHooks();
21
+ if($res->error) {
22
+ throw new Mage_Core_Exception($res->message);//("API Setup failed.");
23
+ }
24
+ if(!isset($status['installed']) || $status['installed'] == '0') {
25
+ //$this->installModule();
26
+ }
27
+ }
28
+
29
+ /**
30
+ * This acts as an installer. The first time the admin sets up the plugin script,
31
+ * we will execute our ALTER on the customer table, by adding the unloq_id field.
32
+ */
33
+ private function installModule() {
34
+ $config = Mage::getModel('core/config');
35
+ $res = Mage::getSingleton('core/resource');
36
+ $connection = $res->getConnection('core_write');
37
+ $table = $res->getTableName('customer_entity');
38
+ $i = $connection->addColumn($table, 'unloq_id', array(
39
+ 'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
40
+ 'nullable' => true,
41
+ 'length' => 50,
42
+ 'comment' => 'UNLOQ.io remote id'
43
+ ));
44
+ if($i) {
45
+ $config->saveConfig('unloq_login/status/installed', '1');
46
+ }
47
+ }
48
+ }
app/code/local/Unloq/Login/Model/Options.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Unloq_Login_Model_Options
3
+ {
4
+ /**
5
+ * Provides available options under the UNLOQ Administration settings.
6
+ *
7
+ * @return array
8
+ */
9
+ public function themes()
10
+ {
11
+ return array(
12
+ 'light' => 'Light',
13
+ 'dark' => 'Dark'
14
+ );
15
+ }
16
+
17
+ }
app/code/local/Unloq/Login/controllers/UauthController.php ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(Mage::getBaseDir('lib') . '/Unloq/UnloqApi.php');
3
+
4
+ class Unloq_Login_UauthController extends Mage_Core_Controller_Front_Action
5
+ {
6
+
7
+ /**
8
+ * Implementing the Unloq auth protocol, this route is called when a user is redirected back to us
9
+ * with a valid access token found in the querystring. We then proceed to request the user information
10
+ * from UNLOQ, and, based on the resulting user from the local database, we create it or just log him in.
11
+ * */
12
+ public function loginAction() {
13
+ $request = Mage::app()->getRequest();
14
+ $sess = Mage::getSingleton("core/session", array('name' => 'frontend'));
15
+ $customerSession = Mage::getSingleton('customer/session');
16
+ $token = $request->getParam('token');
17
+ if(!$token) {
18
+ $sess->addError('The authentication request is missing its token.');
19
+ return $this->_redirect('customer/account/login');
20
+ }
21
+ $active = Mage::getStoreConfig('unloq_login/status/active');
22
+ if(!$active) {
23
+ $sess->addNotice('UNLOQ.io authentication is temporary disabled.');
24
+ return $this->_redirect('customer/account/login');
25
+ }
26
+ $config = Mage::getStoreConfig('unloq_login/api');
27
+ $api = new UnloqApi($config['key'], $config['secret']);
28
+ // Proceed to get the user from UNLOQ
29
+ $result = $api->getLoginToken($token);
30
+ if($result->error) {
31
+ $sess->addError('UNLOQ: ' . $result->message);
32
+ return $this->_redirect('customer/account/login');
33
+ }
34
+ $user = $result->data;
35
+ // Sanity check for user id and email
36
+ if(!isset($user['id']) || !isset($user['email'])) {
37
+ $sess->addError('UNLOQ: Failed to perform authentication.');
38
+ return $this->_redirect('customer/account/login');
39
+ }
40
+ $collection = Mage::getModel('customer/customer')->getCollection();
41
+ $collection->addAttributeToSelect('firstname')
42
+ ->addAttributeToSelect('lastname')
43
+ ->addAttributeToSelect('email')
44
+ ->addAttributeToSelect('unloq_id')
45
+ ->addAttributeToFilter('website_id', Mage::app()->getWebsite()->getId())
46
+ ->addAttributeToFilter('store_id', Mage::app()->getStore()->getStoreId())
47
+ ->addAttributeToFilter('email', $user['email']);
48
+ // Step one, we try and find the user locally.
49
+ $customer = $collection->getFirstItem();
50
+ if($customer->isEmpty()) {
51
+ $customer = $this->createCustomer($user);
52
+ if(!$customer) {
53
+ $sess->addError('Failed to create account. Please try again.');
54
+ return $this->_redirect('customer/account/login');
55
+ }
56
+ } else {
57
+ // If we do have a customer, we check if his account is disabled. If so, we stop.
58
+ $isActive = (bool)$customer->getIsActive();
59
+ if(!$isActive) {
60
+ $sess->addNotice("Your account has been disabled.");
61
+ return $this->_redirect('customer/account/login');
62
+ }
63
+ if(!$this->updateCustomer($customer, $user)) {
64
+ $sess->addError('Failed to update data. Please try again.');
65
+ return $this->_redirect('customer/account/login');
66
+ }
67
+ }
68
+ // At this point, we create the session and log the user in.
69
+ if(!$customerSession->loginById($customer->getId())) {
70
+ $sess->addError("Failed to log you in. Please try again.");
71
+ return $this->_redirect('customer/account/login');
72
+ }
73
+ // Finally, we send the session ID for remote logout.
74
+ $duration = (int) Mage::getStoreConfig('web/cookie/cookie_lifetime');
75
+ $sessionId = $customerSession->getSessionId();
76
+ $api->sendTokenSession($token, $sessionId, $duration);
77
+ // Everything is done, we can just redirect back to account
78
+ $redirect = (!$customer->getFirstname() || !$customer->getLastname()) ? "customer/account/edit" : "customer/account";
79
+ $this->_redirect($redirect);
80
+ }
81
+
82
+
83
+ /**
84
+ * When this endpoint is called with valid arguments, coming from UNLOQ or the user's device,
85
+ * it will perform a remote logout.
86
+ * */
87
+ public function logoutAction() {
88
+ $request = Mage::app()->getRequest();
89
+ // Request validation
90
+ if(!$request->isPost()) {
91
+ return $this->stop('Invalid HTTP method');
92
+ }
93
+ $key = $request->getParam('key');
94
+ $unloqId = $request->getParam('id');
95
+ $sessionId = $request->getParam('sid');
96
+ if(!$key || !$unloqId || !$sessionId) {
97
+ return $this->stop('Invalid arguments');
98
+ }
99
+ $active = Mage::getStoreConfig('unloq_login/status/active');
100
+ if(!$active) {
101
+ return $this->stop('UNLOQ Authentication is disabled.', 401);
102
+ }
103
+ // Validate the request's signature and create the API object
104
+ $config = Mage::getStoreConfig('unloq_login/api');
105
+ $api = new UnloqApi($config['key'], $config['secret']);
106
+ if(!$api->verifySignature($api->getHook('logout'), $_POST)) {
107
+ return $this->stop('Invalid signature');
108
+ }
109
+ // If we've reached this point, we need to read the customer from the db, to validate his existance.
110
+ $collection = Mage::getModel('customer/customer')->getCollection();
111
+ $app = Mage::app();
112
+ $collection->addAttributeToFilter('unloq_id', $unloqId)
113
+ ->addAttributeToFilter('website_id', $app->getWebsite()->getId())
114
+ ->addAttributeToFilter('store_id', Mage::app()->getStore()->getStoreId());
115
+ $customer = $collection->getFirstItem();
116
+ if($customer->isEmpty()) {
117
+ return $this->stop('User not found.', 404);
118
+ }
119
+ // Finally, we destroy the session.
120
+ $customerSession = Mage::getSingleton('customer/session');
121
+ $customerSession->setSessionId($sessionId);
122
+ $customerSession->setCustomer($customer);
123
+ $customerSession->logout();
124
+ echo "Logged out.";
125
+ }
126
+
127
+ /**
128
+ * Internal function for stopping the request and sending back some data.
129
+ * This is used when something goes wrong in login/logout or data is not valid.
130
+ * */
131
+ private function stop($msg = "", $code = 500) {
132
+ header(' ', true, $code);
133
+ echo $msg;
134
+ exit;
135
+ }
136
+
137
+ /**
138
+ * Creates a new customer based on the given user information.
139
+ * */
140
+ private function createCustomer($user) {
141
+ $app = Mage::app();
142
+ $websiteId = $app->getWebsite()->getId();
143
+ $store = $app->getStore();
144
+ $customer = Mage::getModel('customer/customer');
145
+ // Attaching information to our new customer
146
+ $customer->setWebsiteId($websiteId)
147
+ ->setStore($store)
148
+ ->setEmail($user['email'])
149
+ ->setConfirmationRequired(false)
150
+ ->setUnloqId($user['id']);
151
+ if(isset($user['first_name'])) {
152
+ $customer->setFirstname($user['first_name']);
153
+ }
154
+ if(isset($user['last_name'])) {
155
+ $customer->setLastname($user['last_name']);
156
+ }
157
+ try {
158
+ $customer->save();
159
+ Mage::dispatchEvent('customer_register_success',
160
+ array('unloq_controller' => $this, 'customer' => $customer)
161
+ );
162
+ return $customer;
163
+ } catch(Exception $e) {
164
+ Zend_debug::dump($e->getMessage());
165
+ return false;
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Updates the given customer with new information about him.
171
+ * The information always contain the unloqId, but might contain additional
172
+ * properties, such as first_name or last_name.
173
+ * */
174
+ private function updateCustomer($customer, $user) {
175
+ if(!$customer->getUnloqId()) {
176
+ $customer->setUnloqId($user['id']);
177
+ }
178
+ if(strlen($customer->getFirstname()) == 0 && isset($user['first_name'])) {
179
+ $customer->setFirstname($user['first_name']);
180
+ }
181
+ if(strlen($customer->getLastname()) == 0 && isset($user['last_name'])) {
182
+ $customer->setLastname($user['last_name']);
183
+ }
184
+ try {
185
+ $customer->save();
186
+ return true;
187
+ } catch (Exception $e) {
188
+ Zend_debug::dump($e->getMessage());
189
+ return false;
190
+ }
191
+ }
192
+
193
+ }
app/code/local/Unloq/Login/etc/config.xml ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <frontend>
4
+ <routers>
5
+ <setup>
6
+ <use>standard</use>
7
+ <args>
8
+ <module>Unloq_Login</module>
9
+ <frontName>unloq</frontName>
10
+ </args>
11
+ </setup>
12
+ </routers>
13
+ <layout>
14
+ <updates>
15
+ <unloqlogin>
16
+ <file>unloq.xml</file>
17
+ </unloqlogin>
18
+ </updates>
19
+ </layout>
20
+ </frontend>
21
+ <modules>
22
+ <Unloq_Login>
23
+ <version>0.1.0</version>
24
+ </Unloq_Login>
25
+ </modules>
26
+ <global>
27
+ <resources>
28
+ <unloqlogin_setup>
29
+ <setup>
30
+ <module>Unloq_Login</module>
31
+ <class>Mage_Customer_Model_Entity_Setup</class>
32
+ </setup>
33
+ <connection>
34
+ <use>core_setup</use>
35
+ </connection>
36
+ </unloqlogin_setup>
37
+ <unloqlogin_write>
38
+ <connection>
39
+ <use>core_write</use>
40
+ </connection>
41
+ </unloqlogin_write>
42
+ <unloqlogin_read>
43
+ <connection>
44
+ <use>core_read</use>
45
+ </connection>
46
+ </unloqlogin_read>
47
+ </resources>
48
+ <helpers>
49
+ <login>
50
+ <class>Unloq_Login_Helper</class>
51
+ </login>
52
+ </helpers>
53
+ <models>
54
+ <login>
55
+ <class>Unloq_Login_Model</class>
56
+ </login>
57
+ </models>
58
+ <events>
59
+ <admin_system_config_changed_section_unloq_login>
60
+ <observers>
61
+ <login>
62
+ <type>singleton</type>
63
+ <class>login/observer</class>
64
+ <method>handle_optionsChanged</method>
65
+ </login>
66
+ </observers>
67
+ </admin_system_config_changed_section_unloq_login>
68
+ </events>
69
+ <blocks>
70
+ <login>
71
+ <class>Unloq_Login_Block</class>
72
+ </login>
73
+ </blocks>
74
+ </global>
75
+ <adminhtml>
76
+ <acl>
77
+ <resources>
78
+ <admin>
79
+ <children>
80
+ <system>
81
+ <children>
82
+ <config>
83
+ <children>
84
+ <unloq_login>
85
+ <title>UNLOQ.io Authentication settings</title>
86
+ </unloq_login>
87
+ </children>
88
+ </config>
89
+ </children>
90
+ </system>
91
+ </children>
92
+ </admin>
93
+ </resources>
94
+ </acl>
95
+ </adminhtml>
96
+ </config>
app/code/local/Unloq/Login/etc/system.xml ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <sections>
4
+ <unloq_login translate="label" module="login">
5
+ <label>UNLOQ login</label>
6
+ <tab>service</tab>
7
+ <frontend_type>text</frontend_type>
8
+ <sort_order>500</sort_order>
9
+ <show_in_default>1</show_in_default>
10
+ <show_in_website>1</show_in_website>
11
+ <show_in_store>1</show_in_store>
12
+ <groups>
13
+ <status translate="label">
14
+ <label>Status</label>
15
+ <comment>
16
+ You can disable or enable the UNLOQ plugin at any time.
17
+ </comment>
18
+ <frontend_type>text</frontend_type>
19
+ <sort_order>1</sort_order>
20
+ <show_in_default>1</show_in_default>
21
+ <show_in_website>1</show_in_website>
22
+ <show_in_store>1</show_in_store>
23
+ <fields>
24
+ <active>
25
+ <label>Enabled</label>
26
+ <frontend_type>radios</frontend_type>
27
+ <sort_order>4</sort_order>
28
+ <source_model>adminhtml/system_config_source_yesno</source_model>
29
+ <show_in_default>1</show_in_default>
30
+ <show_in_website>1</show_in_website>
31
+ <show_in_store>1</show_in_store>
32
+ </active>
33
+ <installed>
34
+
35
+ </installed>
36
+ </fields>
37
+ </status>
38
+ <api translate="label">
39
+ <label>Setup</label>
40
+ <comment>
41
+ <![CDATA[
42
+ <p>
43
+ <h3>Welcome!</h3>
44
+ If you haven't created an UNLOQ account, please do so <a href="https://account.unloq.io" target="_blank">here.</a>.
45
+ </p>
46
+ <p>
47
+ <b>Steps for enabling the UNLOQ plugin on this website:</b><br/>
48
+ 1. Login to UNLOQ<br/>
49
+ 2. Create a Magento Web Application with this domain <br/>
50
+ 3. Configure the application <br />
51
+ 4. Go to the application's Settings > Widgets section and verify your domain <br/>
52
+ 5. Go to the application's Settings > General > API Keys and add a new key <br />
53
+ 6. Enter the API Key and Login Widget Key of your app below <br />
54
+ </p>
55
+ <br/>
56
+ ]]>
57
+ </comment>
58
+ <frontend_type>text</frontend_type>
59
+ <sort_order>2</sort_order>
60
+ <show_in_default>1</show_in_default>
61
+ <show_in_website>1</show_in_website>
62
+ <show_in_store>1</show_in_store>
63
+ <fields>
64
+ <key>
65
+ <label>Login Widget Key</label>
66
+ <frontend_type>text</frontend_type>
67
+ <sort_order>1</sort_order>
68
+ <show_in_default>1</show_in_default>
69
+ <show_in_website>1</show_in_website>
70
+ <show_in_store>1</show_in_store>
71
+ </key>
72
+ <secret>
73
+ <label>API Key</label>
74
+ <frontend_type>text</frontend_type>
75
+ <sort_order>2</sort_order>
76
+ <show_in_default>1</show_in_default>
77
+ <show_in_website>1</show_in_website>
78
+ <show_in_store>1</show_in_store>
79
+ </secret>
80
+ <theme>
81
+ <label>Plugin theme</label>
82
+ <frontend_type>select</frontend_type>
83
+ <source_model>login/options::themes</source_model>
84
+ <sort_order>3</sort_order>
85
+ <show_in_default>1</show_in_default>
86
+ <show_in_website>1</show_in_website>
87
+ <show_in_store>1</show_in_store>
88
+ </theme>
89
+ </fields>
90
+ </api>
91
+ </groups>
92
+ </unloq_login>
93
+ </sections>
94
+ </config>
app/code/local/Unloq/Login/sql/unloqlogin_setup/mysql4-install-0.1.0.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $installer = $this;
3
+
4
+ $installer->startSetup();
5
+
6
+ $setup = new Mage_Eav_Model_Entity_Setup('core_setup');
7
+
8
+ $entityTypeId = $setup->getEntityTypeId('customer');
9
+ $attributeSetId = $setup->getDefaultAttributeSetId($entityTypeId);
10
+ $attributeGroupId = $setup->getDefaultAttributeGroupId($entityTypeId, $attributeSetId);
11
+
12
+ $installer->addAttribute("customer", "unloq_id", array(
13
+ "type" => "varchar",
14
+ "backend" => "",
15
+ "label" => "UNLOQ.io user id",
16
+ "input" => "text",
17
+ "source" => "",
18
+ "visible" => true,
19
+ "required" => false,
20
+ "default" => "",
21
+ "frontend" => "",
22
+ "unique" => false,
23
+ "note" => "UNLOQ.io user id"
24
+ ));
25
+
26
+ $attribute = Mage::getSingleton("eav/config")->getAttribute("customer", "unloq_id");
27
+
28
+ $setup->addAttributeToGroup(
29
+ $entityTypeId,
30
+ $attributeSetId,
31
+ $attributeGroupId,
32
+ 'unloq_id',
33
+ '999'
34
+ );
35
+
36
+ $used_in_forms=array();
37
+
38
+ $used_in_forms[]="adminhtml_customer";
39
+ $attribute->setData("used_in_forms", $used_in_forms)
40
+ ->setData("is_used_for_customer_segment", true)
41
+ ->setData("is_system", 0)
42
+ ->setData("is_user_defined", 1)
43
+ ->setData("is_visible", 1)
44
+ ->setData("sort_order", 100)
45
+ ;
46
+ $attribute->save();
47
+
48
+ $installer->endSetup();
app/design/frontend/base/default/layout/unloq.xml ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <layout version="0.1.0">
3
+ <default>
4
+ <reference name="head">
5
+ <action method="addCss" ifconfig="unloq_login/status/active">
6
+ <stylesheet>css/unloq/login.css</stylesheet>
7
+ </action>
8
+ </reference>
9
+ </default>
10
+ <customer_account_login translate="label">
11
+ <reference name="content">
12
+ <block type="login/account" name="unloq_login" template="unloq/login.phtml" />
13
+ </reference>
14
+ </customer_account_login>
15
+
16
+ <customer_account_create translate="label">
17
+ <reference name="content">
18
+ <block type="login/account" name="unloq_register" template="unloq/register.phtml" />
19
+ </reference>
20
+ </customer_account_create>
21
+ </layout>
app/design/frontend/base/default/template/unloq/login.phtml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="account-login unloq-login">
2
+ <div class="col-1">
3
+ <div class="content">
4
+ <div>
5
+ <h2><?php echo $this->__('Login with UNLOQ'); ?></h2>
6
+ </div>
7
+ <div class="login-box">
8
+ <script type="text/javascript" src="<?php echo $this->getPluginUrl(); ?>" data-unloq-key="<?php echo $this->getApiKey(); ?>" data-unloq-theme="<?php echo $this->getTheme(); ?>"></script>
9
+ </div>
10
+ </div>
11
+ </div>
12
+ </div>
app/design/frontend/base/default/template/unloq/register.phtml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="account-register unloq-login">
2
+ <div class="col-1">
3
+ <div class="content">
4
+ <div>
5
+ <h2><?php echo $this->__('Register with UNLOQ'); ?></h2>
6
+ </div>
7
+ <div class="login-box fieldset">
8
+ <script type="text/javascript" src="<?php echo $this->getPluginUrl(); ?>" data-unloq-key="<?php echo $this->getApiKey(); ?>" data-unloq-theme="<?php echo $this->getTheme(); ?>"></script>
9
+ </div>
10
+ </div>
11
+ </div>
12
+ </div>
app/etc/modules/Unloq_Login.xml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <config>
3
+ <modules>
4
+ <Unloq_Login>
5
+ <active>true</active>
6
+ <codePool>local</codePool>
7
+ </Unloq_Login>
8
+ </modules>
9
+ </config>
lib/Unloq/UnloqApi.php ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ require_once(Mage::getBaseDir('lib') . '/Unloq/UnloqApiResult.php');
3
+
4
+ class UnloqApi
5
+ {
6
+
7
+ const PLUGIN_URL = "https://plugin.unloq.io/login.js";
8
+ const API_URL = 'https://api.unloq.io';
9
+ const API_VERSION = '1';
10
+
11
+ const HOOK_LOGIN = "/unloq/uauth/login";
12
+ const HOOK_LOGOUT = "/unloq/uauth/logout";
13
+
14
+ private $key;
15
+ private $secret;
16
+
17
+ public function __construct($key, $secret) {
18
+ $this->key = $key;
19
+ $this->secret = $secret;
20
+ }
21
+
22
+ /*
23
+ * Returns the logout hook in relation to the board url.
24
+ * */
25
+ public function getHook($type, $includeDomain) {
26
+ $base = Mage::getBaseUrl();
27
+ if($includeDomain) {
28
+ $path = $base;
29
+ } else {
30
+ $tmp = explode("://", $base);
31
+ $tmp = $tmp[1];
32
+ $pathIdx = strpos($tmp, "/", 0);
33
+ $path = substr($tmp, $pathIdx);
34
+ if($path[strlen($path)-1] == "/") {
35
+ $path = substr($path, 0, strlen($path)-1);
36
+ }
37
+ }
38
+ switch($type) {
39
+ case "login":
40
+ return $path . self::HOOK_LOGIN;
41
+ case "logout":
42
+ return $path . self::HOOK_LOGOUT;
43
+ default:
44
+ return $path;
45
+ }
46
+ }
47
+
48
+ /*
49
+ * Helper function, returns the full API path along with the given path
50
+ * */
51
+ private function getPath($path, $withVersion = true) {
52
+ if (!$withVersion) {
53
+ return $this::API_URL . $path;
54
+ }
55
+ $full = $this::API_URL . '/v' . $this::API_VERSION . $path;
56
+ return $full;
57
+ }
58
+
59
+ /*
60
+ * Performs an API request using cURL
61
+ * */
62
+ protected function request($method = "GET", $path, $data = null, $includeVersion = true) {
63
+ $url = $this->getPath($path, $includeVersion);
64
+ $headers = array(
65
+ "X-Api-Key: " .$this->key,
66
+ "X-Api-Secret: " .$this->secret
67
+ );
68
+ if($method == "POST") {
69
+ array_push($headers, "Content-Type: application/x-www-form-urlencoded");
70
+ }
71
+ $curl_options = array(
72
+ CURLOPT_URL => $url,
73
+ CURLOPT_USERAGENT => 'unloq-ipb',
74
+ CURLOPT_HTTPHEADER => $headers,
75
+ CURLOPT_RETURNTRANSFER => true,
76
+ CURLOPT_TIMEOUT => 120,
77
+ CURLOPT_SSL_VERIFYPEER => false,
78
+ CURLOPT_SSL_VERIFYHOST => 1
79
+ );
80
+ $ch = curl_init();
81
+ if($method == "POST") {
82
+ curl_setopt($ch, CURLOPT_POST, TRUE);
83
+ curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
84
+ }
85
+ curl_setopt_array($ch, $curl_options);
86
+ $result = curl_exec($ch);
87
+ curl_close($ch);
88
+ if(!$result) {
89
+ return new UnloqApiResult("Failed to contact UNLOQ servers", "SERVER_ERROR");
90
+ }
91
+ $res = json_decode($result, true);
92
+ $result = new UnloqApiResult();
93
+ if(!is_array($res) || !isset($res['type'])) {
94
+ $result->error();
95
+ return $result;
96
+ }
97
+ if($res['type'] !== 'success') {
98
+ if($res['code'] == 'APPLICATION.NOT_FOUND') {
99
+ $res['message'] = "Invalid API Key or API Secret";
100
+ }
101
+ $result->error($res);
102
+ return $result;
103
+ }
104
+ $result->success($res);
105
+ return $result;
106
+ }
107
+
108
+ /*
109
+ * Verifies that the given assoc array's signature.
110
+ * 1. Create a string with the URL PATH(PATH ONLY), including QS and the first/
111
+ * 2. Sort the data alphabetically,
112
+ * 3. Append each KEY,VALUE to the string
113
+ * 4. HMAC-SHA256 with the app's api secret
114
+ * 5. Base64-encode the signature.
115
+ * */
116
+ public function verifySignature($path, $data, $signature = null) {
117
+ if($signature == null) { // We take it from headers.
118
+ $headers = getallheaders();
119
+ if(!isset($headers['X-Unloq-Signature']) || !isset($headers['X-Requested-With'])) {
120
+ return false;
121
+ }
122
+ $signature = $headers['X-Unloq-Signature'];
123
+ }
124
+ if(!is_string($path) || !is_array($data)) return false;
125
+ if(substr($path, 0, 1) !== "/") { $path = '/' . $path; }
126
+ $sorted = array();
127
+ foreach($data as $key => $value) {
128
+ if($key == "uauth") continue;
129
+ array_push($sorted, $key);
130
+ }
131
+ asort($sorted);
132
+ foreach($sorted as $key) {
133
+ $val = (isset($data[$key]) ? $data[$key] : '');
134
+ if(!is_string($val)) $val = "";
135
+ $path = $path. $key . $val;
136
+ }
137
+ $hash = hash_hmac("sha256", $path, $this->secret, true);
138
+ $finalHash = base64_encode($hash);
139
+ if($finalHash !== $signature) return false;
140
+ return true;
141
+ }
142
+
143
+ /*
144
+ * Verifies the credentials and updates the login/logout hook.
145
+ * */
146
+ public function updateHooks() {
147
+ $data = array(
148
+ 'login' => $this->getHook('login', true),
149
+ 'logout' => $this->getHook('logout', true)
150
+ );
151
+ return $this->request("POST", "/settings/webhooks", $data);
152
+ }
153
+
154
+ /*
155
+ * Tries and retrieves attached information of an UAuth access token.
156
+ * */
157
+ public function getLoginToken($token) {
158
+ if (!is_string($token) || strlen($token) < 129) {
159
+ return new UnloqApiResult("The UAuth access token is not valid", "ACCESS_TOKEN");
160
+ }
161
+ $data = array("token" => $token);
162
+ $res = $this->request("POST", "/token", $data);
163
+ if(!$res->error) {
164
+ // We verify data integrity.
165
+ if (!isset($res->data['id']) || !isset($res->data['email'])) {
166
+ return new UnloqApiResult("The UAuth response does not contain login information.", "API_ERROR");
167
+ }
168
+ }
169
+ return $res;
170
+ }
171
+
172
+ /*
173
+ * Once a user was logged in, it will send the session id to UNLOQ for remote logout.
174
+ * */
175
+ public function sendTokenSession($token, $sid, $duration = null) {
176
+ $data = array(
177
+ 'token' => $token,
178
+ 'sid' => $sid
179
+ );
180
+ if($duration) {
181
+ $data['duration'] = (int) $duration;
182
+ }
183
+ $res = $this->request('POST', '/token/session', $data);
184
+ return $res;
185
+ }
186
+
187
+ }
lib/Unloq/UnloqApiResult.php ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class UnloqApiResult {
3
+ var $error = false;
4
+ var $message = "";
5
+ var $code = null;
6
+ var $data = null;
7
+
8
+ public function __construct($message=null, $code = null) {
9
+ if(isset($message)) {
10
+ $this->message = $message;
11
+ $this->error = true;
12
+ if(isset($code)) {
13
+ $this->code = $code;
14
+ }
15
+ }
16
+ }
17
+
18
+
19
+ function error($code = "SERVER_ERROR", $message = 'An unexpected error occurred. Please try again later.', $data = null) {
20
+ $this->error = true;
21
+ if(is_array($code)) {
22
+ if(isset($code['code'])) {
23
+ $this->code = $code['code'];
24
+ }
25
+ if(isset($code['message'])) {
26
+ $this->message = $code['message'];
27
+ }
28
+ if(isset($code['data'])) {
29
+ $this->data = $code['data'];
30
+ }
31
+ return $this;
32
+ }
33
+ if(is_string($code)) {
34
+ $this->code = $code;
35
+ }
36
+ if(is_string($message)) {
37
+ $this->message = $message;
38
+ }
39
+ $this->data = data;
40
+ }
41
+
42
+ function success($data = null, $message = null) {
43
+ if(is_array($data)) {
44
+ if(isset($data['message'])) {
45
+ $this->message = $data['message'];
46
+ }
47
+ if(isset($data['data'])) {
48
+ $this->data = $data['data'];
49
+ }
50
+ return $this;
51
+ }
52
+ $this->error = false;
53
+ if(is_string($message)) {
54
+ $this->message = $message;
55
+ }
56
+ $this->data = $data;
57
+ }
58
+ }
59
+ ?>
package.xml ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <package>
3
+ <name>UNLOQ</name>
4
+ <version>1.0.0</version>
5
+ <stability>stable</stability>
6
+ <channel>community</channel>
7
+ <license uri="http://opensource.org/licenses/OSL-3.0.php">OSL</license>
8
+ <extends/>
9
+ <summary>UNLOQ.io Passwordless authentication</summary>
10
+ <description>UNLOQ.io passwordless authentication handled by your phone. For a free account, go to
11
+ https://account.unloq.io
12
+ </description>
13
+ <notes>Updated the API calls</notes>
14
+ <authors>
15
+ <author>
16
+ <name>Adrian Bunta</name>
17
+ <user>unloqer</user>
18
+ <email>adrian@unloq.io</email>
19
+ </author>
20
+ </authors>
21
+ <date>2016-12-29</date>
22
+ <time>07:02:46</time>
23
+ <contents>
24
+ <target name="magelocal">
25
+ <dir name="Unloq">
26
+ <dir name="Login">
27
+ <dir name="Block">
28
+ <file name="Account.php" hash="abd30defa1c262a26fc8246edbcdc095"/>
29
+ </dir>
30
+ <dir name="Helper">
31
+ <file name="Data.php" hash="7597a4b4305b8c05bf3e95919f0b507f"/>
32
+ </dir>
33
+ <dir name="Model">
34
+ <file name="Observer.php" hash="731f13a716115df8213c7aa8527076c2"/>
35
+ <file name="Options.php" hash="3be8f556165652db4225598109872201"/>
36
+ </dir>
37
+ <dir name="controllers">
38
+ <file name="UauthController.php" hash="c3376e40b221ce0373dfe8687d27e703"/>
39
+ </dir>
40
+ <dir name="etc">
41
+ <file name="config.xml" hash="6be16efd64314bed8e51a6231ce023dd"/>
42
+ <file name="system.xml" hash="72229386a8541e4b6d1b44018b15b2e7"/>
43
+ </dir>
44
+ <dir name="sql">
45
+ <dir name="unloqlogin_setup">
46
+ <file name="mysql4-install-0.1.0.php" hash="2802bd1c776714b9223378be207cd007"/>
47
+ </dir>
48
+ </dir>
49
+ </dir>
50
+ </dir>
51
+ </target>
52
+ <target name="mageetc">
53
+ <dir name="modules">
54
+ <file name="Unloq_Login.xml" hash="f9dc907865df3156d2e3c8eaa7933df8"/>
55
+ </dir>
56
+ </target>
57
+ <target name="magedesign">
58
+ <dir name="frontend">
59
+ <dir name="base">
60
+ <dir name="default">
61
+ <dir name="template">
62
+ <dir name="unloq">
63
+ <file name="login.phtml" hash="ca5073969df01a554d597eb36d35ed94"/>
64
+ <file name="register.phtml" hash="a517c995b82118899c08e043d657506d"/>
65
+ </dir>
66
+ </dir>
67
+ <dir name="layout">
68
+ <file name="unloq.xml" hash="296d31d87a867a0503bf9d2072c63630"/>
69
+ </dir>
70
+ </dir>
71
+ </dir>
72
+ </dir>
73
+ </target>
74
+ <target name="magelib">
75
+ <dir name="Unloq">
76
+ <file name="UnloqApi.php" hash="d663106ac630551101a3e674d039bbf8"/>
77
+ <file name="UnloqApiResult.php" hash="bf67c1d7ca0cc99abab0946e85867b8f"/>
78
+ </dir>
79
+ </target>
80
+ <target name="mageskin">
81
+ <dir name="frontend">
82
+ <dir name="base">
83
+ <dir name="default">
84
+ <dir name="css">
85
+ <dir name="unloq">
86
+ <file name="login.css" hash="9f408a2202a977cc42693c4e56301507"/>
87
+ </dir>
88
+ </dir>
89
+ </dir>
90
+ </dir>
91
+ </dir>
92
+ </target>
93
+ </contents>
94
+ <compatible/>
95
+ <dependencies>
96
+ <required>
97
+ <php>
98
+ <min>5.2.0</min>
99
+ <max>6.0.0</max>
100
+ </php>
101
+ <extension>
102
+ <name>curl</name>
103
+ <min></min>
104
+ <max></max>
105
+ </extension>
106
+ </required>
107
+ </dependencies>
108
+ </package>
skin/frontend/base/default/css/unloq/login.css ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
1
+ .unloq-login.account-register {
2
+ margin-top: 10px;
3
+ }
4
+ .unloq-login .login-box {
5
+ text-align: center;
6
+ align-self: center;
7
+ }