1

I am working in two projects. One is using ASP.NET MVC and another one is using ASP.NET WebAPI. Our client wants to implement load balancing but they're concerned that once it is done, it may affect the login feature, as user's authentication token may not be shared among the servers. I am assigned to research on this issue. Can anyone please tell me is it possible to synchronise and share user's login status and authentication token among the servers for load-balancing purpose? If so, how? Here is the code of Login function in ASP.NET WebAPI project.

    public async Task<IHttpActionResult> Login(LoginModel model)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        // Invoke the "token" OWIN service to perform the login (POST /api/token)
        var requestParams = new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string, string>("grant_type", "password"),
            new KeyValuePair<string, string>("client_id", APP_CLIENT_ID),
            new KeyValuePair<string, string>("username", model.Username),
            new KeyValuePair<string, string>("password", model.Password)
        };

        var requestParamsFormUrlEncoded = new FormUrlEncodedContent(requestParams);
        var baseUrl = apiConfiguration.Host;
        var getTokenUrl = $"{baseUrl}/token";
        var tokenServiceResponse = await httpClient.PostAsync(getTokenUrl, requestParamsFormUrlEncoded);
        if (!tokenServiceResponse.IsSuccessStatusCode)
        {
            // Log response
            if (tokenServiceResponse.StatusCode == HttpStatusCode.NotFound)
            {
                log.ErrorFormat("Can not get token from url: {0}", getTokenUrl);
                return BadRequest(SERVER_ERROR_MESSAGE);
            }
            else
            {
                string content = await tokenServiceResponse.Content.ReadAsStringAsync();
                if (tokenServiceResponse.StatusCode == HttpStatusCode.InternalServerError)
                {
                    log.Error(content);
                    return BadRequest(SERVER_ERROR_MESSAGE);
                }
                else
                {
                    log.InfoFormat("Get token fail request fail | RESPONE - StatusCode: {0} - Reason: {1} - Content: {2}", tokenServiceResponse.StatusCode, tokenServiceResponse.ReasonPhrase, content);
                    return BadRequest(INVALID_USER_DATA_MESSAGE);
                }
            }
        }

        var result = ResponseMessage(tokenServiceResponse);
        var user = await userManager.FindByNameAsync(model.Username);
        if (user == null || user.IsDeleted)
        {
            log.InfoFormat("Invalid user data. Username: {0}", model.Username);
            return BadRequest(INVALID_USER_DATA_MESSAGE);
        }
        else if (user.AccountBlocked == true)
        {
            log.InfoFormat("The account {0} is blocked", model.Username);
            return BadRequest(ACCOUNT_IS_BLOCKED_MESSAGE);
        }

        var userId = user.Id;
        accountNotificationService.LogIn(userId, result.Response.IsSuccessStatusCode, APP_CLIENT_ID);

        return result;
    }

And this is the code of the Login function in ASP.NET MVC project.

    public async Task<IHttpActionResult> Login(LoginModel model)
    {
        if (model == null)
        {
            return this.BadRequest("Invalid user data");
        }
        // Invoke the "token" OWIN service to perform the login (POST /api/token)
        var requestParams = new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string, string>("grant_type", "password"),
            new KeyValuePair<string, string>("client_id", APP_CLIENT_ID),
            new KeyValuePair<string, string>("username", model.Username),
            new KeyValuePair<string, string>("password", model.Password)
        };

        try
        {
            var requestParamsFormUrlEncoded = new FormUrlEncodedContent(requestParams);
            var tokenServiceResponse = await new HttpClient().PostAsync(
                string.Format("{0}/Token", Config.Application.Host), requestParamsFormUrlEncoded);
            var result = this.ResponseMessage(tokenServiceResponse);
            if (!result.Response.IsSuccessStatusCode)
            {
                return this.BadRequest("Credentials are incorrect");
            }
            var user = result.Response.IsSuccessStatusCode ? await accountService.FindUser(model.Username, model.Password) : null;
            if (user == null)
            {
                return this.BadRequest("Credentials are invalid");
            }
            else if (user.IsDeleted)
            {
                return this.BadRequest("This user does not exists");
            }
            if (user.AccountBlocked == true)
            {
                return this.BadRequest("This account is blocked");
            }
            notificationService.LogIn(user?.Id, result.Response.IsSuccessStatusCode, APP_CLIENT_ID);


            return result;
        }
        catch (Exception ex)
        {
            ErrorLogging.LogError(ex, "Error in Login");
            return BasicResponse(new ResultModel
            {
                Success = false,
                ResponseCode = System.Net.HttpStatusCode.InternalServerError,
                Message = Constants.ErrorMessageCategory.General
            });
        }
    }
halfer
  • 19,824
  • 17
  • 99
  • 186
Anthony
  • 1,882
  • 2
  • 9
  • 18
  • [This post](https://stackoverflow.com/questions/21102252/how-are-bearer-tokens-stored-server-side-in-web-api-2) will be helpful. Read the answer**s** and comments as well. Hth.. – EdSF Aug 09 '18 at 04:10

1 Answers1

0

When running machines behind a load balancer, there is usually an Affinity setting on the load balancer which will dictate how "sticky" the sessions are, the load balancer should maintain the connection to the same server depending on these sessions. So depending on how the road balancer is configured, this might not be an issue? particularly if the client (user) sends session info or api tokens on each call.

Here is the a description of the Azure Load Balancer distribution mode https://learn.microsoft.com/en-us/azure/load-balancer/load-balancer-distribution-mode

Mark Redman
  • 24,079
  • 20
  • 92
  • 147