简体   繁体   中英

How can I use basic authentication with MobileServiceClient?

I'm using the azure mobile services sdk to do offline sync. I made my api so that it is protected with basic authentication using email and password.

How can I embed these credentials with the MobileServiceClient, so that whenever I call a method it has the correct auth credentials.

this is my existing code for the MobileServiceClient.

var handler = new AuthHandler();

        //TODO 1: Create our client
        //Create our client
        MobileService = new MobileServiceClient(Helpers.Keys.AzureServiceUrl, handler)
        {
            SerializerSettings = new MobileServiceJsonSerializerSettings()
            {
                CamelCasePropertyNames = true
            }
        };

        //assign mobile client to handler
        handler.Client = MobileService;

        MobileService.CurrentUser = new MobileServiceUser(Settings.UserId);
        MobileService.CurrentUser.MobileServiceAuthenticationToken = Settings.AuthToken;

AuthHandler Class

class AuthHandler : DelegatingHandler
{
    public IMobileServiceClient Client { get; set; }
    private static readonly SemaphoreSlim semaphore = new SemaphoreSlim(1);
    private static bool isReauthenticating = false;
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        //Clone the request in case we need to send it again
        var clonedRequest = await CloneRequest(request);
        var response = await base.SendAsync(clonedRequest, cancellationToken);

        //If the token is expired or is invalid, then we need to either refresh the token or prompt the user to log back in
        if (response.StatusCode == HttpStatusCode.Unauthorized)
        {
            if (isReauthenticating)
                return response;

            var service = DependencyService.Get<AzureService>();
            var client = new MobileServiceClient(Helpers.Keys.AzureServiceUrl, null);
            client.CurrentUser = new MobileServiceUser(Settings.UserId);
            client.CurrentUser.MobileServiceAuthenticationToken = Settings.AuthToken;


            string authToken = client.CurrentUser.MobileServiceAuthenticationToken;
            await semaphore.WaitAsync();
            //In case two threads enter this method at the same time, only one should do the refresh (or re-login), the other should just resend the request with an updated header.
            if (authToken != client.CurrentUser.MobileServiceAuthenticationToken)  // token was already renewed
            {
                semaphore.Release();
                return await ResendRequest(client, request, cancellationToken);
            }

            isReauthenticating = true;
            bool gotNewToken = false;
            try
            {

                gotNewToken = await RefreshToken(client);


                //Otherwise if refreshing the token failed or Facebook\Twitter is being used, prompt the user to log back in via the login screen
                if (!gotNewToken)
                {
                    gotNewToken = await service.LoginAsync();
                }
            }
            catch (System.Exception e)
            {
                Debug.WriteLine("Unable to refresh token: " + e);
            }
            finally
            {
                isReauthenticating = false;
                semaphore.Release();
            }


            if (gotNewToken)
            {
                if (!request.RequestUri.OriginalString.Contains("/.auth/me"))   //do not resend in this case since we're not using the return value of auth/me
                {
                    //Resend the request since the user has successfully logged in and return the response
                    return await ResendRequest(client, request, cancellationToken);
                }
            }
        }

        return response;
    }


    private async Task<HttpResponseMessage> ResendRequest(IMobileServiceClient client, HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Clone the request
        var clonedRequest = await CloneRequest(request);

        // Set the authentication header
        clonedRequest.Headers.Remove("X-ZUMO-AUTH");
        clonedRequest.Headers.Add("X-ZUMO-AUTH", client.CurrentUser.MobileServiceAuthenticationToken);

        // Resend the request
        return await base.SendAsync(clonedRequest, cancellationToken);
    }

    private async Task<bool> RefreshToken(IMobileServiceClient client)
    {
        var authentication = DependencyService.Get<IAuthentication>();
        if (authentication == null)
        {
            throw new InvalidOperationException("Make sure the ServiceLocator has an instance of IAuthentication");
        }

        try
        {
            return await authentication.RefreshUser(client);
        }
        catch (System.Exception e)
        {
            Debug.WriteLine("Unable to refresh user: " + e);
        }

        return false;
    }



    private async Task<HttpRequestMessage> CloneRequest(HttpRequestMessage request)
    {
        var result = new HttpRequestMessage(request.Method, request.RequestUri);
        foreach (var header in request.Headers)
        {
            result.Headers.Add(header.Key, header.Value);
        }

        if (request.Content != null && request.Content.Headers.ContentType != null)
        {
            var requestBody = await request.Content.ReadAsStringAsync();
            var mediaType = request.Content.Headers.ContentType.MediaType;
            result.Content = new StringContent(requestBody, Encoding.UTF8, mediaType);
            foreach (var header in request.Content.Headers)
            {
                if (!header.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase))
                {
                    result.Content.Headers.Add(header.Key, header.Value);
                }
            }
        }

        return result;
    }
}

How can I embed these credentials with the MobileServiceClient, so that whenever I call a method it has the correct auth credentials.

Per my understanding, the AuthHandler class could provide a method for setting the current valid user info after the user has successfully logged in with the correct email and password. Also, you need to cache the AuthHandler instance which is used to construct the MobileServiceClient instance, after user logged, you could embed the current user info into the AuthHandler instance.

If you are talking about providing a sign-in process with a username and password rather than using a social provider, you could just follow Custom Authentication for building your CustomAuthController to work with App Service Authentication / Authorization (EasyAuth). For your client, you could use the following code for logging:

MobileServiceUser azureUser = await _client.LoginAsync("custom", JObject.FromObject(account));

Moreover, you need to cache the MobileServiceAuthenticationToken issued by your mobile app backend and manually valid the cached token and check the exp property of the JWT token under the SendAsync method of your AuthHandler class, and explicitly call LoginAsync with the cached user account for acquiring the new MobileServiceAuthenticationToken when the current token would be expired soon or has expired without asking the user to log in again. Detailed code sample, you could follow adrian hall's book about Caching Tokens .

Or if you are talking about Basic access authentication , you could also refer the previous part about embedding credentials into your AuthHandler . For your server-side, you could also add your custom DelegatingHandler to validate the authorization header and set the related Principal to HttpContext.Current.User . And you could initialize your DelegatingHandler under Startup.MobileApp.cs file as follows:

HttpConfiguration config = new HttpConfiguration();
config.MessageHandlers.Add(new MessageHandlerBasicAuthentication());

Moreover, you could follow Basic Authentication Using Message Handlers In Web API .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM