I have built a .net 4.5 webapi using the rc build. I have done what most people do, I built the api and got it working locally now I am trying to bolt on authentication to work....maybe I should have done this first.
My current requirements only require basic authentication. that can be called via ajax or by building a HttpClient and passing in username and password.
Everything is done over https
So I downloaded the lasted Thinktecture WebApi from git and its a little scary to say the least opening the solution, however I have tried to replicate the basic auth stuff thats provided.
I have created a AuthenicationConfig class within my api
Created a customer handler for authentication the same as the demo project
public class AuthenticationHandler : DelegatingHandler
{
HttpAuthentication _authN;
public AuthenticationHandler(AuthenticationConfiguration configuration)
{
_authN = new HttpAuthentication(configuration);
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (_authN.Configuration.InheritHostClientIdentity == false)
{
SetPrincipal(Principal.Anonymous);
}
try
{
// try to authenticate
// returns an anonymous principal if no credential was found
var principal = _authN.Authenticate(request);
if (principal == null)
{
throw new InvalidOperationException("No principal set");
}
if (principal.Identity.IsAuthenticated)
{
// check for token request - if yes send token back and return
if (_authN.IsSessionTokenRequest(request))
{
return SendSessionTokenResponse(principal);
}
// else set the principal
SetPrincipal(principal);
}
}
catch (SecurityTokenValidationException)
{
return SendUnauthorizedResponse();
}
return base.SendAsync(request, cancellationToken).ContinueWith(
(task) =>
{
var response = task.Result;
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
SetAuthenticateHeader(response);
}
return response;
});
}
private Task<HttpResponseMessage> SendUnauthorizedResponse()
{
return Task<HttpResponseMessage>.Factory.StartNew(() =>
{
var response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
SetAuthenticateHeader(response);
return response;
});
}
private Task<HttpResponseMessage> SendSessionTokenResponse(ClaimsPrincipal principal)
{
var token = _authN.CreateSessionToken(principal);
var tokenResponse = _authN.CreateSessionTokenResponse(token);
return Task<HttpResponseMessage>.Factory.StartNew(() =>
{
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(tokenResponse, Encoding.UTF8, "application/json");
return response;
});
}
protected virtual void SetAuthenticateHeader(HttpResponseMessage response)
{
response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(_authN.Configuration.DefaultAuthenticationScheme));
}
protected virtual void SetPrincipal(ClaimsPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
}
From my client I created a helper to call the api
public static HttpResponseMessage Get(string apiMethod, string baseAddress,string userName,string password)
{
var client = new HttpClient
{
BaseAddress = new Uri(baseAddress),
DefaultRequestHeaders = {Authorization = new BasicAuthenticationHeaderValue(userName, password)}
};
var get = client.GetAsync(apiMethod);
return get.Result;
}
When the api is called I get an error in the SendAsync task it fails at line
var principal = _authN.Authenticate(request);
Throwing an error
[A]System.Net.Http.Headers.AuthenticationHeaderValue cannot be cast to [B]System.Net.Http.Headers.AuthenticationHeaderValue. Type A originates from 'System.Net.Http, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' in the context 'Default'
at location 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\api\7c2ebbea\8447f6b2\assembly\dl3\243549ab\672c3758_b63fcd01\System.Net.Http.dll'. Type B originates from 'System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
in the context 'Default' at location 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Net.Http\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Net.Http.dll'.
I am not sure how to fix this error. Can someone please help.
Thinktecture.identityModel is a library and not a sample. It's meant to provide all the low level painful plumbing and you just sit on top of it.
To that end, you're not expected to create a delegating handler -- there's already one created for you. You would need to reference Thinktecture.identityModel -- preferably from NuGet and not by referencing the VS Thinktecture.identityModel project. Then
you would need to do is replicate the confugration that's done in the ~/Samples/Web API/WebHost project. Check out the global.asax and the App_Start/SecurityConfig.cs files and that's it.
Thanks for your reply. I had nearly done everything right. I had used nuget to add the package, I just went one step to far creating the handler. I remove that and sorted it.
spnz
Member
103 Points
467 Posts
Getting Thinktecture.IdentityModel to work with my webapi
Jul 04, 2012 11:49 PM|LINK
I have built a .net 4.5 webapi using the rc build. I have done what most people do, I built the api and got it working locally now I am trying to bolt on authentication to work....maybe I should have done this first.
My current requirements only require basic authentication. that can be called via ajax or by building a HttpClient and passing in username and password.
Everything is done over https
So I downloaded the lasted Thinktecture WebApi from git and its a little scary to say the least opening the solution, however I have tried to replicate the basic auth stuff thats provided.
I have created a AuthenicationConfig class within my api
public static AuthenticationConfiguration CreateConfiguration() { var config = new AuthenticationConfiguration { DefaultAuthenticationScheme = "Basic", InheritHostClientIdentity = true //EnableSessionToken = true, }; #region BasicAuthentication config.AddBasicAuthentication((userName, password) => userName == password); #endregion return config; }Created a customer handler for authentication the same as the demo project
public class AuthenticationHandler : DelegatingHandler { HttpAuthentication _authN; public AuthenticationHandler(AuthenticationConfiguration configuration) { _authN = new HttpAuthentication(configuration); } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (_authN.Configuration.InheritHostClientIdentity == false) { SetPrincipal(Principal.Anonymous); } try { // try to authenticate // returns an anonymous principal if no credential was found var principal = _authN.Authenticate(request); if (principal == null) { throw new InvalidOperationException("No principal set"); } if (principal.Identity.IsAuthenticated) { // check for token request - if yes send token back and return if (_authN.IsSessionTokenRequest(request)) { return SendSessionTokenResponse(principal); } // else set the principal SetPrincipal(principal); } } catch (SecurityTokenValidationException) { return SendUnauthorizedResponse(); } return base.SendAsync(request, cancellationToken).ContinueWith( (task) => { var response = task.Result; if (response.StatusCode == HttpStatusCode.Unauthorized) { SetAuthenticateHeader(response); } return response; }); } private Task<HttpResponseMessage> SendUnauthorizedResponse() { return Task<HttpResponseMessage>.Factory.StartNew(() => { var response = new HttpResponseMessage(HttpStatusCode.Unauthorized); SetAuthenticateHeader(response); return response; }); } private Task<HttpResponseMessage> SendSessionTokenResponse(ClaimsPrincipal principal) { var token = _authN.CreateSessionToken(principal); var tokenResponse = _authN.CreateSessionTokenResponse(token); return Task<HttpResponseMessage>.Factory.StartNew(() => { var response = new HttpResponseMessage(HttpStatusCode.OK); response.Content = new StringContent(tokenResponse, Encoding.UTF8, "application/json"); return response; }); } protected virtual void SetAuthenticateHeader(HttpResponseMessage response) { response.Headers.WwwAuthenticate.Add(new AuthenticationHeaderValue(_authN.Configuration.DefaultAuthenticationScheme)); } protected virtual void SetPrincipal(ClaimsPrincipal principal) { Thread.CurrentPrincipal = principal; if (HttpContext.Current != null) { HttpContext.Current.User = principal; } } }From my client I created a helper to call the api
public static HttpResponseMessage Get(string apiMethod, string baseAddress,string userName,string password) { var client = new HttpClient { BaseAddress = new Uri(baseAddress), DefaultRequestHeaders = {Authorization = new BasicAuthenticationHeaderValue(userName, password)} }; var get = client.GetAsync(apiMethod); return get.Result; }When the api is called I get an error in the SendAsync task it fails at line
Throwing an error
[A]System.Net.Http.Headers.AuthenticationHeaderValue cannot be cast to [B]System.Net.Http.Headers.AuthenticationHeaderValue. Type A originates from 'System.Net.Http, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' in the context 'Default' at location 'C:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\api\7c2ebbea\8447f6b2\assembly\dl3\243549ab\672c3758_b63fcd01\System.Net.Http.dll'. Type B originates from 'System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' in the context 'Default' at location 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Net.Http\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Net.Http.dll'.
I am not sure how to fix this error. Can someone please help.
BrockAllen
All-Star
28052 Points
4996 Posts
MVP
Re: Getting Thinktecture.IdentityModel to work with my webapi
Jul 05, 2012 01:21 AM|LINK
Thinktecture.identityModel is a library and not a sample. It's meant to provide all the low level painful plumbing and you just sit on top of it.
To that end, you're not expected to create a delegating handler -- there's already one created for you. You would need to reference Thinktecture.identityModel -- preferably from NuGet and not by referencing the VS Thinktecture.identityModel project. Then you would need to do is replicate the confugration that's done in the ~/Samples/Web API/WebHost project. Check out the global.asax and the App_Start/SecurityConfig.cs files and that's it.
Please post again if you have further questions.
DevelopMentor | http://www.develop.com
thinktecture | http://www.thinktecture.com/
spnz
Member
103 Points
467 Posts
Re: Getting Thinktecture.IdentityModel to work with my webapi
Jul 05, 2012 03:03 AM|LINK
Thanks for your reply. I had nearly done everything right. I had used nuget to add the package, I just went one step to far creating the handler. I remove that and sorted it.
Thats got the service authenicating serverside however I am now facing the same problem that @diverdan is having http://forums.asp.net/t/1819085.aspx/1?Configure+basic+auth+with+webapi+rc