Where do I handle custom authentication and authorization in webapi? - authentication

NOTE: I have many questions littered in the code comments below. I need answers to those as well.
I have read (among many others) the following articles:
http://blogs.msdn.com/b/hongmeig1/archive/2012/05/11/how-to-write-a-custom-parameter-binding-to-construct-an-object-either-from-body-or-from-uri-s-query.aspx
http://blogs.msdn.com/b/jmstall/archive/2012/05/11/webapi-parameter-binding-under-the-hood.aspx
I would like for my web api to have authentication sent in the header using the Authorization header. I would like this header to be populated into a c# class called AuthenticationToken. Then when I am doing parameter binding I would like to retreive this previously created AuthenticationToken object and pass it on to my controller action. For example, if I have the following controller
public class MyServiceController : ApiController {
readonly ISecurityService _security;
readonly IMyService _myService;
// constructor values are injected
public MyServiceController(ISecurityService security, IMyService myService) {
_security = security;
_myService = myService;
}
public SomeData GetASpecificItem(AuthenticationToken token, int id) {
if (_security.IsAuthorized(token, Permissions.Read)) {
return myService.DoStuffToGetSomeData(token);
} else {
var msg = new HttpResponseMessage(HttpStatusCode.Forbidden);
throw new HttpResponseException(msg);
}
}
}
and the following parameter binding class
public class AuthenticationTokenParameterBinding
: HttpParameterBinding { // do I need to inherit from a different class?
public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
HttpActionContext actionContext,
CancellationToken cancellationToken) {
try {
AuthenticationToken token; // UPDATED: how can i get this from the data
// available from inside this method?
SetValue(actionContext, token);
// is this the correct task to return on successfull parameter binding?
return base.ExecuteBindingAsyn(metadataProvider, actionContext, cancellationToken);
} catch {
return Task<HttpResponseMessage>.Factory.StartNew(() => {
var hpm = new HttpResponseMessage(HttpStatusCode.Unauthorized);
hpm.Headers.Add("WWW-Authenticate","MyCustomScheme");
return hpm;
});
}
}
}
If these two are implemented correctly, then the controller will automatically get the AuthenticationToken instance that was created during authorization.
I do not know where to authenticate ahead of this process. Nor do I know how to pass an object between authentication and authorization.
UPDATE:
I can't use a custom AuthorizeAttribute because authorization may be against an object:
public SaveResponse Save(AuthenticationToken user, SomeObjectThatNeedsToBeSaved obj) {
// NOTE: permissions are checked between the object and the user, not a role
if (_security.IsAuthorized(user, obj, Permission.Modify, Permission.Create)) {
// NOTE: other permissions we don't know about may need to be checked in the service call
return new SaveResponse {
Success = ISomeService.Save(user, obj); // bool return value
}
} else {
// return 403 Forbidden }
}
I need to pass the token to the controller action, but I also need to authenticate the token before it gets passed to the controller. Since all of this is not necessarily role based, I don't see how I can authenticate from inside of a custom AuthorizeAttribute

I have used a custom AuthorizeAttribute to handle both authentication and authorization for Web API. This attribute works as a filter and will process the request before it gets to your Web API method. In the overridden OnAuthorize method you can return HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized) if authentication fails and HttpResponseMessage(System.Net.HttpStatusCode.Forbidden) if authorization fails so that the client can distinguish between both types of errors. In addition to the custom AuthorizeAttribute I implemented a custom MembershipProvider and RoleProvider to handle my specific security requirements and custom database schema.
I use basic authentication to pass the credentials for authorization. This puts the credentials in the header. To do this is pretty straight forward by using the beforeSend event handler of the JQuery ajax function. Here is an example of how to do this.
getAuthorizationHeader = function (username, password) {
var authType;
var up = $.base64.encode(username + ":" + password);
authType = "Basic " + up;
};
return authType;
};
$.ajax({
url: _url,
data: _data,
type: _type,
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", getAuthorizationHeader(username, password));
},
success: ajaxSuccessHandler,
error: ajaxErrHandler
});
This encodes the username/password that is sent in the header. Note that this is not enough security to rely on just the encoding as it is easy to decode. You still want to use HTTPS/SSL to make sure the information sent over the wire is secure.
On the Web API side you can make a custom AuthorizeAttribute that gets the credentials from the header, decodes them, and performs your authorization process. There is a separate AuthorizeAttribute used by the Web API as opposed to the controller. Be sure to use System.Web.Http.AuthorizeAttribute as your base class when creating your custom AuthorizeAttribute. They have different behaviors. The one for the controller will want to redirect to the logon page whereas the one for the Web API returns an HTTP code indicating success or failure. I return an HTTP code of Forbidden if authorization fails to distinguish a failure due to authorization as opposed to authentication so the client can react accordingly.
Here is an example method for getting the credentials from the header that can be used in the custom AuthorizeAttribute.
private bool GetUserNameAndPassword(HttpActionContext actionContext, out string username, out string password)
{
bool gotIt = false;
username = string.Empty;
password = string.Empty;
IEnumerable<string> headerVals;
if (actionContext.Request.Headers.TryGetValues("Authorization", out headerVals))
{
try
{
string authHeader = headerVals.FirstOrDefault();
char[] delims = { ' ' };
string[] authHeaderTokens = authHeader.Split(new char[] { ' ' });
if (authHeaderTokens[0].Contains("Basic"))
{
string decodedStr = SecurityHelper.DecodeFrom64(authHeaderTokens[1]);
string[] unpw = decodedStr.Split(new char[] { ':' });
username = unpw[0];
password = unpw[1];
}
gotIt = true;
}
catch { gotIt = false; }
}
return gotIt;
}
And here is the code for decoding the header data that is used in this method.
public static string DecodeFrom64(string encodedData)
{
byte[] encodedDataAsBytes
= System.Convert.FromBase64String(encodedData);
string returnValue =
System.Text.Encoding.ASCII.GetString(encodedDataAsBytes);
return returnValue;
}
Once you have the username and password you can perform your authorization process and return the appropriate HTTP code to the client for handling.
You could perform the a similar process with your custom token, or you can leverage the cookie that is passed back and forth if you do not want to keep the password/username stored in the client.

