Can I use two authentication methods in Owin? - asp.net-mvc

In my application I use MVC and Web.API.
The MVC portion handles the admin front-end, serving the cshtml pages, communication with the back-end, regular authentication using cookies, etc:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
The Web.API one handles the REST requests made by the iOS and Android applications. For that one I want to use token based authentication:
var oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true, //todo-err: change in prod
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
My question is, what do I need to make this work? Regular and token authentication. Will I need to create a custom AuthorizeAttribute for the API controllers?
I appreciate any help :)

Your SimpleAuthorizationServerProvider needs to be implemented.
Generating a WebAPI project in VS2015 with authentication gives you something like this.

The article Applying Cookie-Stored Sessions With ASP.NET and OpenID Connect 1.0 by Kelvin Amorim is very useful for understanding multiple factors for supporting multiple authentication middlewares.
Some main points are:
Each authentication options middleware should use a different AuthenticationType (it is just a string key and there are some defaults to choose from)
You can set the cookie path and use corresponding MVC Areas (see RouteAreaAttribute) to control which cookies are used for which requests

Related

Owin Middleware per controller in asp.net web API

I am using the following JWT middleware to authorize all controllers with a valid JWT and it works as expected. I need to enable client certificate based authorization on one of the controllers. I understand that i can create a new middleware and plug it into owin pipeline which validates client certificates.
How to decide which controller to use which middleware? As far as i know OWIN does not have any knowledge of any controller. Please suggest
public void ConfigureAuth(IAppBuilder app)
{
TextEncodings.Base64Url.Decode("IxrAjDoa2FqElO7IhrSrUJELhUckePEPVpaePlS_Xaw");
var issuer = System.Configuration.ConfigurationManager.AppSettings["issuer"].ToString();
var audience = System.Configuration.ConfigurationManager.AppSettings["ClientID"].ToString();
var secret = TextEncodings.Base64Url.Decode(System.Configuration.ConfigurationManager.AppSettings["ClientSecret"].ToString());
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audience },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret),
}
});
}
Owin "knows" which controller to authorise because of the [Authorise] tag on top of it.
Why don't you do the same for the other system? Create another tag which then fires up the other authentication system? It's the easiest way to keep a clean system working.
You can more than likely use the owin source code to get some inspiration and se exactly how things are done.

Decouple self-host OWIN authorization server

I have a self-hosted OWIN application configured as an authorization server and a signalr resource server.
My clients are successfully obtaining the bearer token and presenting it for authorization in subsequent calls to the signalR hub.
My next step is to decouple the authorization service so that it can run on its own host. To get started I created a separate self-hosted app that contains only the authorization service code. It's still all in one solution on my development machine, but the authorization service and signalR resources are hosted in separate processes.
The auth flow is still working properly. The token is getting to my resource server, but now getting 401 unauthorized from the signalR hub.
There is alot of support out there for solving this in ASP.Net Web API, in which you would sync up a machine.config value in your web.config files. But that is not my architecture. Running as a self-hosted app under HttpListener uses different encryption, DPAPI by default.
There doesn't seem to be much discussion out there about solving this in a self-hosted architecture. My theory is that even under different processes on the same machine, the DPAPI decryption is failing and so I get 401.
I'm trying to figure out if there is some minimal approach to solving this or if I have to completely refactor maybe to use JWT instead.
EDIT: adding some code to help display my setup
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = false,
TokenEndpointPath = new PathString("/account/login"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
public void ConfigureOAuth(IAppBuilder app)
{
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
Provider = new ApplicationOAuthBearerAuthenticationProvider(),
});
}
Posting my own solution hopefully to help someone else down the road.
I did decide to implement a JWT solution rather than use the default. I think this is the better architecture anyway, decoupling your token encryption from the OS. I used this tutorial http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/
The crucial bits were creating your custom OAuthAuthorizationServerProvider and ISecureDataFormat for encrypting the token as shown in the tutorial. This just shows the OWIN config.
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = false,
TokenEndpointPath = new PathString("/account/login"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new JwtAuthorizationServerProvider(),
AccessTokenFormat = new CustomJwtFormat("https://foo.test.com")
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
Another issue you might face is getting the token to SignalR, where setting the Authorization header is not as straight forward as you might think. As it happens the cookie based implementation in this tutorial worked beautifully with JWT as well! http://blog.marcinbudny.com/2014/05/authentication-with-signalr-and-oauth.html#.VmWgMXarSCd
Again here is the OWIN config example.
public void ConfigureOAuth(IAppBuilder app)
{
//app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
//{
// Provider = new ApplicationOAuthBearerAuthenticationProvider()
//});
var issuer = "https://foo.test.com";
var audience = "client_id";
var secret = TextEncodings.Base64Url.Decode("ABCDEF");
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audience },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
},
Provider = new ApplicationOAuthBearerAuthenticationProvider()
});
}
FWIW, consider that self-hosted OWIN authorization servers use DPAPI protection, but ASP.NET apps default to MachineKey data protection.
If you need to make those two collaborate, in the OWIN config you can specify a provider like this:
app.SetDataProtectionProvider(new DpapiDataProtectionProvider("myApp"));
Just make sure to add it in both Configuration methods for the IAppBuilder (Both projects)
HTH

