Wordworks

Users should be able to register and login with a Phone number (they don't have an email and don't use it)

Assigned to
Xoliswa Shandu, Matogen Digital Xoliswa S.
Notes
https://help.okta.com/oie/en-us/content/topics/identity-engine/authenticators/configure-phone.htm#:~:text=End%2Duser%20experience,and%20enter%20a%20phone%20number

Xoliswa Shandu, Matogen Digital Xoliswa we will need to make it possible for users to login without an email address and use a phone number only - it is a requirement for this project as some users do not have or understand emails  Ian Parsons, Head Chef at Matogen Digital Ian  heads up - I will give more details on another channel 

Comments & Events

Ian Parsons, Head Chef at Matogen Digital
From Claude:

I'll explain how to modify the code to handle account creation with just a phone number in Okta and WordPress. This requires additional handling since WordPress traditionally requires email addresses for accounts.



```php
<?php
/**
 * Plugin Name: Okta Authentication with Email/Phone
 * Description: Enables Okta authentication with email or phone number sign-in options
 */

// Previous code remains the same until add_registration_options()

class OktaEmailPhoneAuth {
    // ... previous properties ...
    
    private $phone_meta_key = 'okta_phone_number';
    private $generated_email_domain = 'phone-user.example.com'; // Change this to your domain

    public function __construct() {
        // ... previous constructor code ...
        
        // Add registration handling
        add_action('register_post', array($this, 'validate_registration'), 10, 3);
        add_action('user_register', array($this, 'handle_user_registration'));
        add_filter('pre_user_login', array($this, 'modify_username_for_phone'));
    }

    public function add_registration_options() {
        ?>
        <div class="okta-registration-options">
            <div class="registration-type">
                <input type="radio" name="registration_type" id="email_registration" value="email" checked>
                <label for="email_registration">Register with Email</label>
            </div>
            <div class="registration-type">
                <input type="radio" name="registration_type" id="phone_registration" value="phone">
                <label for="phone_registration">Register with Phone Number</label>
            </div>
            
            <div id="email_fields">
                <p>
                    <label for="user_email">Email Address</label>
                    <input type="email" name="user_email" id="user_email">
                </p>
            </div>
            
            <div id="phone_fields" style="display: none;">
                <p>
                    <label for="phone_number">Phone Number</label>
                    <input type="tel" name="phone_number" id="phone_number" 
                           pattern="[0-9]{10,15}" title="Please enter a valid phone number">
                </p>
            </div>
        </div>
        <?php
    }

    public function validate_registration($login, $email, $errors) {
        $registration_type = $_POST['registration_type'] ?? 'email';
        $phone_number = $_POST['phone_number'] ?? '';

        if ($registration_type === 'phone') {
            // Clear email-related errors since we're registering with phone
            $errors->remove('empty_email');
            $errors->remove('invalid_email');

            // Validate phone number
            if (empty($phone_number)) {
                $errors->add('empty_phone', 'Please enter a phone number.');
            } elseif (!preg_match('/^[0-9]{10,15}$/', $phone_number)) {
                $errors->add('invalid_phone', 'Please enter a valid phone number.');
            }

            // Check if phone number is already registered
            if ($this->is_phone_number_registered($phone_number)) {
                $errors->add('phone_exists', 'This phone number is already registered.');
            }
        }

        return $errors;
    }

    public function handle_user_registration($user_id) {
        $registration_type = $_POST['registration_type'] ?? 'email';
        
        if ($registration_type === 'phone') {
            $phone_number = sanitize_text_field($_POST['phone_number']);
            
            // Store phone number in user meta
            update_user_meta($user_id, $this->phone_meta_key, $phone_number);

            // Create Okta user with phone number
            $this->create_okta_user_with_phone($user_id, $phone_number);
        }
    }

    private function create_okta_user_with_phone($user_id, $phone_number) {
        // Generate a unique identifier for the user
        $username = 'phone_' . preg_replace('/[^0-9]/', '', $phone_number);
        $generated_email = $username . '@' . $this->generated_email_domain;

        // Prepare user data for Okta
        $user_data = array(
            'profile' => array(
                'firstName' => 'Phone',
                'lastName' => 'User',
                'email' => $generated_email,
                'login' => $username,
                'mobilePhone' => $phone_number,
                'primaryPhone' => $phone_number,
            ),
            'credentials' => array(
                'password' => array(
                    'value' => wp_generate_password(24, true, true)
                )
            )
        );

        // Create user in Okta
        $response = wp_remote_post("{$this->okta_domain}/api/v1/users", array(
            'headers' => array(
                'Authorization' => 'SSWS ' . $this->client_secret,
                'Content-Type' => 'application/json'
            ),
            'body' => json_encode($user_data)
        ));

        if (is_wp_error($response)) {
            error_log('Okta user creation failed: ' . $response->get_error_message());
            return false;
        }

        // Get Okta user ID from response
        $okta_user = json_decode(wp_remote_retrieve_body($response));
        if (isset($okta_user->id)) {
            // Store Okta user ID in WordPress user meta
            update_user_meta($user_id, 'okta_user_id', $okta_user->id);
            
            // Enroll phone factor
            $this->enroll_phone_factor($okta_user->id, $phone_number);
        }

        return true;
    }

    private function enroll_phone_factor($okta_user_id, $phone_number) {
        $factors_api_url = "{$this->okta_domain}/api/v1/users/{$okta_user_id}/factors";
        
        $factor_data = array(
            'factorType' => 'sms',
            'provider' => 'OKTA',
            'profile' => array(
                'phoneNumber' => $phone_number
            )
        );

        $response = wp_remote_post($factors_api_url, array(
            'headers' => array(
                'Authorization' => 'SSWS ' . $this->client_secret,
                'Content-Type' => 'application/json'
            ),
            'body' => json_encode($factor_data)
        ));

        if (is_wp_error($response)) {
            error_log('Failed to enroll phone factor: ' . $response->get_error_message());
            return false;
        }

        return true;
    }

    private function is_phone_number_registered($phone_number) {
        global $wpdb;
        
        $exists = $wpdb->get_var($wpdb->prepare(
            "SELECT user_id FROM {$wpdb->usermeta} 
            WHERE meta_key = %s AND meta_value = %s LIMIT 1",
            $this->phone_meta_key,
            $phone_number
        ));

        return !empty($exists);
    }

    public function modify_username_for_phone($username) {
        if (isset($_POST['registration_type']) && $_POST['registration_type'] === 'phone') {
            $phone_number = $_POST['phone_number'];
            return 'phone_' . preg_replace('/[^0-9]/', '', $phone_number);
        }
        return $username;
    }
}

// Initialize the plugin
new OktaEmailPhoneAuth();

```

