ServiceStack with forms authentication across applications fails...why? - asp.net-mvc

I have a ServiceStack project running an API at api.mydomain.com. An admin project in the same solution is hosted at admin.mydomain.com. Login/Logout is already handled by the admin application, but I want to make sure the user is authenticated (and sometimes check permissions as well) on my api calls. I'm using forms authentication across projects so the auth cookie is available to my api project.
Here's my web.config authentication tag in the api project:
<authentication mode="Forms">
<forms protection="All" loginUrl="home/denied" slidingExpiration="true" timeout="60" defaultUrl="home/denied" path="/" domain="mydomain.com" name=".myAuth"></forms>
</authentication>
Based on this Authentication and authorization post, I added an [Authenticate] attribute to a service method, expecting it to pass/fail based on the value of IsAuthenticated. However, it redirects to 'home/denied' everytime, regardless of whether the auth cookie is present. (I confirmed this by subclassing AuthenticateAttribute and examining the OriginalRequest... The cookie set when I logged in using the admin app is present and req.OriginalRequest.IsAuthenticated is true.)
Why is my request being redirected, and how do I properly utilize the existing auth credential set in the admin app?
EDIT: Here's the solution I came up with. It simply requires an IPrincipal Identity to pass authentication.
public class AuthenticateAspNetAttribute : RequestFilterAttribute
{
public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{
SessionFeature.AddSessionIdToRequestFilter(req, res, null); //Required to get req.GetSessionId()
using (var cache = req.GetCacheClient())
{
var sessionId = req.GetSessionId();
var session = sessionId != null ? cache.GetSession(sessionId) : null;
var originalRequest = (System.Web.HttpRequest) req.OriginalRequest;
var identity = originalRequest.RequestContext.HttpContext.User.Identity;
if (!identity.IsAuthenticated)
AuthProvider.HandleFailedAuth(new BasicAuthProvider(), session, req, res);
}
}
}

On the Authentication and autorization post you reference it reads:
ServiceStack's Authentication, Caching and Session providers are
completely new, clean, dependency-free testable APIs that doesn't rely
on and is devoid of ASP.NET's existing membership, caching or session
provider models.
Meaning it's completely separate and has nothing to do with the ASP.NET's existing Authentication providers. i.e. The client needs to make an explicit call to the /auth service to authenticate with ServiceStack web services.
See the SocialBootstrapApi example demo project for an example of an MVC Website that uses and shares ServiceStack's Authentication providers between MVC Controllers and ServiceStack web services.

Related

JWT token and Owin authentication