Related

How to implement custom ValidateAntiforgeryTokenAuthorizationFilter in ASP.NET Core 3.1

I'd like to implement a filter that skips validation of an antiforgery token when an auth token authentication (Bearer) is used.
In the ASP.NET Core 2.2 the ValidateAntiforgeryTokenAuthorizationFilter and AutoValidateAntiforgeryTokenAuthorizationFilter were public (even though living in the Microsoft.AspNetCore.Mvc.ViewFeatures.Internal namespace), so I was able to just inherit from the latter and override the ShouldValidate method easily.
In the ASP.NET Core 3.0 they became internal, so it's not possible to just inherit from them. I can just copy-paste the code, but it's not the ideal solution obviously.
I was following the Prevent Cross-Site Request Forgery (XSRF/CSRF) attacks in ASP.NET Core article from MSDN, but it doesn't really mention anything relevant to my scenario.
Normally you can use [IgnoreAntiforgeryToken] attribute if you can determine at compile-time that the csrf token should be ignored. If you want such an ability at run-time, you could create a custom FilterProvider that will provide an IAntiforgeryPolicy if there's a Authroization: Bearer json-web-token header.
For example, we can create a custom AutoSkipAntiforgeryFilterProvider as below:
public class AutoSkipAntiforgeryFilterProvider: IFilterProvider
{
private const string BEARER_STRING = "Bearer";
public int Order => 999;
public void OnProvidersExecuted(FilterProviderContext context) { }
public void OnProvidersExecuting(FilterProviderContext context)
{
if (context == null) { throw new ArgumentNullException(nameof(context)); }
if (context.ActionContext.ActionDescriptor.FilterDescriptors != null)
{
var headers = context.ActionContext.HttpContext.Request.Headers;
if (headers.ContainsKey("Authorization"))
{
var header = headers["Authorization"].FirstOrDefault();
if(header.StartsWith(BEARER_STRING,StringComparison.OrdinalIgnoreCase))
{
var FilterDescriptor = new FilterDescriptor(SkipAntiforgeryPolicy.Instance, FilterScope.Last);
var filterItem = new FilterItem( FilterDescriptor,SkipAntiforgeryPolicy.Instance);
context.Results.Add(filterItem);
}
}
}
}
// a dummy IAntiforgeryPolicy
class SkipAntiforgeryPolicy : IAntiforgeryPolicy, IAsyncAuthorizationFilter
{
// a singleton
public static SkipAntiforgeryPolicy Instance = new SkipAntiforgeryPolicy();
public Task OnAuthorizationAsync(AuthorizationFilterContext context) => Task.CompletedTask;
}
}
And register this filter provider in Startup :
services.TryAddEnumerable( ServiceDescriptor.Singleton<IFilterProvider, AutoSkipAntiforgeryFilterProvider>());
Now it will bypass the AntiForgery even there's a [ValidateAntiForgeryToken]attribute.
[Demo]
Assume we have an action method annotated with [ValidateAntiForgeryToken]:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Name")] XModel xModel)
{
....
}
Normally, it will protect this method with CSRF token. But if you send a request like:
POST /XModels/Create HTTP/1.1
Authorization: Bearer Xyz
Content-Type: application/x-www-form-urlencoded
...
it won't validate the csrf token.

