0

I'm writing API which will be consumed by mobile devices and I want to secure this API end points.

User authentication details is provide by another application called User Manger API (another project which contains user details).

How to make use of ASP.NET Identity framework Authorization and other features to secure my API endpoints while getting the user data from the User manager API ?

2 Answers 2

1

The question is a bit broad; basically you are looking for a strategy to authenticate and authorise a client for a web api (dotnet core or normal framework?) using a different existing API (is that API in your control, can you modify it if needed?)

If you can modify both, id say look through StackOverflow and Google for JWT tokens, OAuth and identity server.

Sign up to request clarification or add additional context in comments.

1 Comment

Both the project are created in .net technology. The authenticating project (I cannot modify this project) expose endpoint like normal identity framework action methods there are endpoint for token generation,etc.
1

1- you can implement an attribute and decorate your api controller.

2- you can implement and register a global filter inside your asp.net's app_start (and make sure you are registering filters in your global.asax).

3- you can do what #Roel-Abspoel mentions implement Identity Server in your User Manager API and have your client talk to it and get the token, then your API talk to it to validate the token.

There are other ways, but i will keep this short and sweet.

Here is an example using an attribute:

using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Filters;

namespace myExample
{
    public class ExternalAuthenticationAttribute : IAuthenticationFilter
    {
        public virtual bool AllowMultiple
        {
            get { return false; }
        }

        public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
        {
            // get request + authorization headers
            HttpRequestMessage request = context.Request;
            AuthenticationHeaderValue authorization = request.Headers.Authorization;

            // check for username and password (regardless if it was validated on the client, server should check)
            // this will only accept Basic Authorization
            if (String.IsNullOrEmpty(authorization.Parameter) || authorization.Scheme != "Basic")
            {
                // Authentication was attempted but failed. Set ErrorResult to indicate an error.
                context.ErrorResult = new AuthenticationFailureResult("Missing credentials", request);
                return null;
            }
            var userNameAndPasword = GetCredentials(authorization.Parameter);
            if (userNameAndPasword == null)
            {
                // Authentication was attempted but failed. Set ErrorResult to indicate an error.
                context.ErrorResult = new AuthenticationFailureResult("Could not get credentials", request);
                return null;
            }

            // now that we have the username + password call User manager API
            var client = new HttpClient();
            // POST USERNAME + PASSWORD INSIDE BODY, not header, not query string. ALSO USE HTTPS to make sure it is sent encrypted
            var response = AuthenticateAgainstUserMapagerApi1(userNameAndPasword, client);

            // THIS WILL WORK IN .NET CORE 1.1. ALSO USE HTTPS to make sure it is sent encrypted
            //var response = AuthenticateAgainstUserMapagerApi2(client, userNameAndPasword);

            // parse response
            if (!response.IsSuccessStatusCode)
            {
                context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
            }
            else
            {
                var readTask = response.Content.ReadAsStringAsync();
                var content = readTask.Result;
                context.Principal = GetPrincipal(content); // if User manager API returns a user principal as JSON we would 
            }

            return null;
        }

        //private static HttpResponseMessage AuthenticateAgainstUserMapagerApi2(HttpClient client, Tuple<string, string> userNameAndPasword)
        //{
        //    client.SetBasicAuthentication(userNameAndPasword.Item1, userNameAndPasword.Item2);
        //    var responseTask = client.GetAsync("https://your_user_manager_api_URL/api/authenticate");
        //    return responseTask.Result;
        //}

        private static HttpResponseMessage AuthenticateAgainstUserMapagerApi1(Tuple<string, string> userNameAndPasword, HttpClient client)
        {
            var credentials = new
            {
                Username = userNameAndPasword.Item1,
                Password = userNameAndPasword.Item2
            };
            var responseTask = client.PostAsJsonAsync("https://your_user_manager_api_URL/api/authenticate", credentials);
            var response = responseTask.Result;
            return response;
        }

        public IPrincipal GetPrincipal(string principalStr)
        {
            // deserialize principalStr and return a proper Principal instead of ClaimsPrincipal below
            return new ClaimsPrincipal();
        }

        private static Tuple<string, string> GetCredentials(string authorizationParameter)
        {
            byte[] credentialBytes;

            try
            {
                credentialBytes = Convert.FromBase64String(authorizationParameter);
            }
            catch (FormatException)
            {
                return null;
            }

            try
            {
                // make sure you use the proper encoding which match client
                var encoding = Encoding.ASCII;
                string decodedCredentials;
                decodedCredentials = encoding.GetString(credentialBytes);
                int colonIndex = decodedCredentials.IndexOf(':');
                string userName = decodedCredentials.Substring(0, colonIndex);
                string password = decodedCredentials.Substring(colonIndex + 1);
                return new Tuple<string, string>(userName, password);
            }
            catch (Exception ex)
            {
                return null;
            }
        }

        public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
        {
            throw new NotImplementedException();
        }
    }

    public class AuthenticationFailureResult : IHttpActionResult
    {
        public AuthenticationFailureResult(string reasonPhrase, HttpRequestMessage request)
        {
            ReasonPhrase = reasonPhrase;
            Request = request;
        }

        public string ReasonPhrase { get; private set; }

        public HttpRequestMessage Request { get; private set; }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            return Task.FromResult(Execute());
        }

        private HttpResponseMessage Execute()
        {
            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
            response.RequestMessage = Request;
            response.ReasonPhrase = ReasonPhrase;
            return response;
        }
    }
}

use the attribute on your API class like this, which will call User Manager API each time PurchaseController is accessed:

[ExternalAuthenticationAttribute]
public class PurchaseController : ApiController

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.