User Password Change

Introduction

If one of your users loses or forgets their password, you'll want an automated way for them to retrieve it.

This tutorial shows you how to set up Cloud Code to do this. The set up described here is the most basic way users have access to your platform without them having to be authenticated and can be developed for more advanced and specific applications.

Important! If you're working on a new game that was created after the Game Data Service was launched in January 2018, you won't be able to create new Mongo Runtime collections and the Cloud Code used in the tutorial will not work. For the alternative tutorial for new games using the Game Data Service, see Automating Password Change Using Game Data Service.

Cloud Code Section

Choosing Our Sequence of Actions and Passing in scriptData

1. Navigate to the Configurator > Cloud Code.

2. Under Scripts click to expand the Requests area and then select AuthenticationRequest. The Cloud Code editor opens for the request and you can start editing it. This will allow users to run the password recovery logic without having an authenticated account.

var status = "Started";
//Checking if there is any scriptData passed in, if not then carry on the authentication as normal
if(Spark.data.scriptData){

    var action = Spark.data.scriptData.action;

    if("passwordRecoveryRequest" === action){
        //Start recovery sequence
        startRecovery(Spark.data.scriptData);

    } else if ("resetPassword" === action){
        //Start reset sequence
        resetPassword(Spark.data.scriptData);
    }

    else
    {
        // action variable isn't valid, check spelling or value
        status = "invalid action";
    }
// set an error to prevent the AuthenticationRequest being processed
    Spark.setScriptError("action", status);

}

Password Recovery Sequence

Next, you'll need to link the user account to an E-mail. You can do this:

If the email passed in is linked to an account, then:

Below is an example of how to save Emails on users and keep track of them so you can generate and send users tokens to change their passwords with. For this to work, you need a runtime collection to save emails and tokens in with the player's unique ID for easy reference. Our collection used in this example is named "playerEmailCollection":

The Request Cloud Code:

//Check if scriptData has been passed in to avoid errors
if(Spark.getData().scriptData !== null){
    //Check if the email has been passed in to avoid errors
    if(Spark.getData().scriptData.email === null){
        //If no email has been passed in, force an error to make sure player is not registered
        Spark.setScriptError("error", "DEFINE EMAIL");
    }else{
        //If email is passed in, set it as scriptData so that the response can access it
        //Load email collection
        var emailCollection = Spark.runtimeCollection("playerEmailCollection");
        //Load Email passed in
        var email = Spark.getData().scriptData.email;
        //Compare that email to the database and deny registration if email is already in use
        var checkEmail = emailCollection.findOne({"email":email});
        if(checkEmail === null){
            Spark.setScriptData("email", email)
        } else{
            Spark.setScriptError("error", "EMAIL TAKEN")
        }
    }
}  else{
    //If no scriptData has been passed in, set an error and output useful message
    Spark.setScriptError("error", "DEFINE EMAIL");    
}

The Response Cloud Code:

//Check if player attempting to register isn't already registered
if(Spark.getData().newPlayer === true){
    //If no script errors from request then run sequence
    if(!Spark.hasScriptErrors()){
        //Load email
        var email = Spark.getData().scriptData.email;
        //Set the email as scriptData to keep track of it
        Spark.getPlayer().setScriptData("email", email);
        //Or set the email as privateData to keep track of it
        Spark.getPlayer().setPrivateData("email", email);
        //Load email collection
        var emailCollection = Spark.runtimeCollection("playerEmailCollection");

        //Save the email on the email collection using the player's ID as the document's ID for easy reference later on.
        emailCollection.insert({"_id":{"$oid":Spark.getPlayer().getPlayerId()},"email":email});
    }
}

Sending the Request from Test Harness or the SDK:

{
  "@class": ".RegistrationRequest",
  "displayName": "testUser",
  "password": "password",
  "userName": "testUser",
  "scriptData":{"email":"test@test.com"}
}

Below is an example code of how to create a system that saves emails and generates token for password recovery:

Sequence Picker

Depending on the 'action' scriptData input, we're going to decide the sequence the script is going to follow.