Custom Authorization Filter in .NET Core API

I want to authorize users before accessing any data using my core api, so I tried is using JWT authentication.
I have successfully generated token while signing in user using api and saved that token on client side in session, now whenever user wants to access any data using api, I'll send that token in header to api and I want to validate that JWT token using custom authorization filter. I have created custom authorization filter and applied it on my GetMenu api and I'm able to validate that token successfully but after token validation in authorization filter it is not hitting it on my GetMenu api.
Here is my AccountController code:
[Filters.Authorization]
[AllowAnonymous]
[HttpPost]
[Route("GetMenu")]
public IActionResult GetMenu(string clientid, int rolecode, string repcode)
{
//further process
}
Here is my Filters.Authorization code:
public class Authorization: AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
if (!ValidateToken(filterContext.HttpContext.Request.Headers["token"]))
{
filterContext.Result = new UnauthorizedResult();
}
}
}
I have breakpoints on OnAuthorization method and on GetMenu api.
I'm calling my GetMenu api through postman to test, it is successfully hitting it on OnAuthorization method in Filters.Authorization and validating my JWT Token and displays Status Code: 200 in postman but after successful token validation it should hit on GetMenu api for further data processing but it is not hitting.
What can be the issue? what am i missing? please help.
You should not set the filterContext.Result if the request is successfully authorize.
//
// Summary:
// A context for authorization filters i.e. Microsoft.AspNetCore.Mvc.Filters.IAuthorizationFilter
// and Microsoft.AspNetCore.Mvc.Filters.IAsyncAuthorizationFilter implementations.
public class AuthorizationFilterContext : FilterContext
{
//
// Summary:
// Gets or sets the result of the request. Setting Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext.Result
// to a non-null value inside an authorization filter will short-circuit the remainder
// of the filter pipeline.
public virtual IActionResult Result { get; set; }
}
You only need to set Result when it's failed.
public class Authorization: AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext filterContext)
{
if (!ValidateToken(filterContext.HttpContext.Request.Headers["token"]))
{
filterContext.Result = new UnauthorizedResult();
}
}
}

Adding RequestFilter data to Context (Request Scope), Retrieve in Service