External Cookie for External Login in ASP.NET OWIN

We have a legacy system which is built on ASP.NET Mvc 4, now we would like to support Signal Sign On via Azure Active Directory for current users as well as new users. Since we have managed our own authentication workflow, ASP.NET Identity definitely does not fit in our case.
I have managed to build a demo which is working on OWIN OpenIdConnect middleware passive mode without using ASP.NET Identity. The below code works correctly:
app.SetDefaultSignInAsAuthenticationType("ExternalCookie");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ExternalCookie",
AuthenticationMode = AuthenticationMode.Passive,
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Passive,
ClientId = ClientId,
Authority = Authority
// More code
});
And in ExternalLoginCallback action:
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var authManager = Request.GetOwinContext().Authentication;
var result = await authManager.AuthenticateAsync("ExternalCookie");
authManager.SignOut("ExternalCookie");
//More code to convert to local identity
}
This case is really common even using other providers like Google, Facebook or Twitter. One thing I have not much clear is ExternalCookie, maybe I have missed the whole thing. My understanding is when external login is successfully, external cookie is used to store the external claim identity. And then we call:
var result = await authManager.AuthenticateAsync("ExternalCookie");
authManager.SignOut("ExternalCookie");
In order to get the external claim identity and then convert external identity to local identity. I have a little bit confusion why we have to call SignOut external cookie in this case.
Also, I'm not sure whether External Cookie is a must when using external login, or do we have other ways around without using External Cookie.
Please someone give an explanation on this point.
To answer your last question, you change the name of cookie in startup.auth file where you configure external cookie -
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
You can use a string instead of DefaultAuthenticationTypes enum and directly specify the name of the cookie like -
app.UseExternalSignInCookie("myExternalCookie");

Proper way to authenticate to Web API from SPA MVC app?

