Implementing Passwordless Login Using initPasswordlessLogin and verifyPasswordlessLogin from UserManagement

How initPasswordlessLogin Works in Our Custom Login Component

The initPasswordlessLogin method is used to initiate the passwordless login process. When a user enters their email, the system sends a one-time password (OTP)  to their email address. This method triggers the email with a session identifier, which is then used in the verification process.

Updated Apex Controller for initPasswordlessLogin:

@AuraEnabled
public static String initiateLogin(String email) {
    try {
        // Use the UserManagement method to initiate the passwordless login
        String identifier = UserManagement.initPasswordlessLogin(email);
        
        // Return the identifier to the LWC component for further use
        return identifier;
    } catch (Exception e) {
        // Handle any exceptions that occur during the login process
        throw new AuraHandledException('Error initiating passwordless login: ' + e.getMessage());
    }
}

In this Apex method:

  • We pass the user’s email address to UserManagement.initPasswordlessLogin.
  • A session identifier is returned if the process is successful, and it’s passed back to the LWC component.
  • If an error occurs, we catch and handle it to provide feedback to the user.

LWC JavaScript: Handling the Login Initiation

When the user enters their email and clicks the Login button, the initiateLogin() method in the LWC JavaScript is called:

async initiateLogin() {
    try {
        // Call the Apex method and pass the user’s email address
        this.identifier = await initiateLogin({ email: this.email });
        
        // Show the verification code input after the email is successfully submitted
        this.showVerification = true;
        console.log('code sent successfully.');
    } catch (error) {
        // Log any errors and display them to the user
        console.error('Error initiating login:', error);
        this.errorMessage = JSON.stringify(error);
    }
}

In this JavaScript function:

  • The user’s email is sent to the backend Apex method to initiate the passwordless login.
  • The component switches to display the verification input once the magic link or code has been sent successfully.
  • If there’s an error, it is displayed to the user.

How verifyPasswordlessLogin Works in Our Custom Login Component

The verifyPasswordlessLogin method verifies the one-time password (OTP) or code that the user received via email. After entering the code, it authenticates the user, and they are redirected to the appropriate page if successful.

Updated Apex Controller for verifyPasswordlessLogin:

@AuraEnabled
public static String verifyLogin(String email, String identifier, String code) {
    try {
        // Call the UserManagement method to verify the login with email, identifier, and code
        Boolean isVerified = UserManagement.verifyPasswordlessLogin(email, identifier, code);
        
        // If the verification is successful, return the URL to redirect the user
        if (isVerified) {
            return '/community/home'; // Redirect URL can be customized as needed
        } else {
            throw new AuraHandledException('Invalid code or login failed.');
        }
    } catch (Exception e) {
        // Handle any exceptions that occur during the verification process
        throw new AuraHandledException('Error verifying passwordless login: ' + e.getMessage());
    }
}

In this method:

  • The user’s email, identifier, and verification code are sent to UserManagement.verifyPasswordlessLogin.
  • If the code is valid, the method returns a URL to redirect the user to a specific page (e.g., the Community homepage).
  • If the code is incorrect, an error is thrown and displayed to the user.

LWC JavaScript: Handling the Verification Process

When the user enters the verification code and clicks the Verify Login button, the verifyLogin() function in the LWC JavaScript is called:

 

async verifyLogin() {
    try {
        // Call the Apex method to verify the email, session identifier, and code
        const result = await verifyLogin({
            email: this.email,
            identifier: this.identifier,
            code: this.code,
        });

        if (result) {
            // If verification is successful, redirect to the result URL
            console.log('Login successful! Redirecting to ' + result);
            window.open(result, '_self');
        } else {
            // If the verification fails, show an error message
            console.error('Invalid code or login failed.');
            this.errorMessage = 'Invalid code or login failed.';
        }
    } catch (error) {
        // Display any errors that occur during the verification process
        this.errorMessage = JSON.stringify(error);
        console.error('Error verifying login:', error);
    }
}

In this function:

  • We pass the email, identifier, and code to the backend Apex method to verify the login.
  • If the code is correct, the user is redirected to the specified URL.
  • If the code is incorrect or any other error occurs, it is displayed to the user.

Complete Code Overview

Below is the updated and complete code with UserManagement methods integrated for passwordless login:

HTML:

<template>
    <div class="card">
    <lightning-card>
        <template if:false={showVerification}>
        <div class="slds-p-around_medium">
            <lightning-input 
                label="Email" 
                value={email} 
                onchange={handleEmailChange}>
            </lightning-input>
            <button type="button" label="Login" class="btn" onclick={initiateLogin} variant="brand">Login</button>
        </div>
        </template>
        <template if:true={showVerification}>
            <div class="slds-p-around_medium">
                <lightning-input 
                    label="Enter Your Verification Code" 
                    value={code} 
                    onchange={handleCodeChange}>
                </lightning-input>
                <button type="button" label="Verify Login" class="btn" onclick={verifyLogin} variant="brand">Login</button>
            </div>
        </template>
    </lightning-card>
    <div if:true={errorMessage} style="color: rgb(137, 14, 14);">
        {errorMessage}
    </div>
