JWT Authentication in Unity

Posted on

In the near future, I hope to create an online party game similar to those found in the Jackbox series. There are a lot of concepts I need to learn before I’m ready for that. As a first step, I decided to add JWT authentication to a Unity game.

What is JWT

JWT, or Json Web Token, is a compact and self-contained way for securely transmitting information between parties. It’s very popular in web development and is most commonly used for authentication as an alternative to session cookies. What’s great about it is that it’s significantly easier to implement and has fewer dependencies.

Json web token
The official JWT logo?

Building an API

First things first, I needed an API for my Unity game to interact with. The API consists of three routes.

  1. A signup route which accepts an email and password. If valid, it creates a password hash, saves a user in the database, and sends back an auth token (jwt) with the email and id encoded inside.
  2. A login route which also accepts an email and password. If the user exists and the password is valid, it will send an auth token back.
  3. A validate route which grabs the Authorization header (which should be our auth token) and tries to decode it. If valid, it sends back a 200 status.

I’ve done this many times before, and didn’t want it to be the focus of this project. For that reason I whipped up a quick one using NodeJS. If you’re curious the source code is here.

Building the Unity game

The Unity game was intended to be super simple as well. The goal was to implement the following two scenes:

  1. An authentication scene which allows you to signup/login.
  2. A protected scene which kicks you out if not logged in.

Despite the project’s small scale, I was impressed by the number of new concepts I learned. Here are the notable ones:

Async/Await with UniTask

UniTask is a popular async/await library for Unity that came highly recommended from a friend. It works near-identically to async and await in JavaScript and pairs great with HTTP requests. Here’s an example from my AuthManager script.

private async UniTaskVoid HandleLoginSubmit() {
    try {
        string response = await HTTP.PostJson(APIRoutes.LOGIN, CreateLoginRequestBody());
        // Do something after resolved
    }
    catch {
        // Request failed!
    }
}

I really enjoyed using UniTask and I think in the future, I’ll be using async/await instead of IEnumerators wherever possible.

HTTP with Unity.Networking

Originally, I planned to use an HTTP library from the Unity Asset store, but all the best ones required payment. I soon discovered that it’s easy to make HTTP requests without any additional dependencies.

Instead of adding all the code to my login manager script, I decided to create an HTTP utility class to handle all my requests. I think it turned out very nicely and is easy to use.

public class HTTP : MonoBehaviour {
    public static async UniTask<string> PostJson<T>(string url, T body, string authToken = null) where T : struct {
        // Serialize data to JSON
        string jsonData = JsonUtility.ToJson(body);

        using (UnityWebRequest www = new UnityWebRequest(url, "POST")) {
            byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);
            www.uploadHandler = new UploadHandlerRaw(bodyRaw);
            www.downloadHandler = new DownloadHandlerBuffer();
            www.SetRequestHeader("Content-Type", "application/json");

            // Add auth token if provided
            if (!string.IsNullOrEmpty(authToken)) {
                www.SetRequestHeader("Authorization", authToken);
            }

            // Send the request
            await www.SendWebRequest();

            if (www.result == UnityWebRequest.Result.Success) {
                return www.downloadHandler.text;
            }
            else {
                throw new System.Exception(www.error);
            }
        }
    }
}

Generics

I wanted my HTTP class to handle a variety of requests, which required flexibility in the data being sent. This led me to the world of C# generics. Generics are a powerful feature; they make code much more flexible. Here’s a pointless example:

public static void LogThings<T>(T one, T two) {
    Debug.Log(one);
    Debug.Log(two);
}

Environment variables

Instead of hardcoding URLs everywhere, I created an APIRoutes class that houses all the routes to my API endpoints. When developing, I needed the routes to start with http://localhost:3000, but in production, I needed an entirely different URL. To solve this issue I introduced an environment variable. Here’s the complete class:

public class APIRoutes {
  private static readonly string DEFAULT_ROOT = "https://localhost:3000";

  public static string ROOT {
    get {
      return System.Environment.GetEnvironmentVariable("API_ROOT") ?? DEFAULT_ROOT;
    }
  }

  public static string LOGIN => $"{ROOT}/login";
  public static string SIGNUP => $"{ROOT}/signup";
  public static string VALIDATE => $"{ROOT}/validate";
}

Error handling with try catch

My JSON API returns HTTP 4XX status codes when the data is invalid or something goes wrong. I needed to catch these in order to display error messages on my forms or run actions. They’re very handy and easy to use!

try {
    // Send the request
    string response = await HTTP.PostJson(APIRoutes.SIGNUP, CreateSignupRequestBody());
}
catch (Exception ex) {
    // Display an error or something
}

Storing the token securely

Part of the deal with JWT is that I need to find a place to safely store the authentication token. If someone gets a hold of this token, they can authenticate as you. Upon researching, I discovered some plugins for secure storage similar to Player Prefs, but many sources recommended simply storing the token in memory.

This meant the user would have to login every time they restarted the game, but the token also wouldn’t be easily accessible. I opted for this method.