This plugin is meant to be used with Affinidi Login and uses PKCE extension of OAuth 2.0 standard.
-
- NOTE: If you want to add a
- custom link anywhere in your theme, simply link to
- or use the shortcode [affinidi_login]
- if the user is not logged in.
-
-
-
Step 1: Setup
-
- Create a Login Configuration
-
-
Login to Affinidi Portal and go to the Affinidi Login service.
-
-
Create a Login Configuration and set the following fields:
-
- Redirect URIs:
-
-
- Auth method:None
-
-
Copy the Client ID and Issuer URL and paste it in Step 2 below.
-
-
Modify the Presentation Definition and ID Token Mapping using this template.
-
If you have activated a supported E-Commerce plugin on this WordPress site, use the template for E-Commerce.
-
-
-
-
Step 2: Configure
-
-
-
-
-
There's no active supported e-commerce plugin configured on this WordPress site. E-Commerce Settings is disabled. To learn more about the supported e-commerce plugins, click here.
Select whether to sync the user profile from Vault whenever the user logs in to their WooCommerce account or only sync their profile on sign-up. Sign-up will populate the customer's billing and shipping address info.
-
Remember to modify the Presentation Definition and ID Token Mapping using the E-Commerce template to request the user's profile from Affinidi Vault.
-
-
+ public function __set( $key, $value ) {
+ $this->option_values[ $key ] = $value;
+ }
-
-
Display Affinidi Login button
-
-
-
If you choose "Use shortcode to display the button", use the shortcode [affinidi_login] and manually edit your desired page to display the button.
-
-
+ public function __isset( $key ) {
+ return isset( $this->option_values[ $key ] );
+ }
-
-
Affinidi Login button header (Login Form)
-
- "/>
-
Displays at the top of the Affinidi Login button in the Login Form of WooCommerce.
-
-
+ public function get_options() {
+ return $this->option_values;
+ }
-
-
Affinidi Login button header (Registration Form)
-
- "/>
-
Displays at the top of the Affinidi Login button in the Registration Form of WooCommerce.
-
-
-
-
-
-
-
-
-
-
-
-
-
- option_values );
+ }
}
-
-WP_Affinidi_Login_Admin::init();
diff --git a/includes/wp-affinidi-login-admin-settings.php b/includes/wp-affinidi-login-admin-settings.php
new file mode 100644
index 0000000..6685d49
--- /dev/null
+++ b/includes/wp-affinidi-login-admin-settings.php
@@ -0,0 +1,267 @@
+admin_options = $options;
+ $this->option_name = $this->admin_options->get_option_name();
+ }
+
+ public static function init(Affinidi_Login_Admin_Options $options)
+ {
+ $admin_settings = new self($options);
+ // add_action adds a callback function to an action hook.
+ // admin_init fires as an admin screen or script is being initialized.
+ add_action('admin_init', [$admin_settings, 'admin_init']);
+ // admin_menu fires before the administration menu loads in the admin.
+ // This action is used to add extra submenus and menu options to the admin panel’s menu structure. It runs after the basic admin panel menu structure is in place.
+ add_action('admin_menu', [$admin_settings, 'add_page']);
+ }
+
+ public function get_admin_settings()
+ {
+ return $this->admin_settings_fields;
+ }
+
+ /**
+ * [admin_init description]
+ *
+ * @return [type] [description]
+ */
+ public function admin_init()
+ {
+ // A callback function that sanitizes the option's value
+ register_setting('affinidi_options', $this->option_name, [$this, 'validate']);
+ }
+
+ /**
+ * Add affinidi submenu page to the settings main menu
+ */
+ public function add_page()
+ {
+ add_options_page('Affinidi Login', 'Affinidi Login', 'manage_options', 'affinidi_settings', [$this, 'options_do_page']);
+ }
+
+ /**
+ * [options_do_page description]
+ *
+ * @return [type] [description]
+ */
+ public function options_do_page()
+ {
+ ?>
+
This plugin is meant to be used with Affinidi Login and uses PKCE extension of OAuth 2.0 standard.
+
+ NOTE: If you want to add a
+ custom link anywhere in your theme, simply link to
+ or use the shortcode [affinidi_login]
+ if the user is not logged in.
+
+
+
Step 1: Setup
+
+ Create a Login Configuration
+
+
Login to Affinidi Portal and go to the Affinidi Login service.
+
+
Create a Login Configuration and set the following fields:
+
+ Redirect URIs:
+
+
+ Auth method:None
+
+
Copy the Client ID and Issuer URL and paste it in Step 2 below.
+
+
Modify the Presentation Definition and ID Token Mapping using this template.
+
If you have activated a supported E-Commerce plugin on this WordPress site, use the template for E-Commerce.', 'affinid-login'); ?>
+
+
+
+
Step 2: Configure
+
+
+
+
+
WooCommerce Settings
+
+
+
+
Sync customer profile from Vault
+
+
+
Select whether to sync the user profile from Vault whenever the user logs in to their WooCommerce account or only sync their profile on sign-up. Sign-up will populate the customer billing and shipping address info.
+
Remember to modify the Presentation Definition and ID Token Mapping using the E-Commerce template to request the user profile from Affinidi Vault.
+
+
+
+
+
Display Affinidi Login button
+
+
+
If you choose "Use shortcode to display the button", use the shortcode [affinidi_login] and manually edit your desired page to display the button.
Displays at the top of the Affinidi Login button in the Registration Form of WooCommerce.
+
+
+
+
+
+
There's no active supported e-commerce plugin configured on this WordPress site. E-Commerce Settings is disabled. To learn more about the supported e-commerce plugins, click here.
+
+
+
+
+
+
+
+
+
+ get_admin_settings();
+ $options = array();
+
+ foreach ( $admin_settings as $field ) {
+ if ( isset( $input[ $field ] ) ) {
+ $options[ $field ] = sanitize_text_field( trim( $input[ $field ] ) );
+ } else {
+ $options[ $field ] = '';
+ }
+ }
+
+ return $options;
+ }
+}
+
+$admin_options = new Affinidi_Login_Admin_Options();
+
+Affinidi_Login_Admin_Settings::init($admin_options);
diff --git a/includes/wp-affinidi-login-callback.php b/includes/wp-affinidi-login-callback.php
index 122cb99..3912e57 100644
--- a/includes/wp-affinidi-login-callback.php
+++ b/includes/wp-affinidi-login-callback.php
@@ -16,16 +16,19 @@
session_start();
}
+$admin_options = new Affinidi_Login_Admin_Options();
+$idtoken_parser = new Affinidi_Login_IDToken();
+
// default to homepage if the state not found or expired
$user_redirect = home_url();
+// Not processing form or storing data.
+// phpcs:disable WordPress.Security.NonceVerification.Recommended
+
// Check for error, ensure state has value
if (empty($_GET['state'])) {
- // log error description on server side
- $log_message = "Affinidi Login: state is empty".PHP_EOL;
- error_log($log_message);
// redirect user with error code
- wp_safe_redirect($user_redirect . "?message=affinidi_login_failed");
+ wp_safe_redirect(add_query_arg(array('message' => 'affinidi_login_failed_empty_state'), $user_redirect));
exit;
}
@@ -48,51 +51,60 @@
'oauth' => 'authorize',
'response_type' => 'code',
'scope' => 'openid',
- 'client_id' => affinidi_get_option('client_id'),
+ 'client_id' => $admin_options->client_id,
'redirect_uri' => site_url('?auth=affinidi'),
'state' => urlencode($state),
'code_challenge' => $code_challenge,
'code_challenge_method' => 'S256',
];
$params = http_build_query($params);
- wp_redirect(affinidi_get_option('backend') . '/oauth2/auth?' . $params);
+ wp_redirect(sanitize_url($admin_options->backend) . '/oauth2/auth?' . $params);
exit;
}
+// Check for error
+if (empty($_GET['code']) && !empty($_GET['error_description'])) {
+ // redirect user with error code
+ wp_safe_redirect(add_query_arg(array('message' => 'affinidi_login_failed'), $user_redirect));
+
+ exit;
+}
+
+// grab the code
+$auth_code = sanitize_text_field($_GET['code']);
// retrieve state and get the transient info for redirect
-$state = sanitize_text_field($_GET['state']);
+$state = sanitize_text_field($_GET['state']);
$redirect_to = get_transient("affinidi_user_redirect_to".$state);
+
// check if the state exists
if (!empty($redirect_to) && !empty($redirect_to[$state]) && !empty($redirect_to[$state]['redirect_to'])) {
// set the redirect url based on state
- $user_redirect = $redirect_to[$state]['redirect_to'];
+ $user_redirect = sanitize_url($redirect_to[$state]['redirect_to']);
// delete the transient after
delete_transient("affinidi_user_redirect_to".$state);
}
// Check for error
-if (empty($_GET['code']) && !empty($_GET['error_description'])) {
- // log error description on server side
- $log_message = "Affinidi Login: error={$_GET['error']}&error_description={$_GET['error_description']}".PHP_EOL;
- error_log($log_message);
+if (empty($auth_code) && !empty($_GET['error_description'])) {
// redirect user with error code
- wp_safe_redirect($user_redirect . "?message=affinidi_login_failed&error={$_GET['error']}");
+ wp_safe_redirect(add_query_arg(array('message' => 'affinidi_login_failed'), esc_url($user_redirect)));
exit;
}
+// phpcs:enable WordPress.Security.NonceVerification.Recommended
+
// Handle the callback from the backend is there is one.
-if (!empty($_GET['code'])) {
+if (!empty($auth_code)) {
- $code = sanitize_text_field($_GET['code']);
- $backend = affinidi_get_option('backend') . '/oauth2/token';
+ $backend = sanitize_url($admin_options->backend) . '/oauth2/token';
// retrieve the code verifier from the SESSION
- $code_verifier = $_SESSION[$state];
+ $code_verifier = sanitize_text_field($_SESSION[$state]);
$request_body = [
'grant_type' => 'authorization_code',
- 'code' => $code,
- 'client_id' => affinidi_get_option('client_id'),
+ 'code' => $auth_code,
+ 'client_id' => $admin_options->client_id,
'code_verifier' => $code_verifier,
'redirect_uri' => site_url('?auth=affinidi')
];
@@ -104,39 +116,32 @@
);
if (is_wp_error($response)) {
- // log error description
- $error_message = $response->get_error_message();
- error_log($error_message);
// redirect user with error code
- wp_safe_redirect($user_redirect . "?message=wp_error_affinidi_login");
+ wp_safe_redirect(add_query_arg(array('message' => 'affinidi_login_failed'), esc_url($user_redirect)));
exit;
}
$tokens = json_decode(wp_remote_retrieve_body($response));
if (isset($tokens->error)) {
- // log error description on server side
- $log_message = "Affinidi Login: error={$tokens->error}&error_description={$tokens->error_description}".PHP_EOL;
- error_log($log_message);
// redirect user with error code
- wp_safe_redirect($user_redirect . "?message=affinidi_login_failed&error={$tokens->error}");
+ wp_safe_redirect(add_query_arg(array('message' => 'affinidi_login_failed'), esc_url($user_redirect)));
exit;
}
-
- $access_token = $tokens->access_token;
+ // parse ID Token from Affinidi Login response
$id_token = $tokens->id_token;
$info = json_decode(base64_decode(str_replace('_', '/', str_replace('-', '+', explode('.', $id_token)[1]))), true);
// extract user info
- $userInfo = extract_user_info($info);
+ $userInfo = $idtoken_parser->extract_user_info($info);
// extract contact info
- $contactInfo = extract_contact_info($info);
+ $contactInfo = $idtoken_parser->extract_contact_info($info);
$user_id = null;
if (email_exists($userInfo['email']) == false) {
- if (wp_users_can_signup() == 0) {
- wp_safe_redirect(home_url() . '?message=affinidi_login_only');
+ if (affinidi_login_users_can_signup()) {
+ wp_safe_redirect(add_query_arg(array('message' => 'affinidi_login_only'), esc_url($user_redirect)));
exit;
}
@@ -144,20 +149,31 @@
$random_password = wp_generate_password($length = 16, $extra_special_chars = true);
$user_data = [
'user_email' => $userInfo['email'],
- 'user_login' => $userInfo['email'], // default to mail if not present
+ 'user_login' => $userInfo['email'], // default to mail
'user_pass' => $random_password,
'last_name' => $userInfo['last_name'],
- 'first_name' => $userInfo['first_name'],
+ 'first_name' => $userInfo['first_name'],
'display_name' => (!empty($userInfo['display_name']) ? $userInfo['display_name'] : $userInfo['email']) // default to mail if not present
];
$user_id = wp_insert_user($user_data);
- // set Billing and Shipping Address from Vault
- sync_address_info($user_id, $userInfo, $contactInfo, true);
+ if (empty($user_id)) {
+ // redirect user with error code
+ wp_safe_redirect(add_query_arg(array('message' => 'affinidi_login_failed'), esc_url($user_redirect)));
+ exit;
+ }
+
+ if (affinidi_login_wc_active()) {
+ // instantiate WC Affinidi Login
+ $affinidi_login_wc = new Affinidi_Login_WooCommerce($admin_options);
+ // set Billing and Shipping Address from Vault
+ $affinidi_login_wc->sync_customer_info($user_id, $userInfo, $contactInfo, true);
+ }
+
// Trigger new user created action so that there can be modifications to what happens after the user is created.
// This can be used to collect other information about the user.
- do_action('affinidi_user_created', $info, 1);
+ do_action('affinidi_user_created', $userInfo, 1);
} else {
// Already Registered... Log the User In using ID or Email
@@ -168,22 +184,32 @@
* we should check the user by email. This may be the case when the users are preregistered outside of OAuth
*/
if (!$user) {
- // Get the user by name
+ // Get the user by email using login
$user = get_user_by('login', $userInfo['email']);
}
+ if (!$user) {
+ // redirect user with error code
+ wp_safe_redirect(add_query_arg(array('message' => 'affinidi_login_failed'), esc_url($user_redirect)));
+ exit;
+ }
+
$user_id = $user->ID;
- // sync the address from Vault
- sync_address_info($user_id, $userInfo, $contactInfo, false);
+ if (affinidi_login_wc_active()) {
+ // instantiate WC Affinidi Login
+ $affinidi_login_wc = new Affinidi_Login_WooCommerce($admin_options);
+ // set Billing and Shipping Address from Vault
+ $affinidi_login_wc->sync_customer_info($user_id, $userInfo, $contactInfo, false);
+ }
// Trigger action when a user is logged in.
// This will help allow extensions to be used without modifying the core plugin.
- do_action('affinidi_user_login', $info, 1);
+ do_action('affinidi_user_login', $userInfo, 1);
}
// Did we retrieved or created the user successfully?
- if ($user_id) {
+ if (!empty($user_id)) {
// set current user session
wp_clear_auth_cookie();
wp_set_current_user($user_id);
@@ -195,6 +221,3 @@
}
}
}
-
-
-
diff --git a/includes/wp-affinidi-login-idtoken.php b/includes/wp-affinidi-login-idtoken.php
new file mode 100644
index 0000000..06776c1
--- /dev/null
+++ b/includes/wp-affinidi-login-idtoken.php
@@ -0,0 +1,64 @@
+extract_claim($info, 'email');
+ $firstName = $this->extract_claim($info, 'given_name');
+ $lastName = $this->extract_claim($info, 'family_name');
+ $displayName = trim("{$firstName} {$lastName}");
+
+ return array(
+ 'email' => $email,
+ 'first_name' => $firstName,
+ 'last_name' => $lastName,
+ 'display_name' => $displayName
+ );
+
+ }
+
+ public function extract_contact_info($info)
+ {
+ // get list of countries for transformation
+ include_once(AFFINIDI_PLUGIN_DIR . '/templates/countries-list.php');
+ // extract user info
+ $streetAddress = $this->extract_claim($info['address'], 'street_address');
+ $locality = $this->extract_claim($info['address'], 'locality');
+ $region = $this->extract_claim($info['address'], 'region');
+ $postalCode = $this->extract_claim($info['address'], 'postal_code');
+ $country = $this->extract_claim($info['address'], 'country');
+ $phoneNumber = $this->extract_claim($info, 'phone_number');
+
+ // get the country code
+ $country = sanitize_text_field(array_search($country, $countries_list));
+
+ return array(
+ 'address_1' => $streetAddress,
+ 'city' => $locality,
+ 'state' => $region,
+ 'postcode' => $postalCode,
+ 'country' => $country,
+ 'phone' => $phoneNumber
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/includes/wp-affinidi-login-rewrites.php b/includes/wp-affinidi-login-rewrites.php
index 4b33417..5bf1e8e 100644
--- a/includes/wp-affinidi-login-rewrites.php
+++ b/includes/wp-affinidi-login-rewrites.php
@@ -7,8 +7,16 @@
* Class Rewrites
*
*/
-class WP_Affinidi_Login_Rewrites
+class Affinidi_Login_Rewrites
{
+
+ private $admin_options;
+
+ public function __construct(Affinidi_Login_Admin_Options $admin_options)
+ {
+ $this->admin_options = new Affinidi_Login_Admin_Options();
+ }
+
public function create_rewrite_rules($rules): array
{
global $wp_rewrite;
@@ -33,10 +41,9 @@ public function flush_rewrite_rules()
}
public function template_redirect_intercept(): void
- {
+ {
global $wp_query;
- $auth = $wp_query->get('auth');
- $options = get_option('affinidi_options');
+ $auth = sanitize_text_field($wp_query->get('auth'));
if ($auth !== '') {
// affinidi will add another ? to the uri, this will make the value of auth like this : affinidi?code=c9550137370a99bc2137
@@ -53,7 +60,7 @@ public function template_redirect_intercept(): void
}
global $pagenow;
- $message = $wp_query->get('message');
+ $message = sanitize_text_field($wp_query->get('message'));
if ($pagenow == 'index.php' && isset($message)) {
require_once(AFFINIDI_PLUGIN_DIR . '/templates/wp-affinidi-login-error-msg.php');
}
@@ -65,7 +72,8 @@ public function template_redirect_intercept(): void
}
}
-$rewrites = new WP_Affinidi_Login_Rewrites();
+$rewrites = new Affinidi_Login_Rewrites(new Affinidi_Login_Admin_Options());
+
add_filter('rewrite_rules_array', [$rewrites, 'create_rewrite_rules']);
add_filter('query_vars', [$rewrites, 'add_query_vars']);
add_filter('wp_loaded', [$rewrites, 'flush_rewrite_rules']);
diff --git a/includes/wp-affinidi-login-wc.php b/includes/wp-affinidi-login-wc.php
new file mode 100644
index 0000000..b84b1d7
--- /dev/null
+++ b/includes/wp-affinidi-login-wc.php
@@ -0,0 +1,103 @@
+admin_options = $options;
+ }
+
+ function set_wc_billing_address(& $customer, $userInfo, $contactInfo)
+ {
+ // set billing info
+ $customer->set_billing_first_name($userInfo['first_name']);
+ $customer->set_billing_last_name($userInfo['last_name']);
+ $customer->set_billing_email($userInfo['email']);
+ $customer->set_billing_phone($contactInfo['phone']);
+
+ $customer->set_billing_address($contactInfo['address_1']);
+ $customer->set_billing_city($contactInfo['city']);
+ $customer->set_billing_state($contactInfo['state']);
+ $customer->set_billing_postcode($contactInfo['postcode']);
+ $customer->set_billing_country($contactInfo['country']);
+ }
+
+ function set_wc_shipping_address(& $customer, $userInfo, $contactInfo)
+ {
+ // set billing info
+ $customer->set_shipping_first_name($userInfo['first_name']);
+ $customer->set_shipping_last_name($userInfo['last_name']);
+ $customer->set_shipping_phone($contactInfo['phone']);
+
+ $customer->set_shipping_address($contactInfo['address_1']);
+ $customer->set_shipping_city($contactInfo['city']);
+ $customer->set_shipping_state($contactInfo['state']);
+ $customer->set_shipping_postcode($contactInfo['postcode']);
+ $customer->set_shipping_country($contactInfo['country']);
+ }
+
+ public function sync_customer_info($customerId, $userInfo, $contactInfo, $isSignup)
+ {
+ // Get the WC_Customer instance object from user ID
+ $customer = new WC_Customer( $customerId );
+
+ // sync address info from Vault
+ if ($isSignup || $this->admin_options->ecommerce_sync_address_info != "billing") {
+ $this->set_wc_billing_address($customer, $userInfo, $contactInfo);
+ $this->set_wc_shipping_address($customer, $userInfo, $contactInfo);
+ } else {
+ $this->set_wc_billing_address($customer, $userInfo, $contactInfo);
+ }
+
+ // save customer data
+ $customer->save();
+
+ }
+
+ public function filter_affinidi_login_wc_login()
+ {
+ $affinidi_login_form_button = sprintf(
+ '
',
+ esc_html($alert_message),
+ esc_url(site_url('?auth=affinidi')),
+
+ );
+
+ echo wp_kses_post($alert_message_html);
+}
diff --git a/wp-affinidi-login.php b/wp-affinidi-login.php
index 74e32b9..7c70e29 100644
--- a/wp-affinidi-login.php
+++ b/wp-affinidi-login.php
@@ -20,6 +20,7 @@
* Description: A paradigm shift in the registration and sign-in process, Affinidi Login is a game-changing solution for developers. With our revolutionary passwordless authentication solution your user's first sign-in doubles as their registration, and all the necessary data for onboarding can be requested during this streamlined sign-in/signup process. End users are in full control, ensuring that they consent to the information shared in a transparent and user-friendly manner. This streamlined approach empowers developers to create efficient user experiences with data integrity, enhanced security and privacy, and ensures compatibility with industry standards.
* Version: 1.1.0
* Requires at least: 6.4
+ * Tested up to: 6.5
* Requires PHP: 7.4
* Author: Affinidi
* Author URI: https://affinidi.com
@@ -38,31 +39,13 @@
// Require the main plugin class
require_once(AFFINIDI_PLUGIN_DIR . 'Affinidi.php');
-add_action('wp_loaded', 'affinidi_register_files');
-
-function affinidi_register_files()
-{
- // Register a CSS stylesheet.
- wp_register_style('affinidi_admin', plugins_url('/assets/css/affinidi-login.css', __FILE__), array(), '1.0.0');
- // Register a new script.
- wp_register_script('affinidi_admin', plugins_url('/assets/js/affinidi-login.js', __FILE__), array(), '1.0.0', false);
-}
-
-add_action('admin_head', 'affinidi_register_admin_files');
-
-function affinidi_register_admin_files()
-{
- // Register a CSS stylesheet.
- $styleUrl = plugins_url('/assets/css/admin.css', __FILE__);
- echo "\n";
- // Register a new script.
- $jsUrl = plugins_url('/assets/js/admin.js', __FILE__);
- echo "\n";
-}
-
$affinidi = new Affinidi();
+
add_action('admin_menu', [$affinidi, 'plugin_init']);
-add_action('wp_enqueue_scripts', [$affinidi, 'wp_enqueue']);
+add_action( 'admin_enqueue_scripts', [$affinidi, 'affinidi_login_enqueue_admin_scripts'] );
+add_action( 'wp_enqueue_scripts', [$affinidi, 'affinidi_login_enqueue_fe_scripts'] );
+add_action( 'login_enqueue_scripts', [$affinidi, 'affinidi_login_enqueue_fe_scripts'] );
add_action('wp_logout', [$affinidi, 'logout']);
+
register_activation_hook(__FILE__, [$affinidi, 'setup']);
register_activation_hook(__FILE__, [$affinidi, 'upgrade']);