- Unity 2018 - https://unity3d.com/get-unity/download
- Nodejs - https://nodejs.org/en/download/
- MongoDB - https://docs.mongodb.com/manual/installation/
For running the backend REST API and connecting to MongoDB, see: https://github.com/zklinger2000/express-unity-auth
In my example, I am using a Digital Ocean droplet with MongoDB and running the Express REST API locally. Having a local MongoDB on Linux or OSX is fairly simple to setup, but I would suggest Docker on Windows, if you have a copy of Windows Pro Edition.
This project demos a token-based Authentication system built using ExpressJS and MongoDB on the server side, and a Unity client on the frontend. It is similar to the process of saving cookies into your web browser, but uses Unity's PlayerPrefs utility to save the token locally instead of saving it into the browser's Application cache.
Simply clone the repo and open the project folder in Unity. This will give you the client or front-end piece of this full-stack project. NOTE: You need the REST API, or backend server portion to see this work. See the repo link above.
git clone git@github.com:zklinger2000/unity-auth-manager.git
Go into Assets/Scenes/ and load the scene Boot.
The architecture for this Unity project is based on the principle separation of concerns. Each manager's job is to own a specific set of methods and properties dealing with specific aspects of the application.
When you load up the Boot scene, the only object in it is the GamaManager transform object with a script attached.
The GamaManager is in charge of loading scenes, instantiating prefabs and updating the state of the application.
It is also where all the other managers get initialized on startup, or enabled later at runtime.
The thing to notice about the GameManager class is that it inherits from the Manager class:
public class GameManager : Manager<GameManager>In the /Scripts/Utils folder you'll find two classes, Singleton.cs and Manager.cs. Both of these classes are
singleton implementations in C#, but the Manager class has the major distinction of not replacing an object when a
new instantiation is attempted, thereby only ever having one instance of an object, but instead, does not allow any new
instantiations at all. The video courses in the Programming Path on Pluralsight go into depth on how these two classes
are created and used. I highly suggest watching those if you want more information on the specifics.
public class UIManager : Manager<UIManager>The UIManager has a prefab with all the different canvases and buttons that make up the starting screen and menus. It
also holds all the scripts involved with handling button clicks and updating which menu should be seen for each state.
Depending on which state the application is in, each canvas has its Active property turned on or off, so they are
always sitting above every loaded scene, but not necessarily visible.
public class AuthManager : Manager<AuthManager>The AuthManager holds our User object, saves and loads our User data between sessions and makes HTTP calls to our
backend for logging in and making authorized requests for secure resources.
RestClient for Unity is a very Nodejs friendly package you can get off the Asset store. It is friendly in that it looks very similiar to how one writes requests in ExpressJS. They deserve a lot of thanks for writing that and making it open-sourced.
public void PostNewUser(string username, string password)
{
_currentRequest = new RequestHelper {
Uri = basePath + "/users/create",
Body = new JsonObjectNewUser(username, password)
};
RestClient.Post(_currentRequest)
.Then(res =>
{
JSONObject json = new JSONObject(res.Text);
_user.Username = TrimQuotationMarks(json["username"].ToString());
_user.Token = TrimQuotationMarks(json["token"].ToString());
// Save token and username with PlayerPrefs
userSaveData.Username = _user.Username;
userSaveData.Token = _user.Token;
userSaveData.Save();
GameManager.Instance.GoToMenu("welcome");
})
.Catch(err => EditorUtility.DisplayDialog ("Error", err.Message, "Ok"));A fancy way to say you don't have to login every time you open the app, or a web page. The token we receive upon
login gets saved into a file on your local machine using Unity's PlayerPrefs utility.
[SerializeField] private UserSaveData_SO userSaveData; public void Save()
{
if (key == "")
{
key = name;
}
string jsonData = JsonUtility.ToJson(this, true);
PlayerPrefs.SetString(key, jsonData);
PlayerPrefs.Save();
}
public void Load()
{
if (key == "")
{
key = name;
}
JsonUtility.FromJsonOverwrite(PlayerPrefs.GetString(key), this);
}Whenever the app opens, it checks the registry (on Windows) and if it finds a token, it updates the current User and
can now be considered "logged in" as long as any check for AuthManager.Instance.isAuthorized() returns true.
// AuthManager.cs
private void Start()
{
_user = new User();
// Load User data from PlayerPrefs
userSaveData.Load();
if (userSaveData.Token != String.Empty)
{
_user.Username = userSaveData.Username;
_user.Token = userSaveData.Token;
}
}usernameandpasswordare entered into theInputField's.- The payload is written as a JSON string.
- An
HTTP POSTrequest is sent to the/createroute of the backend API with the payload attached to the body. - If that
usernamedoes not already exist, a newUseris created and saved in MongoDB. - The response from the server sends back a JSON object with the
Userandtoken. - The
Userobject is updated on the client with whatever info we want to send back. - The
usernameandtokenare saved to the local machine.
usernameandpasswordare entered into theInputField's.- The payload is written as a JSON string.
- An
HTTP POSTrequest is sent to the/loginroute of the backend API with the payload attached to the body. - If that
usernameandpasswordare verified, we get theUserfrom MongoDB. - The response from the server sends back a JSON object with the
Userandtoken. - The
Userobject is updated on the client with whatever info we want to send back. - The
usernameandtokenare saved to the local machine.
- The
Userobject in the Unity client has its data deleted. - The file with the token on the local machine is deleted.
- Any requests for secure data to the backend or
statechange in the Unity client will no longer pass a check toAuthManager.Instance.isAuthorized();.
- A new request object is created with the JWT (token) inserted as the value of the
AuthorizationHeader. - Request is sent to the REST API.
- On the backend, the token is pulled out of the Header and decrypted, which gives the timestamp and
User._idvalue used to do a lookup in the database. - If the
Userexists, and has authorization for that particular resource (not implemented for this demo), a response with JSON data is sent back to Unity.
While this is a very simple demo, it does create the framework on which a much more robust auth system could be built. Input validation, encrypting the password in Unity before sending it in the request, adding a timestamp check, etc, can all be added onto this to make it more secure.
On the backend, we're using a simple username/password ourselves, but it could be changed to use Google, AWS, Facebook, or any other ID system, without changing the Unity code at all. This token system does not rely on how the User is authenticated, it simply relies on whether or not you have the Bearer token itself and the ID of the document in the database matching the ID in the encrypted token.
From what I've read, while these tokens can be decrypted by a dedicated hacker, they can never be successfully
created without prior access to the database and the encryption key sitting on the server. So, don't put anything
in your token that is hyper critical. The user._id in your database isn't that important without access to that
encryption key, thus making it the thing that needs to be guarded the most.
Let's say the encryption key changes once a day on the server. Every time that encryption key is changed, no tokens anywhere in the world would be valid and the next time someone tried to launch the app, or request a secured resource, the old token would be invalid, the user is considered 'logged out' and the REST API would refuse any requests made until the user is logged back in and given a new token.
After spending the last few months focused on nothing but animation in Unity, this was a great time for going on a deep dive into the "software" aspects of building a Unity game or VR app. I love the Manager system setup I learned from the Unity courses on Pluralsight. Marrying that to prior work built in React made it for a really fun project.
Zachary Klinger
UNLICENSED
All of this was built upon the techniques learned from the Swords and Shovels course on Pluralsight blended with the
authentication process from Stephen Grider's course on Udemy
Advanced React and Redux.
Swords and Shovels is actually three separate Learning Paths on Pluralsight:
