How do I logging player out from Unity Player account?

Hello, Forum
I tried writing a login system using the Authentication Unity Player account following some Random YouTube videos. It works perfectly fine except that I am unable to implement the Sign Out system.

I tried using:
AuthenticationService.Instance.SignOut(true);
AuthenticationService.Instance.ClearSessionToken();

These two seem not working as I wish they would and respond with:

PlayerAccountsException: Player is already signed in. Unity.Services.Authentication.PlayerAccounts.PlayerAccountServiceInternal.StartSignInAsync (System.Boolean isSigningUp)

When I try to click on that function and re-login again with the Unity player account.
Is anyone familiar with this problem and have some suggestions that would help me complete this function?

Post your full code. Perhaps you are not awaiting the call properly or not catching and handling exceptions?

SignOut(true) is all you need to run. ClearSessionToken is implied by the true parameter, so you ought to remove that call.

Note however that this will prevent automatic sign-in using anonymous sign in which creates additional friction as the user has to purposefully click on the sign-in service button, or enter username/password, every time the app launches.

Thank you for your response! This is my full code from two script:

public class AuthenticationUnityPlayerAccountsControl : MonoBehaviour
{
    public event Action<bool, PlayerInfo, string> OnSignedIn;
    public event Action<string> OnSignedOut;
    private PlayerInfo playerInfo;

    [SerializeField] private AuthenticationUI authenticationUI;
    async void Awake()
    {
        await UnityServices.InitializeAsync();
        PlayerAccountService.Instance.SignedIn += SignedInWithUnity;
        PlayerAccountService.Instance.SignedOut += () => { Debug.Log("--Signed Out"); };
    }

    private async void SignedInWithUnity()
    {
        try
        {
            var accessToken = PlayerAccountService.Instance.AccessToken;
            await SignInWithUnityAsync(accessToken);
        }
        catch (Exception ex)
        {
            authenticationUI.ShowMessage(ex.Message, AuthenticationUI.ResponseMessageType.error);
            Debug.LogException(ex);
        }
    }

    public async Task InitSignIn()
    {
        await PlayerAccountService.Instance.StartSignInAsync();
    }

    public async Task InitSignOut()
    {
        try
        {
            AuthenticationService.Instance.SignOut(true);
            AuthenticationService.Instance.ClearSessionToken();
            PlayerPrefs.DeleteAll();
            PlayerPrefs.Save();
            Debug.Log("Signed Out");
            OnSignedOut?.Invoke("Signed Out Completed");
        }
        catch (AuthenticationException ex)
        {
            authenticationUI.ShowMessage(ex.Message, AuthenticationUI.ResponseMessageType.error);
            Debug.LogException(ex);
        }
        catch (RequestFailedException ex)
        {
            authenticationUI.ShowMessage(ex.Message, AuthenticationUI.ResponseMessageType.error);
            Debug.LogException(ex);
        }
    }


    async Task SignInWithUnityAsync(string accessToken)
    {
        try
        {
            bool firstTime = false;
            await AuthenticationService.Instance.SignInWithUnityAsync(accessToken);
            Debug.Log("SignIn successfully.");

            playerInfo = AuthenticationService.Instance.PlayerInfo;

            var name = await AuthenticationService.Instance.GetPlayerNameAsync(false);

            if (name == null)
            {
                firstTime = true;
                Debug.Log("First time log in");
            }
            else
            {
                Debug.Log("Not first time log in");
            }

            OnSignedIn?.Invoke(firstTime, playerInfo, name);
        }
        catch (AuthenticationException ex)
        {
            authenticationUI.ShowMessage(ex.Message, AuthenticationUI.ResponseMessageType.error);
            Debug.LogException(ex);
        }
        catch (RequestFailedException ex)
        {
            authenticationUI.ShowMessage(ex.Message, AuthenticationUI.ResponseMessageType.error);
            Debug.LogException(ex);
        }
    }
    private void OnDestroy()
    {
        PlayerAccountService.Instance.SignedIn -= SignedInWithUnity;
    }
}
public class AuthenticationUI : MonoBehaviour
{
    public enum ResponseMessageType { successfully, error, }
    [SerializeField] private Button loginButton;
    [SerializeField] private Button logoutButton;
    [SerializeField] private TMP_Text ResponseMessage;
    [SerializeField] private Transform[] logInPanel, loggedInPanel;
    [SerializeField] private AuthenticationUnityPlayerAccountsControl authenticationUnityPlayerAccountsControl;
    [SerializeField] private float displayMessageDuration = 10f;

    private void OnEnable()
    {
        loginButton.onClick.AddListener(LoginButtonPressed);
        logoutButton.onClick.AddListener(LogOutButtonPressed);
        authenticationUnityPlayerAccountsControl.OnSignedIn += LoginController_OnsignedIn;
        authenticationUnityPlayerAccountsControl.OnSignedOut += LoginController_OnsignedOut;
    }

    private void LoginController_OnsignedIn(bool firstTime, PlayerInfo playerInfo, string playerName)
    {
        foreach (Transform aLogInPanel in logInPanel)
        {
            aLogInPanel.gameObject.SetActive(false);
        }
        foreach (Transform aLoggedInPanel in loggedInPanel)
        {
            aLoggedInPanel.gameObject.SetActive(true);
        }
        ShowMessage($"Welcome <u>{playerName}!", ResponseMessageType.successfully);
        Debug.Log("Player Name: " + playerName + " | Player ID: " + playerInfo.Id);
    }

    private async void LoginButtonPressed()
    {
        await authenticationUnityPlayerAccountsControl.InitSignIn();
    }

    private void LoginController_OnsignedOut(string response)
    {
        foreach (Transform aLogInPanel in logInPanel)
        {
            aLogInPanel.gameObject.SetActive(true);
        }
        foreach (Transform aLoggedInPanel in loggedInPanel)
        {
            aLoggedInPanel.gameObject.SetActive(false);
        }
        Debug.Log(response);
    }

    private async void LogOutButtonPressed()
    {
        authenticationUnityPlayerAccountsControl.InitSignOut();
    }

    public void ShowMessage(string message, ResponseMessageType type)
    {
        switch (type)
        {
            case ResponseMessageType.successfully:
                {
                    ResponseMessage.color = Color.white;
                    break;
                }
            case ResponseMessageType.error:
                {
                    ResponseMessage.color = Color.red;
                    break;
                }
        }
        ResponseMessage.text = message;
        ResponseMessage.gameObject.SetActive(true);
        Invoke("HideErrorMessage", displayMessageDuration);
    }
    private void HideErrorMessage()
    {
        ResponseMessage.gameObject.SetActive(false);
    }
    private void OnDisable()
    {
        loginButton.onClick.RemoveListener(LoginButtonPressed);
        logoutButton.onClick.RemoveListener(LogOutButtonPressed);
        authenticationUnityPlayerAccountsControl.OnSignedIn -= LoginController_OnsignedIn;
    }
}

ClearSessionToken is unnecessary as I pointed out due to the SignOut(true) parameter.
Deleting all playerprefs is just brutal and if you or any asset you may be using in the future stores anything in PlayerPrefs you’ll not necessarily remember that you’re deleting them on sign out. Or why.
Instead, you ought to leave that to whichever script registers for the signed out event, and that script should only delete the specific keys that it is responsible for.

You have to await GetPlayerInfoAsync first or you’ll not get the current player info. It might even be null before calling GetPlayerInfoAsync.

I think that is your problem. You reveice the SignedIn event but SignedInWithUnity then calls await SignInWithUnityAsync(accessToken);

If you weren’t getting the “already signed in” exception you’d actually have an infinite loop here. :wink:

Be sure to debug this as your code executes because that would have been easy to catch with a quick debug session.

Thank you very much. The code now work perfectly :smile:

1 Like

That’s great to hear! :slight_smile:

If you don’t mind me asking more questions.
In this case, I want the user to be able to access the app automatically after the first time logging in with a Unity account, how do I do?

Simply don’t clear the session token because that’s what this token is used for: to restore the player’s identity.

If a session token exists then the next time you sign in anonymously it will sign in the player using the previously used credentials.

This means either the linked account or if the account isn’t linked it will sign in the same anonymous player so that player retains progress and can still link the account with an ID provider.

Hi, I am having the similar issue, I am not sure how to resolve it. I am getting the same error after logging out and then trying to log in again. These are my 3 relevant scripts

