Why does Owin Startup order affect Cookie Authentication - asp.net-mvc

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.

Related

how to set up an asp.net mvc app for AD B2C "auth code flow" authentication

It states here
that going forward, we must prefer the auth code flow method since "With the plans for third party cookies to be removed from browsers, the implicit grant flow is no longer a suitable authentication method."
I have set up an asp.net mvc 4 web application according to the sample app here. It works when I set up my app registration in AD B2C directory for Access Tokens (used for implicit grant flows). If I switch to "ID Tokens (for implicit and hybrid flows)" as the documentation recommends, I get error that my application is not setup for it.
As I understand from the documentation, I would have to specifiy separate endpoins for /authorize and/token to fetch a token after authorization. I am not sure from looking at the sample though how exactly I can do this. Below is the ConfigureAuth method as you can see in the sample code on github link provided:
public void ConfigureAuth(IAppBuilder app)
{
// Required for Azure webapps, as by default they force TLS 1.2 and this project attempts 1.0
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
// ASP.NET web host compatible cookie manager
CookieManager = new SystemWebChunkingCookieManager()
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
// Generate the metadata address using the tenant and policy information
MetadataAddress = String.Format(WellKnownMetadata, Tenant, DefaultPolicy),
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = ClientId,
RedirectUri = RedirectUri,
PostLogoutRedirectUri = RedirectUri,
// Specify the callbacks for each type of notifications
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
},
// Specify the claim type that specifies the Name property.
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
ValidateIssuer = false
},
// Specify the scope by appending all of the scopes requested into one string (separated by a blank space)
Scope = $"openid profile offline_access {ReadTasksScope} {WriteTasksScope}",
// ASP.NET web host compatible cookie manager
CookieManager = new SystemWebCookieManager()
}
);
}

Can I use two authentication methods in Owin?

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

Using An OAuth Bearer Token (JWT) With MVC

I've created a backend WebApi to create JWT tokens and they're working fine when I use PostMan to access restricted resources by adding the token to the header, e.g. [Authorise(Roles="SuperAdmin")].
I want to use this infrastructure with my MVC app, but don't quite know how to tie it together.
I'm guessing that when the user creates an account and I generate a JWT for them (Via the WebApi), I need to stick the token in a cookie, but how do this and also extract the JWT from the cookie on future requests, so that it will work with the normal [Authorize] attribute that I decorate the ActionResults with?
Do I need to put something in the Owin pipeline?
Or do I need to create a custom [Authorize] attribute?
My Startup.cs file currently looks like this:
public void Configuration(IAppBuilder app)
{
HttpConfiguration httpConfig = new HttpConfiguration();
ConfigureOAuthTokenGeneration(app);
ConfigureOAuthTokenConsumption(app);
ConfigureWebApi(httpConfig);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(httpConfig);
}
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//TODO: enforce https in live
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat("https://localhost:443")
};
// Plugin the OAuth bearer JSON Web Token tokens generation and Consumption will be here
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var issuer = "https://localhost:443";
string audienceId = ConfigurationManager.AppSettings["as:AudienceId"];
byte[] audienceSecret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["as:AudienceSecret"]);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
}
private void ConfigureWebApi(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
If it helps, I was following this guide:
http://bitoftech.net/2015/02/16/implement-oauth-json-web-tokens-authentication-in-asp-net-web-api-and-identity-2/
The infrastructure your referred to is really designed to handle direct web API calls. A classic redirect based web app would fall back on more traditional patterns, where the app receives one token, validates it and uses it to initiate an authenticated session (by saving the results of the token validation in some session artifact, like a token). Although you can implement this patters starting form any token based system, including your custom one, usually it's more convenient (and secure) to leverage existing protocols (like OpenId Connect) and existing products (like Azure AD or Identity Server). See this for a simple example based on Azure AD - the middleware remains the same no matter what OpenId Provider you pick.

CookieAuthenticationOptions.LoginPath value not used when also using app.UseOpenIdConnectAuthentication

I am using OWIN middleware for cookie authentication and openIdConnect. Before I added openIdConnect authentication to my startup auth code the cookie authentication option, LoginPath was used as the destination for redirecting unauthenticated users. This worked really well and is the functionality I would like to keep.
However, when I added app.UseOpenIdConnectAuthentication to my project, it started automatically redirecting unauthenticated users to my OpenIdConnect Authority (https://login.windows.net/).
Is there a way I can disable OpenIdConnectAuthentication setting the redirect path for unauthenticated users and rely on the LoginPath set for cookie authentication? My current work around is to manually set the redirect path in my authorize attribute, but I would like to let OWIN middleware handle this if possible.
Thanks.
Code:
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
var cookieOptions = new CookieAuthenticationOptions();
cookieOptions.AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie;
cookieOptions.LoginPath = new PathString("/Account/Login");
app.SetDefaultSignInAsAuthenticationType(cookieOptions.AuthenticationType);
app.UseCookieAuthentication(cookieOptions);
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = FranchiseAuthType,
ClientId = franchiseClientId,
Authority = FranchiseAuthority,
PostLogoutRedirectUri = postLogoutRedirectUri,
});
}
I'm not sure if you were able to resolve this issue, but what you want to do it is add
AuthenticationMode = AuthenticationMode.Passive
To your authentication options. This will make the OpenIdConnect authentication rely solely on your code to make calls to it. I believe this is what you intend to happen.
So your new code should look like this:
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
var cookieOptions = new CookieAuthenticationOptions();
cookieOptions.AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie;
cookieOptions.LoginPath = new PathString("/Account/Login");
app.SetDefaultSignInAsAuthenticationType(cookieOptions.AuthenticationType);
app.UseCookieAuthentication(cookieOptions);
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = FranchiseAuthType,
AuthenticationMode = AuthenticationMode.Passive,
ClientId = franchiseClientId,
Authority = FranchiseAuthority,
PostLogoutRedirectUri = postLogoutRedirectUri,
});
}
EDIT: This looked like it fixed my problems but it caused a more serious issue than the one it fixed. If I set use cookies after use open Id connect then my openidconnect notifications will have a null sessions HttpContext.Current.Session and after authentication my authentication result is not stored in the cookie.
Whichever authentication is added last becomes the authoritative source for setting the redirect path for unauthenticated uses.
By moving
app.UseCookieAuthentication();
After app.UseOpenIdConnectAuthentication() the desired behavior was achieved.
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
var cookieOptions = new CookieAuthenticationOptions();
cookieOptions.AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie;
cookieOptions.LoginPath = new PathString("/Account/Login");
app.SetDefaultSignInAsAuthenticationType(cookieOptions.AuthenticationType);
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = FranchiseAuthType,
ClientId = franchiseClientId,
Authority = FranchiseAuthority,
PostLogoutRedirectUri = postLogoutRedirectUri,
});
//move this after UseOpenIdConnectAuthentication and the LoginPath
//value is used for redirecting unauthenticated users
app.UseCookieAuthentication(cookieOptions);
}

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/

Resources