Here's how the phone-only registration process works:

1. **User Registration Flow**:
   - User selects "Register with Phone Number" option
   - Enters their phone number
   - The system validates the phone number format and uniqueness

2. **Account Creation Process**:
   - When registering with phone only:
     - Generates a unique username based on the phone number (e.g., "phone_1234567890")
     - Creates a synthetic email address using the configured domain (e.g., "phone_1234567890@phone-user.example.com")
     - Stores the phone number in WordPress user meta
     - Creates the user in both WordPress and Okta

3. **Okta Integration**:
   - Creates an Okta user with:
     - Generated username and email
     - Phone number stored in both mobilePhone and primaryPhone fields
     - Random secure password (since login will be via phone)
     - Enrolls the SMS factor for the user automatically

4. **Security Considerations**:
   - Validates phone number format and uniqueness
   - Stores phone numbers securely in user meta
   - Maintains mapping between WordPress and Okta user IDs
   - Uses secure communication with Okta API

To implement this:

1. Update your Okta application settings to allow phone number authentication:
   ```json
   {
     "settings": {
       "factors": {
         "sms": {
           "enabled": true,
           "priority": 1
         }
       }
     }
   }
   ```

2. Add JavaScript to handle the registration form:
```javascript
jQuery(document).ready(function($) {
    $('input[name="registration_type"]').on('change', function() {
        if ($(this).val() === 'phone') {
            $('#email_fields').hide();
            $('#phone_fields').show();
        } else {
            $('#email_fields').show();
            $('#phone_fields').hide();
        }
    });
});
```

3. Change the `generated_email_domain` in the code to match your domain.

The key points to understand:

1. WordPress requires an email address for accounts, so we generate a synthetic one
2. The actual authentication is done via phone number using Okta's SMS factor
3. The user never sees or needs to know the generated email address
4. The phone number becomes the primary identifier for the user
Robyn, Project Manager at Matogen Digital 🙌 YAY!
Xoliswa Shandu, Matogen Digital
Updates:
Approach after user signup wordpress user creation with phone number as main login if user email address not present
Approach tried to make phone number main authentication. SA not allowed, will research into requirements.

Notes:
-
Log in via Okta using mobile number/email
- Be authenticated and authorized
- Be redirected to the WordPress multisite admin area after successful authentication

Okta:
User Mapping for Okta attributes ( phone number) properly mapped to the WordPress user fields.
Xoliswa Shandu, Matogen Digital
Register and login pages

Issue requires resolution:
- Styling
-
Login page has reset to default and not displaying as on the Auth0 application.

Register Creates user in wordpress admin, the redirect to the login form does not display the correct form as on the application, investigated the issue but the form seems to be the default

https://wordworks.storiesandscience.co.za/register
https://wordworks.storiesandscience.co.za/phone-login/