Quantcast
Channel: Web API
Viewing all articles
Browse latest Browse all 4850

WebApi Basic Authentication not authenticating second time after 401 Challenge

$
0
0

I’ve implemented Basic Authentication via 2 different methods (

I have a fully operational solution on OneDrive (SkyDrive) 
https://onedrive.live.com/embed?cid=E02420377ABA0395&resid=E02420377ABA0395%21437&authkey=AHneWrMqNmtML4c

but will include information for the first method  as it requires the least amount of setup.

 To quickly summarize the problem, both will send a 401 back to the client (Console Application) at which point the Authorization gets added to the header because of

client.Credentials = new NetworkCredential(userName, password);

 and resent.  I can see this in fiddler information but the authentication code(OnAuthorization) is never run again.  It’s like something is intercepting it and automatically NAK’ing the message.

Fiddler picture link: https://onedrive.live.com/?cid=e02420377aba0395&id=E02420377ABA0395%21438&sff=1&authkey=%21AKuVE-ltUC0MKHw&v=3

As an additional test, I tried to add the U/P to the header directly using

Encoding encoding = Encoding.GetEncoding("iso-8859-1");
byte[] data = encoding.GetBytes(userName + ":" + password);
//byte[] data = Encoding.ASCII.GetBytes(userName + ":" + password);

string credentials = Convert.ToBase64String(data);
client.Headers[HttpRequestHeader.Authorization] = string.Format("Basic {0}", credentials);


 and this acted the same way in that it is being intercepted and NAK’ed.

 Any thoughts on how to get it to authentication once the username/password (Authorization: Basic dXNlcjpwYXNzd29yZA==) is added to the header would be MOST appreciated.

Dave


Code and Project

You can create a new project using the following information and dropping in the code below.

  • Visual Studio 2010
  • ASP.NET MVC 4 Web Application   (WebApi)
    • Deleted extraneous stuff (scripts/content/etc)
    • Added {action} to WebApiConfig.cs to look like  routeTemplate: "api/{controller}/{action}/{id}",
    • Web.Config -  <authentication mode="None" />
  • IIS
    • Created Virtual Directory under Default Web Site using Visual Studio in Properties -> Web
    • Changed Authentication for new Virtual Directory
      • Disabled everything
      • Enabled Anonymous Authentication
      • Enabled Basic Authentication  (HTTP 401 Challenge)

Client

using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Runtime.Serialization.Json;
using System.Text;

namespace TestClient
{
	class Program
	{
		private const string server = "localhost";
		private const string url = "http://" + server + "/GenericWebService/api/";
		//private const string url = "http://" + server + "/GenericWebServiceModule/api/";

		const string userName = "user";
		const string password = "password";

		static void Main()
		{
			try
			{
				//
				// WebClient.UploadValues
				//

				PostMethod("values/Save_1");
				//PostMethod("values/Save_2");
				//PostMethod("values/Save_3");
				//PostMethod("values/Save_4", true);
				//Trace.WriteLine("");
				//PostMethod("values/Save_9");

				//
				// WebRequest
				//
				PostMethod_2("values/Save_1");
			}
			catch (Exception e)
			{
				Trace.WriteLine("\n" + e);
			}
		}

		static private WebClient GetClient()
		{
			var client = new WebClient
			{
				BaseAddress = url
			};

			if (true)
				client.Credentials = new NetworkCredential(userName, password);
			else
			{
				Encoding encoding = Encoding.GetEncoding("iso-8859-1");
				byte[] data = encoding.GetBytes(userName + ":" + password);
				//byte[] data = Encoding.ASCII.GetBytes(userName + ":" + password);

				string credentials = Convert.ToBase64String(data);
				client.Headers[HttpRequestHeader.Authorization] = string.Format("Basic {0}", credentials);
			}

			return client;
		}

		static private void PostMethod(string action, bool shouldDeserialize = false)
		{
			
			using (var client = GetClient())
			{
				NameValueCollection values = new NameValueCollection();
				values.Add("text", "Some Text");
				values.Add("value", "500");

				var result = client.UploadValues(action, "POST", values);

				string text = Encoding.ASCII.GetString(result);

				Trace.Write(action + "  >>  ");
				Trace.WriteLine(text);

				if (shouldDeserialize)
				{
					using (MemoryStream stream = new MemoryStream(result))
					{
						DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof (Something));
						Something x = ser.ReadObject(stream) as Something;
						Trace.WriteLine("\t" + x);
					}
				}

			}
		}

		static private void PostMethod_2(string action)
		{
			var request = (HttpWebRequest)HttpWebRequest.Create(url + "values/Save_1");

			request.Credentials = new NetworkCredential(userName, password);
			request.PreAuthenticate = true;
			request.Method = "Post";
			request.ContentType = "application/x-www-form-urlencoded";


			using (StreamWriter sw = new StreamWriter(request.GetRequestStream()))
			{
				sw.Write("text=Some+Text&value=500");
			}

			var response = (HttpWebResponse)request.GetResponse();

			if (response.StatusCode == HttpStatusCode.OK)
			{
				using (StreamReader sr = new StreamReader(response.GetResponseStream()))
				{
					Trace.Write(action + "  >>  ");
				Trace.WriteLine(sr.ReadToEnd());
				}
			}

			response.Close();
		}
	}


	public class Something
	{
		public string text { get; set; }
		public string value { get; set; }

		public override string ToString()
		{
			return string.Format("Text: {0}  Value: {1}", text, value);
		}
	}
}

