Skip to content

SQL Authentication

Dominique Debergue edited this page Jul 29, 2019 · 14 revisions

Before starting with this section, make sure you've properly set up the server and have a TLS/SSL connection established to the server when connecting clients.

📘 Creating accounts

With the proper server settings in place and an initialized database, clients can create accounts with the signup() function:

gopherClient.signup("bob", "password", null);

This will add an account to the database with the User name "bob", and password "password". The third parameter are the custom account info columns you wish to be set when signing a client up for an account. The third (custom account info columns) parameter must be an object where the keys are the names of existing columns, and the values are the information to store in that column. For example, say we've established two custom account info columns, email and dob:

gopherClient.signup("bob", "password", {email: "[email protected]", dob: "6-18-1992"});

Now, when we want to know if the sign up went well or something went wrong, we can use the events.signup listener:

gopherClient.addEventListener(gopherClient.events.signup, clientSignup);

function clientSignup(success, error){
	if(!success){
		console.log("Error: [ID - "+error.id+"], [Message - '"+error.m+"']");
	}else{
		console.log("Successfully created account!");
	}
}

📘 Deleting Accounts

A client can delete their account while they're logged off with the deleteAccount() function:

gopherClient.deleteAccount("bob", "password", null);

This will delete the account with the name "bob", using the password "password". The last parameter are the custom account info columns you wish to use for something when deleting an account. The last (custom account info columns) parameter must be an object where the keys are the names of existing columns, and the values are optional information to use in your server callbacks. For example:

gopherClient.deleteAccount("bob", "password", {email: "[email protected]"});

Your delete account server callback will now receive a map[string]interface{} that looks like: {email: "[email protected]"}. It will also get the info stored on the database in the email column, so you can, for instance, compare the two emails to check if the client also supplied the correct email address for the account.

Now, when we want to know if the delete went well or something went wrong, we can use the events.accountDelete listener:

gopherClient.addEventListener(gopherClient.events.accountDelete, accountDeleted);

function accountDeleted(success, error){
	if(!success){
		console.log("Error: [ID - "+error.id+"], [Message - '"+error.m+"']");
	}else{
		console.log("Successfully deleted account.");
	}
}

📘 Login

A client cannot login with SQL features enabled on the server unless they have an account, or are logging in as a guest. Once a client has created their account, they can supply their User name and password to login():

gopherClient.login("bob", "password", false, false, null);

This will log the client in under the User name "bob", using the password "password". The last parameter are the custom account info columns you wish to be sent to the server when logging a client in. The last (custom account info columns) parameter must be an object where the keys are the names of existing columns, and the values are optional information to use in your server callbacks. For example:

gopherClient.login("bob", "password", false, false, {email: "[email protected]"});

Your login server callback will now receive a map[string]interface{} that looks like: {email: "[email protected]"}. It will also get the info stored on the database in the email column, so you can, for instance, compare the two emails to check if the client also supplied the correct email address for the account.

To check for a successful login and catch any errors, just use the events.login event listener (example here).

Custom login column

If you have a custom account info column set as the login column, you can instead pass the value for that column you wish to find the account for. For example, say we have the custom account info column, email mentioned above. We set the custom login column as the email column, and now clients would enter their email as the login parameter:

gopherClient.login("[email protected]", "password", false, false, null);

The server will look for the account associated with the email "[email protected]" on the email custom account info column, and log the user "bob" in (assuming they supplied the right password).

📘 Changing Passwords

To change their password, a client must be logged into their account and supply at least a new and the correct old password. This can be done with the changePassword() function:

gopherClient.changePassword("old", "new", null);

This changes the client account's password from "old" to "new". The last parameter are the custom account info columns you wish to be sent to the server when changing a password. The last (custom account info columns) parameter must be an object where the keys are the names of existing columns, and the values are optional information to use in your server callbacks. For example:

gopherClient.changePassword("old", "new", {email: "[email protected]"});

Your password change server callback will now receive a map[string]interface{} that looks like: {email: "[email protected]"}. It will also get the info stored on the database in the email column, so you can, for instance, compare the two emails to check if the client also supplied the correct email address for the account.

To check for a successful password change and catch any errors, just use the events.passwordChange event listener:

gopherClient.addEventListener(gopherClient.events.passwordChange, changedPassword);

function changedPassword(success, error){
	if(!success){
		console.log("Error: [ID - "+error.id+"], [Message - '"+error.m+"']");
	}else{
		console.log("Successfully changed password.");
	}
}