So, I'm not super strong on the intricancies of the new OWIN framework and have the following issue:
I have a SPA web application (latest MVC, AngularJs), latest OWIN, Identity framework is connected to RavenDB user store (using Brock's provider)..
Need: Login user thru username/password or thru Twitter/Fb/LiveId on the SPA website and have that action authenticate future Web API calls made from the browser in a secure fashion. During authentication, I need to pass a number of claims to Web API so that it knows not only who the user is, but also his/her permissions.
Furthermore, need to have Web API be called as an API with use of API Keys (this part I think I got).
What's the proper way to do this? I'm also planning to have mobile apps connect to the Web API in the future too. Been reading numerous articles and my head is spinning already.
Issue: I thought using cookie authentication mode would do it for me. I've got the setup working for Visual Studio debugging mode and thought I was good. However, when I deployed into Azure, I've started getting 401 Unauthorized from API calls. Both the SPA website and API are running under the same root domain but different subdomains. I'm not using any cookie domain setting when testing locally from Visual Studio. I'm using root domain for cookies when in Azure.
Here's my SPA's MVC application ConfigAuth:
public void ConfigureAuth(IAppBuilder app)
{
var decryptor = new SettingsEncryption();
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
LogoutPath = new PathString("/Account/Logout"),
CookieDomain = CloudConfigurationManager.GetSetting("AuthCookieDomain"),
CookieSecure = CookieSecureOption.Always,
//CookiePath = "/",
CookieHttpOnly = true,
ExpireTimeSpan = TimeSpan.FromDays(365),
SlidingExpiration = true,
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseMicrosoftAccountAuthentication(
clientId: decryptor.Decrypt(CloudConfigurationManager.GetSetting("LiveIdOAuthAppId")),
clientSecret: decryptor.Decrypt(CloudConfigurationManager.GetSetting("LiveIdOAuthSecretKey")));
app.UseTwitterAuthentication(
consumerKey: decryptor.Decrypt(CloudConfigurationManager.GetSetting("TwitterOAuthAppId")),
consumerSecret: decryptor.Decrypt(CloudConfigurationManager.GetSetting("TwitterOAuthSecretKey")));
app.UseFacebookAuthentication(
appId: decryptor.Decrypt(CloudConfigurationManager.GetSetting("FacebookOAuthAppId")),
appSecret: decryptor.Decrypt(CloudConfigurationManager.GetSetting("FacebookOAuthSecretKey")));
app.UseGoogleAuthentication(
clientId: decryptor.Decrypt(CloudConfigurationManager.GetSetting("GoogleOAuthAppId")),
clientSecret: decryptor.Decrypt(CloudConfigurationManager.GetSetting("GoogleOAuthSecretKey")));
}
Here's my Web API ConfigAuth:
app.SetDefaultSignInAsAuthenticationType(DefaultAuthenticationTypes.ExternalCookie);
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieDomain = CloudConfigurationManager.GetSetting("AuthCookieDomain"),
CookieSecure = CookieSecureOption.Always,
//CookiePath = "/",
CookieHttpOnly = true,
ExpireTimeSpan = TimeSpan.FromDays(365),
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider
{
OnApplyRedirect = ApplyRedirect
},
});
//app.UseExternalSignInCookie(DefaultAuthenticationTypes.ApplicationCookie);
// Uncomment the following lines to enable logging in with third party login providers
app.UseMicrosoftAccountAuthentication(
clientId: decryptor.Decrypt(CloudConfigurationManager.GetSetting("LiveIdOAuthAppId")),
clientSecret: decryptor.Decrypt(CloudConfigurationManager.GetSetting("LiveIdOAuthSecretKey")));
app.UseTwitterAuthentication(
consumerKey: decryptor.Decrypt(CloudConfigurationManager.GetSetting("TwitterOAuthAppId")),
consumerSecret: decryptor.Decrypt(CloudConfigurationManager.GetSetting("TwitterOAuthSecretKey")));
app.UseFacebookAuthentication(
appId: decryptor.Decrypt(CloudConfigurationManager.GetSetting("FacebookOAuthAppId")),
appSecret: decryptor.Decrypt(CloudConfigurationManager.GetSetting("FacebookOAuthSecretKey")));
app.UseGoogleAuthentication(
clientId: decryptor.Decrypt(CloudConfigurationManager.GetSetting("GoogleOAuthAppId")),
clientSecret: decryptor.Decrypt(CloudConfigurationManager.GetSetting("GoogleOAuthSecretKey")));
var config = GlobalConfiguration.Configuration;
config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
Furthermore, a global handler has been enabled on the Web API to auth every call. (inherits from DelegatingHandler, checks Headers for API Key if it exists or calls AssertAuth() otherwise)
Well I've composed a tutorial covering the scenario you want to achieve, the tutorial is cookie-less and it depends only on bearer tokens which is the right way when you want to build SPA, you can check the series of posts here http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/

Why does Owin Startup order affect Cookie Authentication

I have Owin configured to issue both a token and cookie upon authentication:
public void Configuration(IAppBuilder app)
{
var cookieOptions = new CookieAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
CookieHttpOnly = true, // JavaScript should use the Bearer
//AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieName = "MyCookie",
LoginPath = new PathString("/app/index.html#/login"),
};
var oAuthServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new MyAuthorizationServerProvider(),
};
var oAuthBearerOptions = new OAuthBearerAuthenticationOptions
{
};
// Must be registered in this order!
app.UseCookieAuthentication(cookieOptions);
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(oAuthBearerOptions);
}
This works fine - it issues both the Bearer token for my SPA to call my API and the cookie so my old school MVC pages can be logged in too.
But if I register the OAuth server before declaring that I want to use CookieAuth, no cookie is issued. In other words if I do this, it doesn't work:
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseCookieAuthentication(cookieOptions);
app.UseOAuthBearerAuthentication(oAuthBearerOptions);
Also, if I uncomment this line, it also doesn't issue the cookie:
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
My question is why does the order of registration with Owin matter? And why does setting the AuthenticationType in the cookie as "ApplicationCookie" also make it fail?
I'm not familiar with the UseOAuthAuthorizationServer() middleware, but I assume it works the same as other external authentication middleware (e.g. the Google middleware).
An authentication middleware that redirects to an external source for authentication, will only be used once, at the start, of each browsing session. It will then defer the authentication of the up-coming requests to a cookie authentication that maintains the session. This is good, because it means the overhead of the external authentication is only done once for each session.
A middleware that wants to set a cookie does typically not do it itself. Instead it sets a property in the Owin context with an AuthenticationResponseGrant. The grant is then processed by the cookie middleware that extracts the identity and sets the cookie.
For this to work:
The cookie handler must be registered before the external authentication middleware in the pipeline.
The authentication type in the AuthenticationResponseGrant must match the type of the cookie middleware.
So changing the order of the registration violates 1. and excluding the authentication type violates 2.
I have written an in depth blog post about it if you want more details.

Resources