Custom Authorization

using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Web.Http;
using System.Web.Http.Controllers;

// http://www.dotnet-tricks.com/Tutorial/webapi/E3K8220314-Securing-ASP.NET-Web-API-using-basic-Authentication.html

namespace GenericWebService.Security
{
	public class CustomAuthorizeAttribute : AuthorizeAttribute
	{
	private const string BasicAuthResponseHeader = "WWW-Authenticate";
		private const string BasicAuthResponseHeaderValue = "Basic";

		public string UsersConfigKey { get; set; }
		public string RolesConfigKey { get; set; }

		protected CustomPrincipal CurrentUser
		{
			get { return Thread.CurrentPrincipal as CustomPrincipal; }
			set { Thread.CurrentPrincipal = value; }
		}

		public override void OnAuthorization(HttpActionContext actionContext)
		{
			try
			{
				AuthenticationHeaderValue authValue = actionContext.Request.Headers.Authorization;

				if (authValue != null && !String.IsNullOrWhiteSpace(authValue.Parameter) && authValue.Scheme == BasicAuthResponseHeaderValue)
				{
					Credentials parsedCredentials = ParseAuthorizationHeader(authValue.Parameter);

					if (parsedCredentials != null)
					{
						return;
					}
				}
			}
			catch
			{
			}

			actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
			actionContext.Response.Headers.Add(BasicAuthResponseHeader, BasicAuthResponseHeaderValue);
		}

		protected override bool IsAuthorized(HttpActionContext actionContext)
		{
			return base.IsAuthorized(actionContext);
		}

		private Credentials ParseAuthorizationHeader(string authHeader)
		{
			string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(authHeader)).Split(new[] { ':' });

			if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[1]))
				return null;

			return new Credentials
			{
				Username = credentials[0],
				Password = credentials[1]
			};
		}
	}


	
	public class Credentials
	{
		public string Username { get; set; }
		public string Password { get; set; }
	}



	public class CustomPrincipal : IPrincipal
	{
		public IIdentity Identity { get; private set; }
		public bool IsInRole(string role)
		{
			return roles.Any(r => role.Contains(r));
		}

		public CustomPrincipal(string Username)
		{
			Identity = new GenericIdentity(Username);
		}

		public int UserId { get; set; }
		public string FirstName { get; set; }
		public string LastName { get; set; }
		public string[] roles { get; set; }
	}

}

ValuesController

using System.Net.Http.Formatting;
using System.Web.Http;
using GenericWebService.Security;

namespace GenericWebService.Controllers
{
	[CustomAuthorize]
	public class ValuesController : ApiController
	{
		[HttpPost]
		public string Save_1(FormDataCollection data)
		{
			return string.Format("FormDataCollection - {0}: {1}", data.Get("text"), data.Get("value"));
		}

		[HttpPost]
		public string Save_2(Something data)
		{
			return string.Format("THIS IS A CLASS    - {0}: {1}", data.text, data.value);
		}

		[HttpPost]
		public int Save_3(FormDataCollection data)
		{
			return int.Parse(data.Get("value"));
		}

		[HttpPost]
		public Something Save_4(FormDataCollection data)
		{
			return new Something
			{
				text = data.Get("text") + " x Junk",
				value = data.Get("value") + " x 123"
			};
		}

		[HttpPost]
		public string Save_9([FromBody]string text, [FromBody]string value)
		{
			return string.Format("{0}: {1}", text, value);
		}
	}

	public class Something
	{
		public string text { get; set; }
		public string value { get; set; }
	}
}


Viewing all articles
Browse latest Browse all 4850

Latest Images

Trending Articles



Latest Images

<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>