📘 Changing Custom Account Info

To change data in a custom account info column, a client must be logged into their account and supply at least the column data to change, and the correct password for the account. This can be done with the changeAccountInfo() function:

gopherClient.changeAccountInfo("password", {email: "[email protected]"});

This checks the current account's password against "password" and changes the client account's email column to "[email protected]". The second parameter are the custom account info columns you wish to change. This must be an object where the keys are the names of existing columns, and the values are optional information to use in your server callbacks.

Your account info change server callback will now receive a map[string]interface{} that looks like: {email: "[email protected]"}. It will also get the info stored on the database in the email column, so you can, for instance, compare the two emails to check if the client didn't supply the same email again.

To check for a successful info change and catch any errors, just use the events.accountInfoChange event listener:

gopherClient.addEventListener(gopherClient.events.accountInfoChange, changedAccountInfo);

function changedAccountInfo(success, error){
	if(!success){
		console.log("Error: [ID - "+error.id+"], [Message - '"+error.m+"']");
	}else{
		console.log("Successfully changed account info.");
	}
}

📘 Auto-Login (Remember Me)

Before you can use this feature, you must enable the required core server settings. With the settings in place, a client can now send a login() request with a rememberMe bool as true, and the server will make a login key pair that saves to the client's localStorage. To make ensure that subsequent visits logs the User in, you must set the events.autologInit, events.autologNoFile, and events.autologFailed listeners:

gopherClient.addEventListener(gopherClient.events.autologInit, autologInited);
gopherClient.addEventListener(gopherClient.events.autologNoFile, autologNoFile);
gopherClient.addEventListener(gopherClient.events.autologFailed, autologFailed);

function autologInited(){
	// Prevent any login tries until either the login, autologNoFile, or autologFailed
	// listener is triggered.
}

function autologNoFile(){
	// The client has no auto-log info stored. Enable the client to login
}

function autologFailed(){
	// The client's saved login info either expired, or was compromised
	// Prompt client to change their password and enable logging in.
}

With the "remember me" server settings enabled, events.autologInit will always get triggered directly after a successful connection to the server. If the client has not logged in on a previous session, events.autologNoFile will get triggered, and the client should be allowed to log in normally. Then on subsequent visits after logging in with "remember me" enabled, the client automatically logs in, triggering the events.login listener. Otherwise, if the auto-log errored for some reason, the events.autologFailed will trigger.

📝 Note: If the events.autologFailed listener triggers, it's either because the client disconnected and disrupted the server when creating a new auto-log key, or the client's keys were compromised. It's best to assume the worst and prompt a password change after an events.autologFailed event.

📘 Friending

Basics

Enabling the SQL features automatically makes the friending features available to logged in client Users (with a valid account). When a client User sends a friend request to another User, the receiving User can accept or decline the friend request. During this time, they both will have each other temporarily on their friends lists. When the request is accepted the Users become friends, and when declined the Users are removed from each others friends lists (subsequent requests can still be sent after declining).

You can get the client's friend list with the getFriends() function:

var friends = gopherClient.getFriends();

This retrieves an Object that will look like:

friends {
	"bob": {name: "bob", requestStatus: 1, status: -1},
	"moe": {name: "moe", requestStatus: 2, status: 3},
	"ike": {name: "ike", requestStatus: 2, status: 0},
	// ...
}

As you can see, each friend Object has a name, requestStatus, and status. The requestStatus represents the status of friendship between the two Users and can be one of the following:

  1. friendStatusDefs.requested: The client requested friendship and is awaiting action
  2. friendStatusDefs.pending: The client needs to accept or decline this friendship
  3. friendStatusDefs.accepted: The client is friends with this User

status is the User status of the friend, and is always -1 unless they're an accepted friend. Look through this section to learn about User statuses.

Requesting, accepting, and declining

To send a friend request, use the requestFriend() function:

gopherClient.requestFriend("bill");

requestFriend() will trigger an events.friendRequested event for the client sending the friend request and an events.friendRequestReceived event for the User receiving the friend request:

gopherClient.addEventListener(gopherClient.events.friendRequested, requestedFriend);
gopherClient.addEventListener(gopherClient.events.friendRequestReceived, gotFriendRequest);

