Skip to content

Custom Authentication Tutorial

Implementing custom authentication allows you to secure your endpoints and games with bespoke authentication logic. This tutorial will guide you through creating and applying custom authentication logic. It covers both client and server-side implementations.

By following this guide, you will gain insights into how to effectively control access to your game content and services, ensuring that only authorized users can interact with your system.

Server Side Authentication

Server side Authentication in the Cobra Updater is handled by classes that inherit from the Authenticator abstract class. For this tutorial, we focus on AdminAuthenticator and ClientAuthenticator.

Creating a Custom Authenticator

This section explains how you can create custom admin or player authenticators for the server.

Custom Player Authenticator

In most scenarios involving player authentication, you'll require a tailored solution that interfaces with your specific data sources, such as a proprietary database or an external service.

To facilitate this, the tutorial will guide you through the process of creating a CustomClientAuthenticator. This class, derived from the abstract base class ClientAuthenticator, will enable you to incorporate custom logic for verifying player credentials against your unique data repositories or authentication services.

Step 1: Implement Client Authenticator
namespace ByteCobra.Updater.Server.Authenticators
{
    public class CustomClientAuthenticator : ClientAuthenticator
    {
        protected override Task<bool> AuthenticateCredentialsAsync(
            string username, 
            string password)
        {
            // Get all users from your own source.
            // Check if there is a username and password 
            // match and return the result.
            return true;
        }
    }
}
Step 2: Use Authenticator in Controllers

Register your CustomClientAuthenticator as a singleton in Program.cs:

ClientAuthenticator clientAuthenticator = new CustomClientAuthenticator();
builder.Services.AddSingleton<AdminAuthenticator>(
    serviceProvider => clientAuthenticator);

Now your CustomClientAuthenticator will be used in all the controllers that require player authentication (e.g. for downloading games).

Custom Admin Authenticator

Typically, managing admin access in the Cobra Updater is as simple as updating the Admins.json file to add or remove administrators. However, there might be scenarios where you require more dynamic management of admin credentials, such as retrieving them from a database or integrating with an external authentication system. In such cases, a custom authenticator becomes necessary.

This tutorial guides you through the process of creating a CustomAdminAuthenticator. By implementing this custom class, which inherits from the existing AdminAuthenticator, you'll be able to tailor the authentication process to your specific needs, like interfacing with databases or other credential management systems

Step 1: Implement Admin Authenticator
using ByteCobra.Updater.Models;

namespace ByteCobra.Updater.Server.Authenticators
{
    public class CustomAdminAuthenticator : AdminAuthenticator
    {
        public CustomAdminAuthenticator(List<UserCredentials> admins) 
            : base(admins)
        {
        }

        protected override Task<bool> AuthenticateCredentialsAsync(
            string username, 
            string password)
        {
            // Here you can implement custom 
            // admin authentication logic

            bool authenticated = 
                username.Equals("admin", StringComparison.OrdinalIgnoreCase)
                && password.Equals("admin", StringComparison.Ordinal);

            return Task.FromResult(true);
        }
    }
}
Step 2: Use Authenticator in Controllers

Register your CustomAdminAuthenticator class as a singleton in Program.cs:

// Read Admins.json
List<UserCredentials> admins = 
    serializer.Deserialize<List<UserCredentials>>(adminsJson);

AdminAuthenticator adminAuthenticator = 
    new CustomAdminAuthenticator(admins);

builder.Services.AddSingleton<AdminAuthenticator>(
    serviceProvider => adminAuthenticator);

Now your CustomAdminAuthenticator will be used in all the controllers that require admin authentication instead of the default one.

Client Side Authentication

When it comes to the client-side environment, be it in the Unity Editor or Unity Runtime, the process of authenticating administrators is designed to be highly automated and efficient, providing a smooth, out-of-the-box experience. Generally, it's advisable to avoid extensive customizations for administrator authentication on the client side. Alterations could potentially interfere with this optimized and streamlined process.

However, one important thing you need to focus on is creating a safe way to send player information (like usernames, passwords, or Steam IDs) to the server, so that it can verify that the user really owns the game. This is really important, especially if your game isn't free and you need to check who's playing it.

Custom Player Authenticator

The project ByteCobra.Updater.Client (which is also available in Unity) includes an Authenticator class which you can extend to integrate player authentication. This class plays an important role in securely transmitting player credentials to the server, an essential process especially in scenarios where game access is restricted to legitimate purchasers.

For instance, if your games are not free and they're available on platforms like itch.io, Steam, or similar, you'll likely want to limit downloads exclusively to players who have purchased your game. In such cases, the client-side Authenticator can be customized to send unique identifiers, such as a Steam ID. Subsequently, the server validates these identifiers to confirm that the player attempting to download the game actually owns it on any of the platforms.

To illustrate, if you're distributing your game via Steam, the client-side Authenticator would be responsible for sending the player's Steam ID to your server. The server would then verify whether this Steam ID corresponds to an account that has purchased the game, thereby regulating access to downloads based on ownership verification.

Core Client-Side Components

This section details the key classes integral to the client-side player authentication functionality.

Authenticator

The Authenticator class, integral to the client-side architecture, facilitates the transmission of client information to the server for verification purposes. Depending on your security requirements, this information could range from traditional username/password credentials to platform-specific identifiers, such as a Steam ID. To utilize the Authenticator class effectively, you need to create a subclass and implement the AuthenticateAsync method. This method is responsible for handling the authentication logic and ensuring secure communication between the client and the server.

using System.Threading;
using System.Threading.Tasks;

namespace ByteCobra.Updater
{
    public abstract class Authenticator
    {
        public abstract Task<bool> AuthenticateAsync(
            CancellationToken cancellationToken);
    }
}

Downloader

The Downloader class, an abstract base class in the Cobra Updater architecture, optionally incorporates an Authenticator to enable player authentication on the client side. This design allows you to pass an Authenticator instance to the Downloader constructor, activating authentication processes when needed.

Subclasses of Downloader can leverage this Authenticator to send client credentials to the server before initiating any game downloads.

using ByteCobra.Updater.Models;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace ByteCobra.Updater
{
    public abstract class Downloader
    {
        /// <summary>
        /// Optional authenticator for client authentication.
        /// </summary>
        protected Authenticator? Authenticator { get; }

        public Downloader(
            Action<float> onDownloadProgress, 
            Authenticator? authenticator = null)
        {
            Authenticator = authenticator;
            // Additional initialization logic...
        }
    }
}

To utilize this feature in your Cobra Updater setup, create an instance of Downloader and pass in your custom Authenticator.

Authenticator authenticator = new MyCustomAuthenticator();
Downloader downloader = new WebDownloader(
    baseUrl, 
    serializer, 
    onDownloadProgress, 
    authenticator);

WebDownloader

The WebDownloader class in the Cobra Updater framework is designed to integrate with the Authenticator. By default, it invokes the Authenticator's authentication method before initiating any game downloads:

public override async Task<PackageZipFile?> DownloadAsync(
    string applicationName, 
    Platform platform, 
    ushort major, 
    ushort minor, 
    CancellationToken cancellationToken)
{
    if (Authenticator != null)
    {
        bool authorized = await Authenticator.AuthenticateAsync(cancellationToken);

        Log.Assert(authorized, "Unauthorized", throwException: false);
        return null;
    }
}

If the existing functionality of WebDownloader does not fully meet your specific requirements, or if you need more sophisticated control over the download process, you have the option to create a custom Downloader or WebDownloader. In your custom implementation, you can extend or modify the default behavior, such as implementing additional checks, altering the download logic, or integrating more complex authentication schemes.