My application requires 2 login mechanisms, one is Basic Authentication giving credentials in windows security dialog, the other way is kind of faking Basic Authentication meaning an encrypted username, password is sent as part of url in querystring say http://myurl.com?username=xxx&pwd=xxxxx
I have created a customModule like below
public class mymodule:IhttpModule
{
public void OnApplicationAuthenticateRequest(object sender, EventArgs e)
{
var context = ((HttpApplication)sender).Context;
if(context.Request.Querystring["username"]!=null)
{
//then write the cookie
createcookie();
}
else
{
//Request user for Basic authentication
context.Request.Headers["Authorization"])
}
}
}
1) When I call the URL like this http://myurl.com?username=xxx&pwd=xxxxx first request goes to AuthenticateRequest and cookie gets created, but the AuthenticateRequest gets called for multiple times and at one point Request.QueryString is null and it executes the line context.Request.Headers["Authorization"]) and it triggers Windows Security Dialog(basic authentication), how do I achieve my requirement.
Related
I'm setting up my own OAuth2 server. So far, I have succesfully implemented GrantResourceOwnerCredentials in my implementation of OAuthAuthorizationServerProvider. Now, because I am developing an app for our business, I want to implement the OAuth2 Authorization Code grant.
I have tried to follow directions here https://learn.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server but in my implementation, I have not found how to reach the Create call of the AuthorizationCodeProvider (which I set in OAuthAuthorizationServerOptions).
I have briefly checked whether accessing the TokenEndpointPath with a (wrong) code parameter works, and in the debugger I see that my AuthorizationCodeProvider's Receive call is hit. Of course there is no success because the code I send is 'sometestcode' instead of a real one, but the code is hit so that means I'm on the right path.
Here's what I have so far:
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (OAuthRepository.GetClient(context.ClientId) != null)
{
var expectedRootUri = new Uri(context.Request.Uri, "/");
if (context.RedirectUri.StartsWith(expectedRootUri.AbsoluteUri))
{
context.Validated();
return Task.FromResult<object>(null);
}
}
context.Rejected();
return Task.FromResult<object>(null);
}
public override Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
// I know this is wrong but it's just a start and not the focus of this SO question.
context.Response.Redirect(context.AuthorizeRequest.RedirectUri);
context.RequestCompleted();
return Task.FromResult<object>(null);
}
public override Task GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context)
{
// Needs additional checks, not the focus of my question either
var newTicket = new AuthenticationTicket(context.Ticket.Identity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult<object>(null);
}
Now, when I call my AuthorizeEndpointPath with a redirect_uri, I am sent to that Uri immediately. I know this is wrong: I should be sent to a separate login page. I'll fix my Web API later to redirect to the correct Uri.
The focus of my question is this: I am now in the process of implementing the login page, but I do not know how to get the authorization code from my WebAPI after the user has logged in. (I'm skipping the consent part for now and assume that if the user is logged in they're okay with it, I'll add giving consent later.)
I am basing my flow on the diagram shared here https://docs.apigee.com/api-platform/security/oauth/oauth-v2-policy-authorization-code-grant-type
I am using Thinktecture IdentityModel to create the login page in an MVC Controller. Now I need to retrieve the authorization code from the Web API in my MVC Controller. And after that I can then redirect the user back to the original client (app) that requested the Authorization Code flow.
To obtain the authorization code from my Web API, I see three methods in Thinktecture's OAuth2Client:
CreateAuthorizeUrl
CreateCodeFlowUrl
RequestAuthorizationCodeAsync
Neither seem to do what I want. How do I proceed so that my WebAPI is called to generate the code?
[HttpGet]
[ImportModelStateFromTempData]
public ActionResult Authorize(string clientId, string returnUrl, string responseType)
{
AuthorizeViewModel viewModel = new AuthorizeViewModel();
...
...
...
return View(viewModel);
}
[HttpPost]
[ExportModelStateToTempData]
public async Task<ActionResult> Authorize(AuthorizeViewModel viewModel)
{
// NOTE: This is in MVC and is postback from *.cshtml View.
OAuth2Client.?????? // <=== How to obtain authorization code from WebAPI?
...
return Redirect(returnUrl);
}
I think I have it correctly setup on the Web API side. I just don't know how to hit the Create part of the flow. I hope someone can help me understand what I am not seeing. I have a blind spot somewhere I think...
How do I have OAuth2Client get me the authorization code from my WebAPI?
I am also using Postman to test my Web API. If anyone can help me get the URL in Web API 2.0 that returns an authorization code, I would also accept that as an answer. Then I can write the code in MVC myself.
Edit
Okay, so I think I found a part of my blind spot. Firstly, I marked `AuthorizeEndpoint' as "not the focus of this SO question", but that was a big mistake.
When I adapt the AuthorizeEndpoint like so:
public override Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
System.Security.Claims.ClaimsIdentity ci = new System.Security.Claims.ClaimsIdentity("Bearer");
context.OwinContext.Authentication.SignIn(ci);
context.RequestCompleted();
return Task.FromResult<object>(null);
}
And if I adapt my implementation of AuthorizationCodeProvider.Create like so:
public void Create(AuthenticationTokenCreateContext context)
{
context.Ticket.Properties.IssuedUtc = DateTime.UtcNow;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddSeconds(60);
// Some random Guid
context.SetToken(Guid.NewGuid().ToString("n"));
}
Any call to /authorize is redirected to redirect_uri with a query parameter code=<THE_RANDOM_GUID>! :D
Obviously, this implementation is not where it should be, so my question is not yet resolved. Remaining issues:
Right now, anybody can request an authorization code, the client_id is ignored. ValidateClientAuthentication is apparently not hit as part of AuthorizeEndpoint. How do I obtain ClientId in AuthorizeEndpoint?
The authorization code is not coupled to a client. Anyone who intercepts the code could use it. How do I obtain the ClientId in AuthorizationCodeProvider.Create so that I can store it with the code?
The authorization code is not coupled to a user at all, it's an empty ClaimsIdentity. How do I put a user-login page in between and in AuthorizeEndpoint obtain the ClaimsIdentity for the logged-in user?
So, after quite some searching online, I got some success by searching github. Apparently, OAuthAuthorizationServerProvider offers AuthorizeEndpoint and that method should be used for both "Hey, you're not authorized, go log in you!" as well as for "Ahh, okay you're cool, here's an authorization code.". I had expected that OAuthAuthorizationServerProvider would have two separate methods for that, but it doesn't. That explains why on github, I find some projects that implement AuthorizeEndpoint in a rather peculiar way. I've adopted this. Here's an example:
public override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
if (context.Request.User != null && context.Request.User.Identity.IsAuthenticated)
{
var redirectUri = context.Request.Query["redirect_uri"];
var clientId = context.Request.Query["client_id"];
var authorizeCodeContext = new AuthenticationTokenCreateContext(
context.OwinContext,
context.Options.AuthorizationCodeFormat,
new AuthenticationTicket(
(ClaimsIdentity)context.Request.User.Identity,
new AuthenticationProperties(new Dictionary<string, string>
{
{"client_id", clientId},
{"redirect_uri", redirectUri}
})
{
IssuedUtc = DateTimeOffset.UtcNow,
ExpiresUtc = DateTimeOffset.UtcNow.Add(context.Options.AuthorizationCodeExpireTimeSpan)
}));
await context.Options.AuthorizationCodeProvider.CreateAsync(authorizeCodeContext);
context.Response.Redirect(redirectUri + "?code=" + Uri.EscapeDataString(authorizeCodeContext.Token));
}
else
{
context.Response.Redirect("/account/login?returnUrl=" + Uri.EscapeDataString(context.Request.Uri.ToString()));
}
context.RequestCompleted();
}
Source: https://github.com/wj60387/WebApiOAUthBase/blob/master/OwinWebApiBase/WebApiOwinBase/Providers/OAuthServerProvider.cs
As for my remaining three questions:
Right now, anybody can request an authorization code, the client_id is ignored. ValidateClientAuthentication is apparently not hit as part of AuthorizeEndpoint. How do I obtain ClientId in AuthorizeEndpoint?
Answer: You have to implement `ValidateClientAuthentication'.
The authorization code is not coupled to a client. Anyone who intercepts the code could use it. How do I obtain the ClientId in AuthorizationCodeProvider.Create so that I can store it with the code?
Answer: OAuthAuthorizationServerProvider takes care of this. As long as you set "client_id" in the ticket, it will check that the client that requests an access token for the authorization code is the same.
The authorization code is not coupled to a user at all, it's an empty ClaimsIdentity. How do I put a user-login page in between and in AuthorizeEndpoint obtain the ClaimsIdentity for the logged-in user?
Answer: You create a separate login page. What this does is sign the user in. If your WebAPI uses cookie-based authentication, you can just redirect the user to the AuthorizeEndpoint again. If you use access tokens, your login page has to make a request to `AuthorizeEndpoint' with the access token to obtain an authorization code. (Don't give the access token to the third party. Your login page requests the authorization code and sends that back.) In other words, if you use access tokens then there are two clients involved in this flow.
We have an ASP.NET MVC 4 intranet application. We’re using Windows Authentication and that aspect works fine. The user’s credentials are used and we can access those credentials from the web app.
What we really want is some sort of hybrid mode, however. We want to get the user’s credentials from the browser, but we also want to verify that the user is in our application’s database. If the user’s in the database, then they can just continue on. If they’re not, we want to redirect them to a page asking for alternate credentials. What I’m doing now is, in Global.asax.cs, I’ve got an Application_AuthenticateRequest method and I’m checking to see if the user is authenticated. If they are and their cookie information doesn’t reflect the fact that they’re logged into the system, then I log them in and set up some cookies with info about the user. If they’re not authenticated, I redirect them to a login page. We can’t use AD roles for reasons involved with company policy, so we need to use the database for additional authentication.
I’m guessing Application_AuthenticateRequest isn’t the place to do this, but maybe it is. But we basically need a place to filter the requests for authentication. But additionally this implementation leads me to another issue:
We have certain URLs in our app that allow anonymous access. I’ve added <location> tags to the web.config for these. The problem is, when anonymous calls are made into these, it gets to Application_AuthenticateRequest and tries to log the user into the DB. Now, I can add code into Application_AuthenticateRequest to handle these URLs and that’s currently my plan, but if I’m write and Application_AuthenticateRequest isn’t the place to be doing this, then I’d rather figure it out now than later.
You need to use Action Filters for this purpose. You can extend the AuthorizeAttribute like this:
public class MyAuthorizeAttribute : AuthorizeAttribute
{
private UnitOfWork _unitOfWork = new UnitOfWork();
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = false;
var username = httpContext.User.Identity.Name;
// Some code to find the user in the database...
var user = _unitOfWork.UserRepository.Find(username);
if(user != null)
{
isAuthorized = true;
}
return isAuthorized;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (AuthorizeCore(filterContext.HttpContext))
{
SetCachePolicy(filterContext);
}
else
{
// If not authorized, redirect to the Login action
// of the Account controller...
filterContext.Result = new RedirectToRouteResult(
new System.Web.Routing.RouteValueDictionary {
{"controller", "Account"}, {"action", "Login"}
}
);
}
}
protected void SetCachePolicy(AuthorizationContext filterContext)
{
// ** IMPORTANT **
// Since we're performing authorization at the action level,
// the authorization code runs after the output caching module.
// In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later
// be served the cached page. We work around this by telling proxies not to
// cache the sensitive page, then we hook our custom authorization code into
// the caching mechanism so that we have the final say on whether a page
// should be served from the cache.
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidationHandler, null /* data */);
}
public void CacheValidationHandler(HttpContext context,
object data,
ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
}
Then, you can use this attribute at the Controller level or Action level like this:
[MyAuthorize]
public ActionResult SomeAction()
{
// Code that is supposed to be accessed by authorized users only
}
I am creating a custom ClaimsPrincipal that has additional claims using a custom AuthenticationManager. It appears that in ASP.NET 4.5 the AuthenticationManager is not automatically invoked and you have to manually invoke it. I have seen posts that suggest you do this in the PostAuthenticateRequest. My code looks like this in the Global.asax:
protected void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
{
System.Web.HttpContext.Current.User =
FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager.Authenticate("none",
System.Web.HttpContext.Current.User as ClaimsPrincipal);
}
I am using Forms Authentication with cookies. The current principal gets set correctly when the PostAuthenticateRequest is fired. The problem is that when the next request comes in to the ASP.NET Web API the custom claims are lost. I am using Web API for AJAX requests in a single page application (SPA). I suspect that what is in the current ClaimsPrincipal is being overwritten by what is in the cookie, but I am not sure. I am not using an inherited ClaimsPrincipal for type-safe retrieval of custom claims. I am just adding new claims which are subsequently lost somewhere in the process. What is the proper way to setup a custom ClaimsPrincipal so the additional claims are not loss between requests.
Here is the solution I came up with. First I used the FormsAuthentication_OnAuthenticate event instead of the Application_OnPostAuthenticateRequest event. I get the cookie to retrieve the authenticated users name and build a new claims principal with the claims I want to add. I do not add the roles as claims because the system adds them later in the authentication process and ends up duplicating them. This allows me to add custom claims to the principal for future processing in the application.
public void FormsAuthentication_OnAuthenticate(object sender, FormsAuthenticationEventArgs args)
{
if (FormsAuthentication.CookiesSupported)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(
Request.Cookies[FormsAuthentication.FormsCookieName].Value);
SaClaimsSecurity security = new SaClaimsSecurity();
//Do not add roles here because the security framework adds them
//somewhere else in the chain and will duplicate roles
ClaimsPrincipal principal = security.CreateClaimsPrincipalWithoutRoles(ticket.Name);
args.User = principal;
}
catch (Exception e)
{
// Decrypt method failed.
}
}
}
else
{
throw new HttpException("Cookieless Forms Authentication is not " +
"supported for this application.");
}
}
Where can I plug in a custom provider to set up the request context?
I want to run an ASP.NET MVC application in "slave" mode while gradually transitioning features from a legacy system. Each request will have a cookie, and I want to grab the cookie, make an external call to resolve it to a user identity, and set up that user identity for the remainder of the request.
I might set a forms authentication cookie, or use Session, but the source of truth about authentication has to be the externally-set cookie, on every request.
What's the best way to do this? Where do I plug in? I've looked at Authentication providers, and the Authorization attribute, but neither of those seems the right place for this.
I would have thought an HttpModule would be ideal for this scenario?
If I understand you correctly, I did something similar on a project I was working on recently:
public class UserSessionHttpModule : IHttpModule
{
private HttpApplication mApplication;
public void Dispose()
{
}
public void Init(HttpApplication context)
{
mApplication = context;
context.BeginRequest += new EventHandler(CheckUserSession);
}
private void CheckUserSession(Object sender, EventArgs e)
{
var extension = Path.GetExtension(mApplication.Context.Request.Path);
if (extension == "" || extension == ".aspx")
{
var userSessionService = ObjectFactory.GetInstance<IUserSessionService>();
userSessionService.CheckUserSession();
}
}
}
you could override the Init method in Global.asax page and listen to the PostAuthenticateRequest event, the events fires after authentication so you could change whatever values you like from the form authenticate cookies and inject your own
How do I even begin coding authentication using ASP.NET Web API so it is cross-platform to support desktop, mobile and web? I'd read of some methods of doing RESTful authentication, such as using tokens in the header.
Are there any example projects out there that utilizes this method?
Questions:
If not how do I fix the [Authorize] attribute to read the token?
How do I generate this token? I dont think i can use formsauthentication because that uses cookies.
How do I handle the actual authorization, do the client send raw password and username then I generate the token or is there some other way?
How do I handle when my website is using it? I heard this is handled differently than when an app is using it, such as getting the domain and authorizing it.
I think tokens would be a solid way to go. Forms authentication is based on cookies for the web. Not the most idea situation for all non browser clients though.
What I'd suggest is creating a custom AuthorizationFilterAttribute and overriding the OnAuthorization method. In that method, you could check for the existence of a token that you've issued to the client after they've supplied valid credentials. You can use this attribute on any method or controller you want validated. Here's a sample you might reference
public class AuthorizeTokenAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext != null)
{
if (!AuthorizeRequest(actionContext.ControllerContext.Request))
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { RequestMessage = actionContext.ControllerContext.Request };
}
return;
}
}
private bool AuthorizeRequest(System.Net.Http.HttpRequestMessage request)
{
bool authorized = false;
if (request.Headers.Contains(Constants.TOKEN_HEADER))
{
var tokenValue = request.Headers.GetValues("TOKEN_HEADER");
if (tokenValue.Count() == 1) {
var value = tokenValue.FirstOrDefault();
//Token validation logic here
//set authorized variable accordingly
}
}
return authorized;
} }
TOKEN_HEADER is just a string representing an HTTP header that the client should pass back for authenticated requests.
So let's walk through it
Client requests secure data
Client is not authorized, return a response with an Unauthorized status code
Client sends credentials to authenticate, which should be secured via HTTPS
Once validated, client receives a token via an HTTP header, or whatever works for you
Client tries requesting secure data again, this time attached the token to the request
The AuthorizeTokenAttribute will validate the token and allow the action to execute.
Also, check this post by John Petersen. Making your ASP.NET Web API’s secure
There are lots of ways to authenticate users for a REST service. Using tokens is possible but just using Basic Authentication is even simpler and about as standard and cross platform as you can go.
Don't confuse authorization with authentication. The [Authorize] attribute is all about authorization but only after a user has been authenticated using some other mechanism. Authorization is completely useless without doing proper authentication first.
The best resource to check is Dominick Baier who is an expert on the subject.
I use a very simple approach:
define an access profile with its unique accessId and accessKey (e.g. MD5 hashed GUID value)
store such access profile in database
every request (GET/POST/etc.) must supply accessId, queryHash (MD5 hash value represents the query) and signature (MD5 hash value of queryHash + accessKey). Of course the client needs keep the accessKey in a secure place!!!
server gets the request will check the accessId and the signature using the same calculation algorithm to reject or grant the access (authenticate)
further authorization can be done on request type basis utilizing the access profile
the service with this approach using the new ASP.NET MVC web API can serve whatever type of client: browser/javascript and native(desktop or mobile) etc.
U can use ActionFilterAttribute and override the OnActionExecuting method.
Later on register this filter in global.cs to apply this filter for all the actions like this in Application Start method
var config = GlobalConfiguration.Configuration;
config.Filters.Add(new CustomAuthAttribute ());
{
namespace Customss
{
Public class CustomAuthAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// To inforce HTTPS if desired , else comment out the code
if (!String.Equals(actionContext.Request.RequestUri.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
{
Content = new StringContent("HTTPS Required")
};
return;
}
// get toekn from the header
var userToken = actionContext.Request.Headers.GetValues("UserToken");
// Customer Logic to check the validity of the token.
// U can have some DB logic to check , custom STS behind or some loca cache used to compare the values
}
}
}
}