function requestedFriend(friendName, error){
	if(error != null){
		console.log("Error: [ID - "+error.id+"], [Message - '"+error.m+"']");
	}else{
		console.log("Sent friend request to '"+friendName+"'");
	}
}

function gotFriendRequest(friendName){
	console.log("Received friend request from '"+friendName+"'")
}

After a successful request, both users will have each other on their friends lists in a temporary state. The User sending the request will have the other in their friends list with requestStatus as friendStatusDefs.pending. The receiving User will have the other in their friends list with requestStatus as friendStatusDefs.requested.

📝 Note: Do not rely solely on the events.friendRequestReceived listener for incoming friend requests! Let's say the other User wasn't online when the client sent a friend request to them. The next time the requested User logs in, they will not get a friend request notice from the server. Instead, the client UI should show all friends in their list with requestStatus set as friendStatusDefs.requested to notify about any incoming friend requests while they were offline.

When a client receives a friend request from another User, they can either accept or decline the request with acceptFriend() or declineFriend():

gopherClient.acceptFriend("bob");
gopherClient.declineFriend("bob");

When a client accepts a friend request from another User, an events.friendAccepted event will trigger for them. And for the other User who sent the request, they'll receive an events.friendRequestAccepted event when the request gets accepted. Similarly, when a client declines a friend request from another User, an events.friendDeclined event will trigger for them and the other User who sent the request will receive an events.friendRemoved event:

gopherClient.addEventListener(gopherClient.events.friendAccepted, acceptedFriend);
gopherClient.addEventListener(gopherClient.events.friendRequestAccepted, friendAcceptedRequest);
gopherClient.addEventListener(gopherClient.events.friendDeclined, declinedFriend);
gopherClient.addEventListener(gopherClient.events.friendRemoved, friendRemoved);

function acceptedFriend(friendName, error){
	if(error != null){
		console.log("Error: [ID - "+error.id+"], [Message - '"+error.m+"']");
	}else{
		console.log("Accepted '"+friendName+"' as a friend");
	}
}

function friendAcceptedRequest(friendName){
	console.log("'"+friendName+"' accepted your friend request");
}

function declinedFriend(friendName, error){
	if(error != null){
		console.log("Error: [ID - "+error.id+"], [Message - '"+error.m+"']");
	}else{
		console.log("Declined '"+friendName+"' as a friend");
	}
}

function friendRemoved(friendName){
	console.log("'"+friendName+"' was removed from your friends list");
}

Removing

A client can remove a friend from their friends list with the removeFriend() function. This will also remove the friend from the other User's friends list and trigger an events.friendRemoved for both Users:

gopherClient.addEventListener(gopherClient.events.friendRemoved, friendRemoved);

// Remove "bob" from friends list for both Users
gopherClient.removeFriend("bob");

function friendRemoved(friendName){
	// Will trigger on both friended Users
	console.log("'"+friendName+"' was removed from your friends list");
}

📝 *Note: You may have noticed already, but the events.friendRemoved event will fire from both a friend request decline and a general friend removal. But don't forget, you can always use a friend's requestStatus to tell if they declined the client's request or simply unfriended them.

📘 User Status

User statuses don't necessarily tie directly into the SQL features and can be implemented into your own friending mechanisms. The reason I put this section here is because when SQL features are on, User status changes will be broadcasted to all the User's friends. This makes friending an important factor for using User statuses.

The client can change their status with the changeStatus() function:

// Change the client's status to "Idle"
gopherClient.changeStatus(gopherClient.userStatuses.idle);

There are four statuses that a User can be (or you can define your own):

  1. userStatuses.available: The friend is available
  2. userStatuses.inGame: The friend is in a game
  3. userStatuses.idle: The friend is idle/away
  4. userStatuses.offline: The friend is offline

You can get a printable name of a status with the statusName() function:

var sName = gopherClient.statusName(userStatuses.inGame);
console.log(sName) // prints "In Game"

Changing a client's User status will broadcast an events.friendStatusChanged event to all their friends:

gopherClient.addEventListener(gopherClient.events.friendStatusChanged, friendChangedStatus);

function friendChangedStatus(friendName, statusNumber){
	var statusName = gopherClient.statusName(statusNumber);
	console.log("'"+friendName+"' changed their status to '"+statusName +"'");
}

If you notice there is lacking information, missing features, or bad explanations, please open an issue. All requests are acceptable and will be taken into consideration, so don't be afraid to ask or report something!

Clone this wiki locally