Basic authentication on a remote server - c#

I need some help with ASMX web-services.
Let's suppose I have a ServerService which provides some data. Let's suppose it has a method GetRandomInteger which returns a random integer (obviously). It implements a custom basic authentication using IHttpModule.
public class BasicAuthHttpModule : IHttpModule
{
private UserRepository _userRepository;
public void Dispose()
{
}
public void Init(HttpApplication application)
{
_userRepository = new UserRepository();
application.AuthenticateRequest += OnAuthenticateRequest;
application.EndRequest += OnEndRequest;
}
public void OnAuthenticateRequest(object source, EventArgs e)
{
var app = (HttpApplication)source;
string authHeader = app.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authHeader))
{
// Here I successfully get credentials from header
if (_userRepository.ValidateUser(username, password)) return;
// Return 401 and CompleteRequest
}
else
{
// Return 401 and End
}
}
public void OnEndRequest(object source, EventArgs eventArgs)
{
if (HttpContext.Current.Response.StatusCode == 401)
{
// Return 401 and require new authorization
}
}
Fortunately, it works. Now I can successfully open Service.asmx file, get basic authentication window and get access to it's GetRandomInteger method after successful authentication.
Now I have an ASP.NET MVC 4 application called ClientService. It must provide user interface with convenient and appropriate access to methods of ServerService. Now it has default controllers like Account and Home, default views etc.
I need this ClientService to authenticate on a ServerService. I mean there will be a Home/Index page with button "Login". I enter login and password there and ClientService tries to authenticate at ServerService. It returns error on fail or authenticates on success providing access to some Home/RandomInt page which will show the integer requested from ServerService. What is the best and the easiest way to do this?
How to implement registration on a ServerService? There is no AllowAnonymous attribute or something at ASMX, so I can't register user because he doesn't have access to any of methods due to 401 error.
Thank you in advance.
P.S. No. I can't use WCF or something else. I need to implement an ASMX web-service.
Update 1: OK, I have learned something new from here
http://www.aspsnippets.com/Articles/How-to-add-reference-of-Web-Service-ASMX-in-ASPNet-using-Visual-Studio.aspx
There is an old-style thing like "Web reference" and it's not an "Service reference". I have added this Web reference to my project and now I can call some methods from this ASMX page in this way:
try
{
ServerService svc = new ServerService();
svc.Credentials = new NetworkCredential("user", "password");
int a = svc.GetRandomInteger();
} catch (WebException e) {
// Auth failed
}
However, I don't understand how to link it with ASP.NET MVC ClientService authentication. So, both questions are still open. Hopefully, I will understand it or you will help me.