</div>
</template>

JavaScript :

import { LightningElement, track } from 'lwc';
import initiateLogin from '@salesforce/apex/MFILoginController.initiateLogin';
import verifyLogin from '@salesforce/apex/MFILoginController.verifyLogin';
export default class login extends LightningElement {
    @track email = '';
    @track code = '';
    @track identifier;
    @track showVerification = false;
    @track errorMessage;
    // Handles email input change
    handleEmailChange(event) {
        this.email = event.target.value;
    }

    // Handles code input change
    handleCodeChange(event) {
        this.code = event.target.value;
    }

    // Initiates the passwordless login process
    async initiateLogin() {
        try {
            this.identifier = await initiateLogin({ email: this.email });
            this.showVerification = true;
            console.log('Magic link or code sent successfully.');
        } catch (error) {
            console.error('Error initiating login:', error);
            this.errorMessage = JSON.stringify(error);
        }
    }

    // Verifies the login using the code
    async verifyLogin() {
        try {
            const result = await verifyLogin({
                email: this.email,
                identifier: this.identifier,
                code: this.code,
            });

            if (result) {
                console.log('Login successful!'+result);
                window.open(result,'_self');
            } else {
                console.error('Invalid code or login failed.');
                this.errorMessage = 'Invalid code or login failed.';
            }
        } catch (error) {
            this.errorMessage = JSON.stringify(error);
            console.error('Error verifying login:', error);
        }
    }
}

 

Apex Controller

public without sharing class MFILoginController { 

    /**
     * Initiates passwordless login for the given user.
     * @param userId The Salesforce User Id.
     * @return String Identifier (e.g., token or challenge).
     */
    @AuraEnabled
    public static String initiateLogin(String email) {
        User user = [SELECT Id FROM User WHERE Email = :email AND IsPortalEnabled = true LIMIT 1];
        if (user == null) {
            throw new AuraHandledException('User not found with the given email.');
        }

        // Use standard UserManagement method to initiate passwordless login
        return UserManagement.initPasswordlessLogin(
            user.Id, 
            Auth.VerificationMethod.EMAIL // Use EMAIL method or another appropriate one.
        );
    }

    /**
     * Verifies passwordless login using the provided code or token.
     */
    @AuraEnabled
    public static String verifyLogin(String email, String identifier, String code) {
        User user = [SELECT Id FROM User WHERE Email = :email AND IsPortalEnabled = true LIMIT 1];
        if (user == null) {
            throw new AuraHandledException('User not found.');
        }

        // Verify the login using UserManagement standard method.
        Auth.VerificationResult result = UserManagement.verifyPasswordlessLogin(
            user.Id,
            Auth.VerificationMethod.EMAIL,
            identifier,
            code,
            '/home/' // Redirect URL after successful login.
        );
        return result.redirect.getUrl() ;
    }
}

 

CSS

/* Lightning card customization */
.card {
    max-width: 400px !important;
    margin:  auto !important;
}

/* Input field customization */
.lightning-input {
    width: 100%;
    margin-bottom: 16px;
}

.slds-form-element__label {
    font-size: 16px;
    font-weight: normal;
}

/* Login button customization */
.btn {
    background-color: #0070d2; /* Custom Red Color */
    border-color: #0070d2; /* Red Border */
    color: white; /* White Text */
    font-weight: bold;
    padding: 8px 12px; /* Custom Padding */
    border-radius: 8px; /* Rounded Corners */
    width: 100%;
    margin-top: 1rem;
    box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); /* Subtle Shadow */
    transition: background-color 0.3s ease, transform 0.2s ease;
}

.btn:hover {
    background-color: #005fb2; /* Darker Red on Hover */
    border-color: #005fb2;
    transform: scale(1.05); /* Slight Zoom on Hover */
    box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.3);
}

.btn:active {
    background-color: #005fb2; /* Even Darker Red on Click */
    transform: scale(0.95); /* Button Press Effect */
    box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2);
}

/* Error message styling */
.slds-text-color_error {
    color: #d9534f; /* Matches Salesforce error color */
    text-align: center;
    font-size: 14px;
}

 

XML :

<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>61.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightningCommunity__Page</target>
        <target>lightningCommunity__Default</target>
        <target>lightningCommunity__Page_Layout</target>
        <target>lightningCommunity__Theme_Layout</target>
    </targets>
</LightningComponentBundle>

 


Conclusion

By using UserManagement’s initPasswordlessLogin and verifyPasswordlessLogin methods, you’ve created a secure, passwordless authentication flow that enhances user experience while ensuring security. This approach is modern, eliminating the need for passwords, and works seamlessly with your Salesforce Community.

5 2 votes
Article Rating