I have MVC front end application using WebApi 2 application for authentication and authorization. I am using JWT tokens for the same. So far I have been able to successfully authenticate and receive back a token… I can further access the restricted resource ([Authorize] attribute) by adding an Authorization token to the header using POSTMAN tool.
Authorization: “Bearer <jwt.token.string>”
Issue is, I am not able to intercept the call in MVC pipeline to add the token to the httpHeader. It always routes me back to the login page. Not the case when I use the POSTMAN tool. I have unsuccessfully tried injecting the token at following points:
Extending Authorize attribute with custom implementation
Adding a custom ActionFilterAttribute
Adding custom DelegatingHandler
Owin pipeline using StageMarker (PipelineStageAuthenticate) in Startup.cs
In all above cases I am hitting the event because I can debug. I have strong suspicion that I am hitting the authorization point before I set my header but I can’t figure out sequence of flow to properly intercept the HttpContext object and inject the Authorization header.
After successful authentication, add
var ctx = Request.GetOwinContext();
var authenticateResult = await ctx.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ExternalBearer);
ctx.Authentication.SignOut(DefaultAuthenticationTypes.ExternalBearer);
var applicationCookieIdentity = new ClaimsIdentity(authenticateResult.Identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
ctx.Authentication.SignIn(applicationCookieIdentity);
This will create a signed cookie and your Authorize attribute will automatically read the cookie. All your requests will become authorized subsequently.

Skipping Azure ACS home realm discovery

I've been looking for a way to integrate the Azure ACS home realm discovery page into our ASP.Net MVC5 app login page rather than use the default one hosted on ACS itself.
What I want is something like what is suggested here:
http://www.cloudidentity.com/blog/2014/11/17/skipping-the-home-realm-discovery-page-in-azure-ad/#comment-126567
I’m building an MVC 5.1 on .Net 4.5.1 Azure Web Role which needs to authenticate users from multiple corporate identity providers – some with AAD, some with ADFS – and the list will grow over time. Initially this has been simple enough to set-up with Azure ACS as the federation provider. It presents a home realm discovery (HRD) page and the flow works.
Things become complicated when I try and follow the instructions for adding the HRD into my login page directly (following the instructions given in the ACS application integration pages using the Home Realm Discovery Metadata Feed and some example HTML + JS). I am able to present the HRD buttons as I would like however I am struggling with wiring the sign in process into the OWIN WsFederation setup, which in my application currently only knows about the ACS WsFederationMetadataUrl.
I’ve currently got this in my ConfigureAuth method:
app.UseWsFederationAuthentication(wsFederationOptions: new WsFederationAuthenticationOptions()
{
Notifications = new WsFederationAuthenticationNotifications()
{
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.Whr = ".com";
return Task.FromResult(0);
}
},
MetadataAddress = CloudConfigurationManager.GetSetting("Authentication.WsFederationMetadataUrl"),
Wtrealm = CloudConfigurationManager.GetSetting("Authentication.Realm"),
AuthenticationMode = AuthenticationMode.Passive,
});
Where .com is the domain of one of the IdPs in my ACS which happens to be our own AAD. But this doesn’t work. Can this scenario be made to work? I found one relevant stack post which talks about skipping the home realm discovery with Ws-Federation OWIN Middleware and allows for the Whr parameter to be set via a user action but so far I haven’t been able to get it to work with the Whr hard coded.
Skipping home realm discovery with Ws-Federation OWIN Middleware
I’ve changed the buttons from the example HTML+JS so that they post to the /Account/ExternalLogin controller action and get into the OWIN pipeline that way:
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string homeRealm, string returnUrl)
{
// Request a redirect to the external login provider
return new ChallengeResult(provider, homeRealm, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
rather than use a cookie and navigate to the login page at the chosen home realm (as the boiler plate HTML+JS from the ACS does) :
// Sets a cookie to remember the chosen identity provider and navigates to it.
function IdentityProviderButtonClicked() {
SetCookie(this.getAttribute("name"));
window.location = this.getAttribute("id");
return false;
}

Add custom claims to ASP.NET MVC with organizational account (multiple tenants)

Is there a way to add custom claim to my ClaimsPrincipal once the user is authenticated? When using ASP.NET identity for individual accounts, one could add custom claims to the ClaimsPrincipal when the principal was created but I can not find the way to do this when using the Organizational Accounts template.
For organizational account authentication, the templates setup HTTP handlers to handle authentication of your users. If you look in your web.config you will see two modules that were added to your project, which are the WSFederationAuthenticationModule and the SessionAuthenticationModule. As such, it's an entirely different authentication and authorization dance than what you are used to with cookie based authentication for individual accounts.
The extensibility point you are looking for is the Authenticate method of the ClaimsAuthenticationManager. Simply create a class that derives from this and override the Authenticate method. This will give you access to the ClaimsPrincipal object for the authenticated user where you can extend the claims collection for the user to whatever you want before your application code is invoked.
An example of how to set this up is here.
You do need to sign the user back in.
By default your claims are encrypted and stored as a single cookie in your browser. After you've manipulated your ClaimsPrincipal you need to persist the cookie back to the browser.
I agree the verbage is bad however you have to do the following
var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
var authenticationProperties = new AuthenticationProperties { IsPersistent = false };
authenticationManager.SignIn(authenticationProperties, myManipulatedClaimsIdentity);

Understanding ASP.NET MVC5 new authentication mechanism

Hi I am trying to get a hang of how the new authentication mechanism works in MVC5 in the SPA template and I seem to be left confused.My end goal is to create an API that will be exposed to a SPA , iOS , Android and Windows Phone clients
Here is what I understand:
I understand that somehow at startup the class decorated with:
[assembly: OwinStartup(typeof(WebApplication1.Startup))]
is magicly calling ConfigureAuth method:
Inside this method I have 3 lines of code and inside the startup class constructor I have initialized the OAuth authentication options:
static Startup(){
PublicClientId = "self";
UserManagerFactory = () => new UserManager<IdentityUser>(new UserStore<IdentityUser>());
OAuthOptions = new OAuthAuthorizationServerOptions {
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
}
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseOAuthBearerTokens(OAuthOptions);
}
The first two lines in ConfigureAuth seem to set my application and external application to use cookies for storing authentication state, while the third seems to state that it is using bearer tokens for my application.
From what limited knowledge I have so far about mobile devices native apps do not understand cookies and I should use tokens for authentication.
If that is the case shouldn't the externalSignIn be set to Bearer tokes instead of external cookie?
While debugging I also noticed that in the OAuthProvider the authentication type is actually set to bearrer tokens.If that is the case what does this line of code actualy do:
app.UseCookieAuthentication(new CookieAuthenticationOptions());
Some clarification to how this works would be grattely appreciated I could only find information online that shows me how tu use external logins.
It seems to me that the MVC 5 SPA Template is a demonstration of what is possible more than a commitment to a particular best practice.
I have found that removing the line app.UseCookieAuthentication(new CookieAuthenticationOptions()); has no effect on the SPA at all because, as is typical with SPAs, all HTML needed is retrieved anonymously and all authentication is, thereafter, done on any subsequent requests for data. In this case data would be retrieved from WebAPI endpoints and protected with Bearer Tokens.
I don't know why it has been done this way. There are a number of other areas like this where two different concerns are a bit muddled. for example the traditional Global.asax MVC Application_Start is still in place but the newer OWIN Startup mechanism is also present. There is no reason why everything in Application_Start (Filter / Route / Bundle registration, etc.) couldn't have been handled in OWIN Startup.
There are other issues too. If you turn on External Auth (e.g. with Google) and then reduce the AccessTokenExpireTimeSpan, you'll find that when the Token has expired your SPA presents a 'Authorization has been denied for this request.' message. In other words, there is no mechanism in place for Token refreshes. This is not immediately apparent out of the box because the Access Token timeout is set to 14 days, which is rather insecure when considering Cross-Site Request Forgery attacks and the like. Furthermore, there is no enforcement of a transport security mechanism, such as SSL. Tokens are not inherently secure and need to be secured in transport to prevent CRSF attacks and data being extracted en route.
So, MVC 5 SPA is good as a demo, I think, but I wouldn't use it in production. It shows what the new OWIN Middleware can do but it is no substitute for a comprehensive knowledge of Token-based security.

Setting ServiceStack requests authentication from OpenAuth token

(This question can be seen as follow ups to these two StackOverflow posts about OpenAuth with DotNetOpenAuth in a ServiceStack scenario: first and second)
From what I understand, ServiceStack uses an IAuthSession to know which user is authenticated, but this seems to rely on the HTTP session cookie. With OAuth request, no such cookie exist.
Question: I want my ServiceStack requests to be considered authenticated if 1) a the browser cookie is present or 2) if the OAuth Authentication Header Bearer is present. How should I do this?
I tried the following to set the thread's authentication, but it relies on ASP.NET's HttpContext.Current.User.
I'd also like it to work on both IIS hosted and Self-Hosted scenarios...
var analyzer = new StandardAccessTokenAnalyzer((RSACryptoServiceProvider)signCert.PublicKey.Key, (RSACryptoServiceProvider)encryptCert.PrivateKey);
var resourceServer = new ResourceServer(analyzer);
var requestWrapper = new HttpRequestWrapper((HttpRequest)request.OriginalRequest);
var principal = resourceServer.GetPrincipal(requestWrapper, requiredScopes);
HttpContext.Current.User = principal;
Any help is appreciated.

Resources