I implemented Basic Auth for my services. Since ServiceStack's AuthFeature is strongly coupled with the session concept, I implemented a custom RequestFilter that performs stateless basic auth (credentials go in, on every request). Our auth strategy internally contemplates roles and permissions.
Besides authentication, we need to enforce authorization (e.g., user is manipulating a product that he owns). We are using FluentValidation, for all service validations.
Authorization validations include cross checking auth data with request parameters. Question is, where should I place the auth data produced in the BasicAuthRequestFilter? Should I key value pair it in cache, associating, for instance, RequestContext (or any other object that uniquely identifies the request scope) with an Authentication object?
I could just plug the AuthData in the Request Dto, which is available directly at the RequestFilter, however this would mess up our service contract design. We define dtos in a separate DLL, where only service input/output details are defined.
Any suggestions?
Thanks in advance.
I too use my own custom authentication mechanism and make custom role information available to my service. I do this by authenticating the request in a custom ServiceRunner which can then pass the information directly to my custom Service base. This ultimately means accessing information about the users permissions is exceptionally easy.
Create a custom ServiceRunner:
public class ServiceRunner<T> : ServiceStack.ServiceHost.ServiceRunner<T>
{
public ServiceRunner(IAppHost appHost, ActionContext actionContext) : base(appHost, actionContext)
{
}
public override object Execute(IRequestContext requestContext, object instance, T request)
{
// Check if the instance is of type AuthenticatedBase
var authenticatedBase = instance as AuthenticatedBase;
// If the request is not using the AuthenticatedBase, then allow it to run, as normal.
if(authenticatedBase == null)
return base.Execute(requestContext, instance, request);
/*
* Authentication required. Do you authorization check here.
* i.e.
* var authorization = requestContext.GetHeader("Authorization");
* bool authorised = ... some condition;
*/
/* You have access to your service base so if you injected the Db connection
* in you app config using IoC, then you can access the Db here.
* i.e.
* authenticatedBase.Db
*/
/*
* Not authorized?
* throw new UnauthorizedException();
*/
/*
* If authorized:
* Then simple set the details about their permissions
*/
authenticatedBase.AuthData = new AuthData { Id = 123, Roles = [], Username = "" };
// Pass back the authenticated base
return base.Execute(requestContext, authenticatedBase, request);
}
}
Configure you application to use it by adding this to your AppHost:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
return new ServiceRunner<TRequest>(this, actionContext);
}
Create a custom class to hold your auth data i.e. the user session information, such as:
public class AuthData
{
public int Id { get; set; }
public string Username { get; set; }
public int[] Roles { get; set; }
...
}
Then create a custom service base
public class AuthenticatedBase : Service
{
public AuthData AuthData { get; set; }
}
To then use the AuthData in the service is simply a case of extending AuthenticatedBase.
public class CustomerHandler : AuthenticatedBase
{
public object Get(ListCustomers request)
{
// You can access the AuthData now in the handler
var roles = AuthData.Role; // Check they have the role required to list customers
...
}
}
You are probably wondering why go to all the trouble of using the ServiceRunner over a RequestFilter but the main advantage is it gives direct access to the instance of the Service base, which isn't available to a RequestFilter.
The RequestFilters are run before the Service base is instantiated, so you can't populate it from there. See order of operations for more information.
By having access to the ServiceBase we can populate values (in this case AuthData) and we have access to our injected dependancies such as the database connection.
I hope you find this useful. You should be able to copy most of your existing RequestFilter into the service runner. If you need any further help with this just let me know.
Update to support Attributes:
Since you are unable to avoid using the attribute method to handle your authentication needs you can still use this method:
Continue doing your authentication and access filtering the way you were before.
In your existing authentication mechanism use req.Items.Add to set the AuthData i.e. Where req is your request object
req.Items.Add("AuthData", new AuthData { Username = "", Roles = [] ... });
Then access your AuthData item in your service base:
public class AuthenticatedBase : Service
{
public AuthData AuthData
{
get { return base.Request.Items["AuthData"] as AuthData; }
}
}

Intercepting an encrypted login token in a request

I am working on an MVC site that has some pages that need authentication and others that don't. This is determined using the Authorize and AllowAnonymous attributes in a pretty standard way. If they try to access something restricted they get redirected to the login page.
I'm now wanting to add the functionality to automatically log them in using an encrypted token passed in the querystring (the link will be in emails sent out). So the workflow I want now is that if a request goes to a page that is restricted and there is a login token in the querystring I want it to use that token to log in. If it logs in successfully then I want it to run the original page requested with the new logged in context. If it fails to log in then it will redirect to a custom error page.
My question is where would I need to insert this logic into the site?
I have seen some suggestions on subclassing the Authorize attribute and overriding some of the methods but I'm not 100% sure how to go about this (eg what I would override and what I'd do in those overridden methods.
I've also had a look at putting the logic at a controller level but I am led to understand that the authorize attribute would redirect it away from the controller before any code in the controller itself was run.
It would be better to write a custom authorization attribute that will entirely replace the default functionality and check for the query string parameter and if present, decrypt it and authenticate the user. If you are using FormsAuthentication that would be to call the FormsAuthentication.SetAuthCookie method. Something along the lines of:
public class TokenAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
string token = filterContext.HttpContext.Request["token"];
IPrincipal user = this.GetUserFromToken(token);
if (user == null)
{
this.HandleUnAuthorizedRequest(filterContext);
}
else
{
FormsAuthentication.SetAuthCookie(user.Identity.Name, false);
filterContext.HttpContext.User = user;
}
}
private IPrincipal GetUserFromToken(string token)
{
// Here you could put your custom logic to decrypt the token and
// extract the associated user from it
throw new NotImplementedException();
}
private void HandleUnAuthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new ViewResult
{
ViewName = "~/Views/Shared/CustomError.cshtml",
};
}
}
and then you could decorate your action with this attribute:
[TokenAuthorize]
public ActionResult ProcessEmail(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}