using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.Services.Authentication;
using UnityEngine;
using UnityEngine.UI;

public class UILogin : MonoBehaviour
{
    [SerializeField] private Button loginButton;
   // [SerializeField] private TMP_Text userIdText;
    [SerializeField] private Transform loginPanel, introPanel;
    [SerializeField] private LoginController loginController;

    private void OnEnable()
    {
        loginButton.onClick.AddListener(LoginButtonPressed);
        loginController.OnSingedIn += loginController_OnSingedIn;
    }

    private void loginController_OnSingedIn(PlayerInfo playerInfo, string playerName)
    {
        loginPanel.gameObject.SetActive(false);
        introPanel.gameObject.SetActive(true);

     //   userIdText.text = $"id_{playerInfo.Id}";
        Debug.Log(playerName);
    }

    private async void LoginButtonPressed()
    {
        await loginController.InitSignIn();
    }

    private void OnDisable()
    {
        loginButton.onClick.RemoveListener(LoginButtonPressed);
        loginController.OnSingedIn -= loginController_OnSingedIn;

    }
}
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Services.Authentication;
using Unity.Services.Authentication.PlayerAccounts;
using Unity.Services.Core;
using UnityEngine;
using System.Threading.Tasks;

public class LoginController : MonoBehaviour
{
    //Login i cloud save
    public static string[] collectedStickers = { };
    public static int brojPackova = 3;

    public event Action<PlayerInfo, string> OnSingedIn;
    private PlayerInfo playerInfo;

    async void Awake()
    {
        await UnityServices.InitializeAsync();
        PlayerAccountService.Instance.SignedIn += SignedIn;
        if (PlayerPrefs.HasKey("PlayerToken"))
        {
            await SignInWithUnityAsync(PlayerPrefs.GetString("PlayerToken"));
     
        }
    }

    public async void SignedIn()
    {
        try
        {
            var accessToken = PlayerAccountService.Instance.AccessToken;
            PlayerPrefs.SetString("PlayerToken", accessToken);
            PlayerPrefs.Save();
            await SignInWithUnityAsync(accessToken);
        }
        catch (Exception ex)
        {

        }
    }

    public async Task InitSignIn()
    {
        await PlayerAccountService.Instance.StartSignInAsync();
    }

    async Task SignInWithUnityAsync(string accessToken)
    {
        try
        {
            await AuthenticationService.Instance.SignInWithUnityAsync(accessToken);
            Debug.Log("SignIn is successful.");

            playerInfo = AuthenticationService.Instance.PlayerInfo;

            var name = await AuthenticationService.Instance.GetPlayerNameAsync();

            OnSingedIn?.Invoke(playerInfo, name);

            //pozvati load
            try
            {
                GetComponent<CloudSave>().LoadData();
            } catch (Exception ex)
            {
                Debug.Log(ex);
            }
        }
        catch (AuthenticationException ex)
        {
            // Compare error code to AuthenticationErrorCodes
            // Notify the player with the proper error message
            Debug.LogException(ex);
        }
        catch (RequestFailedException ex)
        {
            // Compare error code to CommonErrorCodes
            // Notify the player with the proper error message
            Debug.LogException(ex);
        }
    }

    private void OnDestroy()
    {
        Debug.Log("Destroyed");
        PlayerAccountService.Instance.SignedIn -= SignedIn;
    }
}
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Services.Authentication;
using Unity.Services.Authentication.PlayerAccounts;
using Unity.Services.Core;
using UnityEngine;
using System.Threading.Tasks;
using UnityEngine.SceneManagement;

public class LogOut : MonoBehaviour
{
    public void Logout()
    {
        AuthenticationService.Instance.SignOut(true);
        PlayerPrefs.DeleteKey("PlayerToken");
        PlayerPrefs.Save();
        Debug.Log("User logged out and token deleted.");
        SceneManager.LoadScene("Login");
    }
}

LogOut script is connected to a button that is in another scene.

I’m searching for a unity package that’ll log other users out of a project, can I be recommended one?

To make sure I understand your request correctly, are you asking whether it’s possible to log out all players using the Client API or the Admin API?

If you could share a bit more about your use case and what you’re hoping to accomplish, we’ll be better able to guide you toward the best solution