var status = "Started";
//Checking if there is any scriptData passed in and if not, then carry on the authentication as normal
if(Spark.data.scriptData){

    var action = Spark.data.scriptData.action;

    if("passwordRecoveryRequest" === action){
        //Start recovery sequence
        startRecovery(Spark.data.scriptData);

    } else if ("resetPassword" === action){
        //Start reset sequence
        resetPassword(Spark.data.scriptData);
    }

    else
    {
        // action variable isn't valid, check spelling or value
        status = "invalid action";
    }
// set an error to prevent the AuthenticationRequest being processed
    Spark.setScriptError("action", status);

}

Token Generation

This is our example of a token generating function. Yours could be anything you want it to be, but be careful that there aren't any duplicates because that would cause you a lot of problems. This is a simple string, which retrieves the date combined with randomlyGeneratedToken-, which can easily be taken advantage of.

function generateRecoveryToken(){
    // this should be cryptographically strong, not simply date-based
    return "randomlyGeneratedToken-" + new Date().getTime();
}

Sending Out the E-Mail

We have full integration with SendGrid services. If you plan to use it to send your E-Mails, then create an account with them and wait for their E-Mail confirming that: "Your SendGrid account has been provisioned!", which then allows you to start sending emails. It might take up to 24 hours to receive it. You'll need:

function sendRecoveryEmail(email, name , token){
    //Here we use sendGrid as an example because we have full integration with their services.
    var myGrid = Spark.sendGrid("Username", "Password");
    //The email to send the message to and the name of the user
    myGrid.addTo( email , name );
    //Here you'd leave your organization or personal email
    myGrid.setFrom("Email", "Name");
    //The subject of your email
    myGrid.setSubject("Subject");
    // The body and message of your email, here we just send the token
    myGrid.setText(token);
    //Finally send the email
    myGrid.send();
}

If you don't want to use sendGrid you can use SparkHTTP to use your own provider.

Password Recovery Sequence

The password recovery sequence is going to generate a token, save it on the player's email collection, and send the player an email with a token.

function startRecovery(request){
    if(!request.email){
        //Either the email variable was not passed in or it was spelt incorrectly
        status = "email variable not passed in";
        return;
    }
    var emailsCollection = Spark.runtimeCollection("playerEmailCollection")
    // requires an 'email' value to be set on each player via setScriptData, and an index to exist on scriptData.email
    var playerId = emailsCollection.findOne({"email" : request.email}, {_id : 1, displayName:1});
    if(playerId === null){
        //Email is incorrect or account isn't linked to one
        status = "invalid";
        return;
    }
    // Function to generate a unique token
    var token = generateRecoveryToken();
    var player = Spark.loadPlayer(playerId._id.$oid);
    Spark.setScriptData("token", token)
     //Sends the token back with the response
     emailsCollection.update({"_id":{"$oid":playerId._id.$oid}}, {$set: {"token":token}});
    player.setScriptData("passwordRecoveryToken", token);
   // Function used to send email
    sendRecoveryEmail(request.email, playerId.displayName, token);
    // Successfully found player and attempted to send email
    status = "complete";
}

Password Reset Sequence

Finally, you have the password reset function, which takes a token and password parameter and checks to see if the Token is linked to an account. If it is, then it sets the password of that account to the new password passed in.

function resetPassword(request){
    if(!request.token || !request.password){
        status = "Password or token variable not passed in";
        return;
    }
    var emailCollection = Spark.runtimeCollection("playerEmailCollection");
    var playerId = emailCollection.findOne({"token" : request.token}, {_id : 1});
 // requires an index to exist on scriptData.passwordRecoveryToken
    if(!playerId){
        status = "invalid";
        return;
    }
    var player = Spark.loadPlayer(playerId._id.$oid);
    player.setScriptData("passwordRecoveryToken", null); // make sure the token can only be used once
    player.setPassword(request.password); //Change password
    player.unlock(); //Unlock player just in case too many failed attempts were tried and player was locked
    status = "complete";
}

Changing Password Using The SDK

Sending E-Mail

For this step you'll have to allow the user to input their e-mail:

Changing Password

After the player has been sent a token, they'll want to be able to change their password:

Did this page help you? Please enter your feedback below. For questions about using this part of the platform, please contact support here