Here is a documentation for adding a Web reference to an ASMX Service.
http://www.aspsnippets.com/Articles/How-to-add-reference-of-Web-Service-ASMX-in-ASPNet-using-Visual-Studio.aspx
Using this information I can easily make requests to a web service.
The only thing I left to do on the moment of question update is to create a custom authentication.
When user logins, the client sends a request to a service. In case of successful basic authentication, it creates proper FormsAuthentication cookie ticket for a user. User logs in.
On each request to a service, the client extracts login from FormsAuthentication cookie and his password from server cache and uses them to authenticate on a service. In case of basic auth failure (it can only occur if user's password has been changed on the service side) the cookie is cleared and session is aborted.
Registration is implemented using another one ASMX service which is not using basic auth but is anonymous (because registration is supposed to be anonymous method).
That's it. Finally, I have found a proper solution :)

Related

Google Data API Authorization Redirect URI Mismatch

Background
I am wanting to write a small, personal web app in .NET Core 1.1 to interact with YouTube and make some things easier for me to do and I am following the tutorials/samples in Google's YouTube documentation. Sounds simple enough, right? ;)
Authenticating with Google's APIs seems impossible! I have done the following:
Created an account in the Google Developer Console
Created a new project in the Google Developer Console
Created a Web Application OAuth Client ID and added my Web App debug URI to the list of approved redirect URIs
Saved the json file provided after generating the OAuth Client ID to my system
In my application, my debug server url is set (and when my application launches in debug, it's using the url I set which is http://127.0.0.1:60077).
However, when I attempt to authenticate with Google's APIs, I recieve the following error:
That’s an error.
Error: redirect_uri_mismatch
The redirect URI in the request, http://127.0.0.1:63354/authorize/,
does not match the ones authorized for the OAuth client.
Problem
So now, for the problem. The only thing I can find when searching for a solution for this is people that say
just put the redirect URI in your approved redirect URIs
Unfortunately, the issue is that every single time my code attempts to authenticate with Google's APIs, the redirect URI it is using changes (the port changes even though I set a static port in the project's properties). I cannot seem to find a way to get it to use a static port. Any help or information would be awesome!
NOTE: Please don't say things like "why don't you just do it this other way that doesn't answer your question at all".
The code
client_id.json
{
"web": {
"client_id": "[MY_CLIENT_ID]",
"project_id": "[MY_PROJECT_ID]",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "[MY_CLIENT_SECRET]",
"redirect_uris": [
"http://127.0.0.1:60077/authorize/"
]
}
}
Method That Is Attempting to Use API
public async Task<IActionResult> Test()
{
string ClientIdPath = #"C:\Path\To\My\client_id.json";
UserCredential credential;
using (var stream = new FileStream(ClientIdPath, FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { YouTubeService.Scope.YoutubeReadonly },
"user",
CancellationToken.None,
new FileDataStore(this.GetType().ToString())
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = this.GetType().ToString()
});
var channelsListRequest = youtubeService.Channels.List("contentDetails");
channelsListRequest.Mine = true;
// Retrieve the contentDetails part of the channel resource for the authenticated user's channel.
var channelsListResponse = await channelsListRequest.ExecuteAsync();
return Ok(channelsListResponse);
}
Project Properties
The Original Answer works, but it is NOT the best way to do this for an ASP.NET Web Application. See the update below for a better way to handle the flow for an ASP.NET Web Application.
Original Answer
So, I figured this out. The issue is that Google thinks of a web app as a JavaScript based web application and NOT a web app with server side processing. Thus, you CANNOT create a Web Application OAuth Client ID in the Google Developer Console for a server based web application.
The solution is to select the type Other when creating an OAuth Client ID in the Google Developer Console. This will have Google treat it as an installed application and NOT a JavaScript application, thus not requiring a redirect URI to handle the callback.
It's somewhat confusing as Google's documentation for .NET tells you to create a Web App OAuth Client ID.
Feb 16, 2018 Updated Better Answer:
I wanted to provide an update to this answer. Though, what I said above works, this is NOT the best way to implement the OAuth workflow for a ASP.NET solution. There is a better way which actually uses a proper OAuth 2.0 flow. Google's documentation is terrible in regards to this (especially for .NET), so I'll provide a simple implementation example here. The sample is using ASP.NET core, but it's easily adapted to the full .NET framework :)
Note: Google does have a Google.Apis.Auth.MVC package to help simplifiy this OAuth 2.0 flow, but unfortunately it's coupled to a specific MVC implementation and does not work for ASP.NET Core or Web API. So, I wouldn't use it. The example I'll be giving will work for ALL ASP.NET applications. This same code flow can be used for any of the Google APIs you've enabled as it's dependent on the scopes you are requesting.
Also, I am assuming you have your application set up in your Google Developer dashboard. That is to say that you have created an application, enabled the necessary YouTube APIs, created a Web Application Client, and set your allowed redirect urls properly.
The flow will work like this:
The user clicks a button (e.g. Add YouTube)
The View calls a method on the Controller to obtain an Authorization URL
On the controller method, we ask Google to give us an Authorization URL based on our client credentials (the ones created in the Google Developer Dashboard) and provide Google with a Redirect URL for our application (this Redirect URL must be in your list of accepted Redirect URLs for your Google Application)
Google gives us back an Authorization URL
We redirect the user to that Authorization URL
User grants our application access
Google gives our application back a special access code using the Redirect URL we provided Google on the request
We use that access code to get the Oauth tokens for the user
We save the Oauth tokens for the user
You need the following NuGet Packages
Google.Apis
Google.Apis.Auth
Google.Apis.Core
Google.apis.YouTube.v3
The Model
public class ExampleModel
{
public bool UserHasYoutubeToken { get; set; }
}
The Controller
public class ExampleController : Controller
{
// I'm assuming you have some sort of service that can read users from and update users to your database
private IUserService userService;
public ExampleController(IUserService userService)
{
this.userService = userService;
}
public async Task<IActionResult> Index()
{
var userId = // Get your user's ID however you get it
// I'm assuming you have some way of knowing if a user has an access token for YouTube or not
var userHasToken = this.userService.UserHasYoutubeToken(userId);
var model = new ExampleModel { UserHasYoutubeToken = userHasToken }
return View(model);
}
// This is a method we'll use to obtain the authorization code flow
private AuthorizationCodeFlow GetGoogleAuthorizationCodeFlow(params string[] scopes)
{
var clientIdPath = #"C:\Path\To\My\client_id.json";
using (var fileStream = new FileStream(clientIdPath, FileMode.Open, FileAccess.Read))
{
var clientSecrets = GoogleClientSecrets.Load(stream).Secrets;
var initializer = new GoogleAuthorizationCodeFlow.Initializer { ClientSecrets = clientSecrets, Scopes = scopes };
var googleAuthorizationCodeFlow = new GoogleAuthorizationCodeFlow(initializer);
return googleAuthorizationCodeFlow;
}
}
// This is a route that your View will call (we'll call it using JQuery)
[HttpPost]
public async Task<string> GetAuthorizationUrl()
{
// First, we need to build a redirect url that Google will use to redirect back to the application after the user grants access
var protocol = Request.IsHttps ? "https" : "http";
var redirectUrl = $"{protocol}://{Request.Host}/{Url.Action(nameof(this.GetYoutubeAuthenticationToken)).TrimStart('/')}";
// Next, let's define the scopes we'll be accessing. We are requesting YouTubeForceSsl so we can manage a user's YouTube account.
var scopes = new[] { YouTubeService.Scope.YoutubeForceSsl };
// Now, let's grab the AuthorizationCodeFlow that will generate a unique authorization URL to redirect our user to
var googleAuthorizationCodeFlow = this.GetGoogleAuthorizationCodeFlow(scopes);
var codeRequestUrl = googleAuthorizationCodeFlow.CreateAuthorizationCodeRequest(redirectUrl);
codeRequestUrl.ResponseType = "code";
// Build the url
var authorizationUrl = codeRequestUrl.Build();
// Give it back to our caller for the redirect
return authorizationUrl;
}
public async Task<IActionResult> GetYoutubeAuthenticationToken([FromQuery] string code)
{
if(string.IsNullOrEmpty(code))
{
/*
This means the user canceled and did not grant us access. In this case, there will be a query parameter
on the request URL called 'error' that will have the error message. You can handle this case however.
Here, we'll just not do anything, but you should write code to handle this case however your application
needs to.
*/
}
// The userId is the ID of the user as it relates to YOUR application (NOT their Youtube Id).
// This is the User ID that you assigned them whenever they signed up or however you uniquely identify people using your application
var userId = // Get your user's ID however you do (whether it's on a claim or you have it stored in session or somewhere else)
// We need to build the same redirect url again. Google uses this for validaiton I think...? Not sure what it's used for
// at this stage, I just know we need it :)
var protocol = Request.IsHttps ? "https" : "http";
var redirectUrl = $"{protocol}://{Request.Host}/{Url.Action(nameof(this.GetYoutubeAuthenticationToken)).TrimStart('/')}";
// Now, let's ask Youtube for our OAuth token that will let us do awesome things for the user
var scopes = new[] { YouTubeService.Scope.YoutubeForceSsl };
var googleAuthorizationCodeFlow = this.GetYoutubeAuthorizationCodeFlow(scopes);
var token = await googleAuthorizationCodeFlow.ExchangeCodeForTokenAsync(userId, code, redirectUrl, CancellationToken.None);
// Now, you need to store this token in rlation to your user. So, however you save your user data, just make sure you
// save the token for your user. This is the token you'll use to build up the UserCredentials needed to act on behalf
// of the user.
var tokenJson = JsonConvert.SerializeObject(token);
await this.userService.SaveUserToken(userId, tokenJson);
// Now that we've got access to the user's YouTube account, let's get back
// to our application :)
return RedirectToAction(nameof(this.Index));
}
}
The View
#using YourApplication.Controllers
#model YourApplication.Models.ExampleModel
<div>
#if(Model.UserHasYoutubeToken)
{
<p>YAY! We have access to your YouTube account!</p>
}
else
{
<button id="addYoutube">Add YouTube</button>
}
</div>
<script>
$(document).ready(function () {
var addYoutubeUrl = '#Url.Action(nameof(ExampleController.GetAuthorizationUrl))';
// When the user clicks the 'Add YouTube' button, we'll call the server
// to get the Authorization URL Google built for us, then redirect the
// user to it.
$('#addYoutube').click(function () {
$.post(addYoutubeUrl, function (result) {
if (result) {
window.location.href = result;
}
});
});
});
</script>
As referred here, you need to specify a fix port for the ASP.NET development server like How to fix a port number in asp.NET development server and add this url with the fix port to the allowed urls. Also as stated in this thread, when your browser redirects the user to Google's oAuth page, you should be passing as a parameter the redirect URI you want Google's server to return to with the token response.
I noticed that there is easy non-programmatic way around.
If you have typical monotlith application built in typical MS convention(so not compatible with 12factor and typical DDD) there is an option to tell your Proxy WWW server to rewrite all requests from HTTP to HTTPS so even if you have set up Web App on http://localhost:5000 and then added in Google API url like: http://your.domain.net/sigin-google, it will work perfectly and it is not that bas because it is much safer to set up main WWW to rewrite all to HTTPS.
It is not very good practice I guess however it makes sense and does the job.
I've struggled with this issue for hours in a .net Core application. What finally fixed it for me was, in the Google developers console, to create and use a credential for "Desktop app" instead of a "Web application".
Yeah!! Using credentials of desktop app instead of web app worked for me fine. It took me more than 2 days to figure out this problem. The main problem is that google auth library dose not adding or supporting http://localhost:8000 as redirect uri for web app creds but credentials of desktop app fixed that issue. Cause its supporting http://___ connection instead of https: connection for redirect uri

How to create custom authentication mechanism based on HTTP header?

I'm leaving old version of question on a bottom.
I'd like to implement custom authentication for SignalR clients. In my case this is java clients (Android). Not web browsers. There is no Forms authentication, there is no Windows authentication. Those are plain vanilla http clients using java library.
So, let's say client when connects to HUB passes custom header. I need to somehow authenticate user based on this header. Documentation here mentions that it is possible but doesn't give any details on how to implement it.
Here is my code from Android side:
hubConnection = new HubConnection("http://192.168.1.116/dbg", "", true, new NullLogger());
hubConnection.getHeaders().put("SRUserId", userId);
hubConnection.getHeaders().put("Authorization", userId);
final HubProxy hubProxy = hubConnection.createHubProxy("SignalRHub");
hubProxy.subscribe(this);
// Work with long polling connections only. Don't deal with server sockets and we
// don't have WebSockets installed
SignalRFuture<Void> awaitConnection = hubConnection.start(new LongPollingTransport(new NullLogger()));
try
{
awaitConnection.get();
Log.d(LOG_TAG, "------ CONNECTED to SignalR -- " + hubConnection.getConnectionId());
}
catch (Exception e)
{
LogData.e(LOG_TAG, e, LogData.Priority.High);
}
P.S. Original question below was my desire to "simplify" matter. Because I get access to headers in OnConnected callback. I thought there is easy way to drop connection right there..
Using Signal R with custom authentication mechanism. I simply check if connecting client has certain header passed in with connection request.
Question is - how do I DECLINE or NOT connect users who don't pass my check? Documentation here doesn't really explain such scenario. There is mentioning of using certificates/headers - but no samples on how to process it on server. I don't use Forms or windows authentication. My users - android java devices.
Here is code from my Hub where I want to reject connection..
public class SignalRHub : Hub
{
private const string UserIdHeader = "SRUserId";
private readonly static SignalRInMemoryUserMapping Connections = new SignalRInMemoryUserMapping();
public override Task OnConnected()
{
if (string.IsNullOrEmpty(Context.Headers[UserIdHeader]))
{
// TODO: Somehow make sure SignalR DOES NOT connect this user!
return Task.FromResult(0);
}
Connections.Add(Context.Headers[UserIdHeader], Context.ConnectionId);
Debug.WriteLine("Client {0}-{1} - {2}", Context.Headers[UserIdHeader], Context.ConnectionId, "CONNECTED");
return base.OnConnected();
}
So I just created a custom Authorization Attribute and overrode the AuthorizeHubConnection method to get access to the request and implemented the logic that you were trying to do with the Header and it appears to be working.
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
namespace SignalR.Web.Authorization
{
public class HeadersAuthAttribute : AuthorizeAttribute
{
private const string UserIdHeader = "SRUserId";
public override bool AuthorizeHubConnection(HubDescriptor hubDescriptor, IRequest request)
{
if (string.IsNullOrEmpty(request.Headers[UserIdHeader]))
{
return false;
}
return true;
}
}
}
Hub
[HeadersAuth]
[HubName("messagingHub")]
public class MessagingHub : Hub
{
}
Which yields this in the console (if the picture doesn't show up, it's a [Failed to load resource: the server responded with a status of 401 (Unauthorized)]):
In fact, accepted answer is wrong. Authorization attribute, surprisingly, shall be used for authorization (that is, you should use it for checking whether requesting authenticated user is authorized to perform a desired action).
Also, since you using incorrect mechanics, you don't have HttpContext.Current.User.Identity set. So, you have no clear way to pass user info to your business / authorization logic.
And third, doing that you won't be able to use Clients.User() method to send message to specific user, since SignalR will be not able to map between users and connections.
The correct way is to plug in into OWIN authentication pipeline. Here is an excellent article explaining and demonstrating in detail how to implement custom authentication to be used in OWIN.
I not going to copy-paste it here, just follow it and make sure you implement all required parts:
Options
Handler
Middleware
After you have these, register them into OWIN:
app.Map("/signalr", map =>
{
map.UseYourCustomAuthentication();
var hubConfiguration = new HubConfiguration
{
Resolver = GlobalHost.DependencyResolver,
};
map.RunSignalR(hubConfiguration);
});

SignalR - authenticate windows forms user on windows forms server

I have a small project which contains a windows forms signalr client and a windows forms server. Clients can connect to server. Now, I want to implement a login system. I read some blogposts, articles and questions about this, but I didn't found a way to do it. I would like to use the signalr authentication system so I can use attributes such as [Authorize] because it's already there.
To use this, I need to validate the username and password. Can the client sends the credentials in header like this
Connection = new HubConnection(BaseEngine.ServerURI);
Connection.Headers.Add("Username", username);
Connection.Headers.Add("Password", password);
HubProxy = Connection.CreateHubProxy("ChatHub");
await Connection.Start();
and the server should validate those credentials somehow in a method and throw an exception if are not valid?
I've tried to use the builtin system, but no luck. I couldn't obtain the Context.User in OnConnected method. As a workaround, I've tried to send the username and password in header and validate them, but the OnConnected method cannot throw errors to client. I am sure the client has to have an auth cookie, but I really don't know how to add it.
Thank you!
It is never a good idea to send password to the server this way , it is better to send a token that the server can validate .
also SignalR has some authentication features read more about this here
I have found something like a workaround: first of all, I implemented an attribute, derived from AuthorizeAttribute used by SignalR. This implementation overrides the AuthorizeHubMethodInvocation method witch is called when a method that is decorated with this attribute is called. So, in this method, I'm checking that an Authorization Token is present in the header of the request and validate the information. The client has to add this header to connect to the server. It's the easiest method I have found so far, but it's still an workaround.
Implementation, server:
[AttributeUsage(AttributeTargets.Method)]
internal class CustomAuthorizeAttribute : AuthorizeAttribute
{
public override bool AuthorizeHubMethodInvocation(Microsoft.AspNet.SignalR.Hubs.IHubIncomingInvokerContext hubIncomingInvokerContext, bool appliesToMethod)
{
string token = hubIncomingInvokerContext.Hub.Context.Headers["AuthenticationToken"];
if (string.IsNullOrEmpty(token))
return false;
else
{
string decryptedValue = Encryptor.Decrypt(token, Encryptor.Password);
string[] values = decryptedValue.Split(';');
string userName = values[0],
deviceId = values[1];
bool b = ...check if it's ok...
return b;
}
}
}
Implementation, client:
ComEngine.Connection = new HubConnection(BaseEngine.ServerURI);
ComEngine.Connection.Headers.Add("AuthenticationToken", Encryptor.Encrypt(string.Format("{0};{1};{2}", BaseEngine.UserName, BaseEngine.DeviceId, BaseEngine.Password), Encryptor.Password));
try
{
await Connection.Start();
}
catch (Exception ex)
{
...
}

SignalR Security

I am new to SignalR but I was curious about how secure it is.
For example, I create the following method to send a message to all users like so:
public class NotificationHub : Hub
{
public void Send(string message)
{
Clients.All.broadcastMessage(message);
}
}
SignalR generates the following method in a js file (hubs):
proxies.notificationHub.server = {
send: function (message) {
return proxies.notificationHub.invoke.apply(proxies.notificationHub, $.merge(["Send"], $.makeArray(arguments)));
}
};
So, couldn't any user in the world just copy and paste this into their console and send a message of their choice to all of my users without my say-so?
var notifications = $.connection.notificationHub;
notifications.server.send("Your site has been hacked!");
I just tried this and it works - so, how can I prevent my users from sending unauthorized messages from the client side?
It's an HTTP endpoint like any other. If you want to restrict access to it you need to authenticate users and authorize their actions. You authenticate using standard web auth methods (forms auth, cookies, Windows auth, etc.) and you can authorize in code using SignalR constructs (like the Authorize attribute you point out) or with your own code.
This is all documented: http://www.asp.net/signalr/overview/signalr-20/security/introduction-to-security

Side by side Basic and Forms Authentication with ASP.NET Web API

Disclaimer: let me start by saying that I am new to MVC4 + Web Api + Web Services in general + JQuery. I might be attacking this on the wrong angle.
I am trying to build a Web MVC App + Web API in C# for .NET 4 to deploy in Azure. The web api will be used by mobile clients (iOS, using RestKit).
The Web MVC App will be relatively simple. We would like to use Forms Authentication for it and SimpleMembership - which we achieved and works fine.
We'll use the Web API methods from JQuery (Knockout) scripts to fill pieces of the web pages. Therefore, we expect the JQuery to use the same identity authenticated by Forms Authentication.
However, the idea is that the Web Api can be called directly by mobile clients. No Forms Authentications for those.
We have been looking at the Thinktecture Identity Model (http://nuget.org/packages/Thinktecture.IdentityModel https://github.com/thinktecture/Thinktecture.IdentityModel.40). We added the BasicAuth and AcessKey handlers to the config and it works (see code below).
When you try to access the webapi without being authenticated the browser displays the basic authentication dialog and works as expected.
The "issue" is that when you ARE already logged in via Forms Authentication and try to call a Web Api method you still get the Basic Authentication dialog. In other words, Thinktecture IdentityModel seems to ignore the Forms Authentication altogether.
My questions are:
Are my expectations correct? that once I have done the forms authentication I shouldn't do anything else to let the JQuery scripts, etc., access the Web API from the same browser user session.
How do I fix it?
If my expectations are not correct; how is this supposed to work? ie: how do I make the JQuery scripts authenticate?
I know there are tons of similar questions in Stackoverflow, I honestly looked a lot of up, saw videos, etc., but either I am missing something obvious or there is no clear documentation about this for somebody new in the technologies.
I appreciate the help. Thanks.
public static AuthenticationConfiguration CreateConfiguration()
{
var config = new AuthenticationConfiguration
{
DefaultAuthenticationScheme = "Basic",
EnableSessionToken = true,
SetNoRedirectMarker = true
};
config.AddBasicAuthentication((userName, password) => userName == password, retainPassword: false);
config.AddAccessKey(token =>
{
if (ObfuscatingComparer.IsEqual(token, "accesskey123"))
{
return Principal.Create("Custom",
new Claim("customerid", "123"),
new Claim("email", "foo#customer.com"));
}
return null;
}, AuthenticationOptions.ForQueryString("key"));
Here is the solution for this problem which I have come up with earlier.
Note: This solution doesn't involve Thinktecture Identity Model.
I have an abstract BasicAuthenticationHandler class which is a delegating handler. You can get this handler by installing the latest stable WebAPIDoodle NuGet package.
You can give a hint to this base basic authentication handler to suppress the authentication process if the request has been already authentication (e.g: by forms auth). Your custom handler that you need to register would look like as below:
public class MyApplicationAuthHandler : BasicAuthenticationHandler {
public MyApplicationAuthHandler()
: base(suppressIfAlreadyAuthenticated: true) { }
protected override IPrincipal AuthenticateUser(
HttpRequestMessage request,
string username,
string password,
CancellationToken cancellationToken) {
//this method will be called only if the request
//is not authanticated.
//If you are using forms auth, this won't be called
//as you will be authed by the forms auth bofore you hit here
//and Thread.CurrentPrincipal would be populated.
//If you aren't authed:
//Do you auth here and send back an IPrincipal
//instance as I do below.
var membershipService = (IMembershipService)request
.GetDependencyScope()
.GetService(typeof(IMembershipService));
var validUserCtx = membershipService
.ValidateUser(username, password);
return validUserCtx.Principal;
}
protected override void HandleUnauthenticatedRequest(UnauthenticatedRequestContext context) {
// Do nothing here. The Autharization
// will be handled by the AuthorizeAttribute.
}
}
As a final step, you will need to apply System.Web.Http.AuthorizeAttribute (not System.Web.Mvc.AuthorizeAttribute) to your controllers and action methods to give authorization for the specific roles and users.
I hope this helps to solve your problem.

Categories