SimpleMembership in MVC4 app + WebApi using basic HTTP auth

I'm trying to implement an MVC4 web application with the following requirements:
(a) it offers its services to authenticated users only. As for authentication, I'd like to use simple membership, as it is the latest authentication technique from MVC, gives me the advantage of defining my own db tables, provides OAuth support out of the box, and is easily integrated with both MVC and WebApi.
(b) it exposes some core functions via WebApi for mobile/JS clients, which should be authenticated via basic HTTP authentication (+SSL). Typically I'll have JS clients using jQuery AJAX calls to WebApi controllers, decorated with the Authorize attribute for different user roles.
(c) ideally, in a mixed environment I would like to avoid a double authentication: i.e. if the user is already authenticated via browser, and is visiting a page implying a JS call to a WebApi controller action, the (a) mechanism should be enough.
Thus, while (a) is covered by the default MVC template, (b) requires basic HTTP authentication without the mediation of a browser. To this end, I should create a DelegatingHandler like the one I found in this post: http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers.
The problem is that its implementation requires some way of retrieving an IPrincipal from the received user name and password, and the WebSecurity class does not provide any method for this (except Login, but I would avoid changing the logged user just for the purpose of authorization, also because of potential "mixed" environments like (c)). So it seems my only option is giving up simple membership. Does anyone have better suggestions? Here is the relevant (slightly modified) code from the cited post:
public interface IPrincipalProvider
{
IPrincipal GetPrincipal(string username, string password);
}
public sealed class Credentials
{
public string Username { get; set; }
public string Password { get; set; }
}
public class BasicAuthMessageHandler : DelegatingHandler
{
private const string BasicAuthResponseHeader = "WWW-Authenticate";
private const string BasicAuthResponseHeaderValue = "Basic";
public IPrincipalProvider PrincipalProvider { get; private set; }
public BasicAuthMessageHandler(IPrincipalProvider provider)
{
if (provider == null) throw new ArgumentNullException("provider");
PrincipalProvider = provider;
}
private static Credentials ParseAuthorizationHeader(string sHeader)
{
string[] credentials = Encoding.ASCII.GetString(
Convert.FromBase64String(sHeader)).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],
};
}
protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
AuthenticationHeaderValue authValue = request.Headers.Authorization;
if (authValue != null && !String.IsNullOrWhiteSpace(authValue.Parameter))
{
Credentials parsedCredentials = ParseAuthorizationHeader(authValue.Parameter);
if (parsedCredentials != null)
{
Thread.CurrentPrincipal = PrincipalProvider
.GetPrincipal(parsedCredentials.Username, parsedCredentials.Password);
}
}
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
var response = task.Result;
if (response.StatusCode == HttpStatusCode.Unauthorized
&& !response.Headers.Contains(BasicAuthResponseHeader))
{
response.Headers.Add(BasicAuthResponseHeader,
BasicAuthResponseHeaderValue);
}
return response;
});
}
}
Here is another solution that meets all of your requirements. It uses SimpleMemberhsip with a mix of forms authentication and basic authentication in an MVC 4 application. It can also support Authorization, but it is not required by leaving the Role property null.
Thank you, this seems the best available solution at this time!
I managed to create a dummy solution from scratch (find it here: http://sdrv.ms/YpkRcf ), and it seems to work in the following cases:
1) when I try to access an MVC controller restricted action, I am redirected to the login page as expected to get authenticated.
2) when I trigger a jQuery ajax call to a WebApi controller restricted action, the call succeeds (except of course when not using SSL).
Yet, it does not work when after logging in in the website, the API call still requires authentication. Could anyone explain what's going here? In what follows I detail my procedure as I think it might be useful for starters like me.
Thank you (sorry for the formatting of what follows, but I cannot manage to let this editor mark code appropriately...)
Procedure
create a new mvc4 app (basic mvc4 app: this already comes with universal providers. All the universal providers class names start with Default...);
customize web.config for your non-local DB, e.g.:
<connectionStrings>
<add name="DefaultConnection"
providerName="System.Data.SqlClient"
connectionString="data source=(local)\SQLExpress;Initial Catalog=Test;Integrated Security=True;MultipleActiveResultSets=True" />
Also it's often useful to set a machineKey for hashing passwords, so that you can freely move this site around from server to server without having your passwords scrambled. Use a machine key generator website to define an entry like this:
<system.web>
<machineKey
validationKey="...thekey..."
decryptionKey="...thekey..."
validation="SHA1"
decryption="AES" />
if required create a new, empty database corresponding to the connection string of your web.config. Then start our good old pal WSAT (from VS Project menu) and configure security by adding users and roles as required.
if you want to, add a HomeController with an Index action, because no controller is present in this template and thus you could not test-start your web app without it.
add Thinktecture.IdentityModel.45 from NuGet and add/update all your favorite NuGet packages. Notice that at the time of writing this, jquery validation unobtrusive from MS is no more compatible with jQuery 1.9 or higher. I rather use http://plugins.jquery.com/winf.unobtrusive-ajax/ . So, remove jquery.unobtrusive* and add this library (which consists of winf.unobtrusive-ajax and additional-methods) in your bundles (App_Start/BundleConfig.cs).
modify the WebApiConfig.cs in App_Start by adding it the code after the DefaultApi route configuration:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// added for Thinktecture
var authConfig = new AuthenticationConfiguration
{
InheritHostClientIdentity = true,
ClaimsAuthenticationManager = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager
};
// setup authentication against membership
authConfig.AddBasicAuthentication((userName, password) => Membership.ValidateUser(userName, password));
config.MessageHandlers.Add(new AuthenticationHandler(authConfig));
}
}
To be cleaner, the api controllers will be placed under Controllers/Api/, so create this folder.
Add to models a LoginModel.cs:
public class LoginModel
{
[Required]
[Display(Name = "UserName", ResourceType = typeof(StringResources))]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Password", ResourceType = typeof(StringResources))]
public string Password { get; set; }
[Display(Name = "RememberMe", ResourceType = typeof(StringResources))]
public bool RememberMe { get; set; }
}
This model requires a StringResources.resx resource (with code generation) I usually place under an Assets folder, with the 3 strings quoted in the attributes.
Add a ClaimsTransformer.cs to your solution root, like this:
public class ClaimsTransformer : ClaimsAuthenticationManager
{
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (!incomingPrincipal.Identity.IsAuthenticated)
{
return base.Authenticate(resourceName, incomingPrincipal);
}
var name = incomingPrincipal.Identity.Name;
return Principal.Create(
"Custom",
new Claim(ClaimTypes.Name, name + " (transformed)"));
}
}
Add Application_PostAuthenticateRequest to Global.asax.cs:
public class MvcApplication : HttpApplication
{
...
protected void Application_PostAuthenticateRequest()
{
if (ClaimsPrincipal.Current.Identity.IsAuthenticated)
{
var transformer = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager;
var newPrincipal = transformer.Authenticate(string.Empty, ClaimsPrincipal.Current);
Thread.CurrentPrincipal = newPrincipal;
HttpContext.Current.User = newPrincipal;
}
}
}
web.config (replace YourAppNamespace with your app root namespace):
<configSections>
<section name="system.identityModel"
type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
...
add the other models for account controller, with their views (you can derive them from MVC3 application template, even if I prefer changing them to more localizable-friendly variants using attributes requiring string resource names rather than literals).
to test browser-based authentication, add some [Authorized] action to a controller (e.g. HomeController), and try accessing it.
to test basic HTTP authentication, insert in some view (e.g. Home/Index) a code like this (set your user name and password in the token variable):
...
<p>Test call
$(function() {
$("#test").click(function () {
var token = "USERNAME:PASSWORD";
var hash = $.base64.encode(token);
var header = "Basic " + hash;
console.log(header);
$.ajax({
url: "/api/dummy",
dataType: "json",
beforeSend: function(xhr) {
xhr.setRequestHeader("Authorization", header);
},
success: function(data) {
alert(data);
},
error: function(jqXHR, textStatus, errorThrown) {
alert(errorThrown);
}
});
});
});
This requires the jQuery plugin for encoding/decoding Base64: jquery.base64.js and its minified counterpart.
To allow SSL, follow the instructions here: http://www.hanselman.com/blog/WorkingWithSSLAtDevelopmentTimeIsEasierWithIISExpress.aspx (basically, enable SSL in the web project properties and connect to the port specified in the property value).
Maybe this helps - sounds this is like your scenario:
http://leastprivilege.com/2012/10/23/mixing-mvc-forms-authentication-and-web-api-basic-authentication/
http://leastprivilege.com/2012/10/24/extensions-to-the-web-apimvc-formsbasic-auth-sample-claims-transformation-and-ajax/