Typically MVC
is not used for API work. While MVC
can handle serving API calls, Microsoft has an MVC
like product called WebApi
that is more geared to serving an API, such as returning JSON or XML instead of a view.
In both MVC
and WebApi
, handling authentication and authorization within methods is not advised. While it works, it opens you up to forgetting to secure an API call. Therefore Microsoft made the AuthenticationFilter
and AuthorizationFilter
attributes. These allow you to mark which APIs or Views to secure by Method, Class, or Application.
To access basic credentials from a request in MVC
, you would access the Authorization header. This will be a string in the form of Basic [Base64Encoded, colon delimited strings]
.
The following code shows a rough example of how to read the credentials:
public ActionResult TestView()
{
bool isAuthenticated;
var base64Header = Request.Headers["Authorization"];
//The header is a string in the form of "Basic [base64 encoded username:password]"
if (base64Header != null)
{
var authHeader = AuthenticationHeaderValue.Parse(base64Header);
if (authHeader != null
&& authHeader.Scheme.Equals("basic", StringComparison.OrdinalIgnoreCase)
&& authHeader.Parameter != null)
{
//Decode the username:password pair
var credentialPair = Encoding.ASCII.GetString(Convert.FromBase64string(authHeader.Parameter));
//Split into pieces
var credentials = credentialPair.Split(new [] {":"}, StringSplitOptions.None);
var userName = credentials[0];
var plainTextPassword = credentials[1];
isAuthenticated = SomeAuthenticator.Authenticate(userName, password);
}
}
if (isAuthenticated)
return Foo();
else
RedirectResult("your login view");
}
It would be best to insert this code into an AuthenticationFilter
however. This grants you the ability to turn authorization on/off by method, class, or application.
public class CustomAuthFilter : IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
var header = filterContext.RequestContext.HttpContext.Request.Headers["Authorization"];
if (!Authenticate(header))
filterContext.Result = new HttpUnauthorizedResult();
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
filterContext.Result = new RedirectResult(@"https:\\foo\YourLoginPage");
}
private bool Authenticate(string rawAuthorizationHeader)
{
try
{
if (rawAuthorizationHeader != null)
{
var authHeader = AuthenticationHeaderValue.Parse(rawAuthorizationHeader);
if (authHeader != null
&& authHeader.Scheme.Equals("basic", StringComparison.OrdinalIgnoreCase)
&& authHeader.Parameter != null)
{
var credentialPair = Encoding.ASCII.GetString(Convert.FromBase64String(authHeader.Parameter));
var credentials = credentialPair.Split(new[] { ":" }, StringSplitOptions.None);
var userName = credentials[0];
var plainTextPassword = credentials[1];
return SomeAuthenticator.Authenticate(userName, plainTextPassword);
}
}
return false;
}
catch (Exception)
{
return false;
}
}
}
And then consumed by
[CustomAuthFilter] //Secure all methods in the class
public class SecureApiController
{
public ActionResult SecuredApiCall()
{
return Foo();
}
public ActionResult AnotherSecuredCall()
{
return Bar();
}
[AllowAnonymous]
public ActionResult UnsecuredApiCall()
{
return UnsecureFoo();
}
}
Note that in typical Microsoft fashion, Authentication and Authorization are two different things. You will need to setup an Authorization filter if you want to secure an API by more than "this user has an account".