UseGoogleAuthentication force login session expiration? - asp.net

I'm using ASP.NET MVC 5's external authentication middleware UseGoogleAuthentication/UseExternalSignInCookie with GoogleOAuth2AuthenticationOptions. Is there a way to force a user to have to re-authenticate with Google each time a user visits the site?
Presently, if the user is already logged into Google and they access the site they do not have to re-authenticate with Google. Ideally the cookie assigned would only be good for their current session on the site...
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
var authOptions = new GoogleOAuth2AuthenticationOptions();
authOptions.ClientId = AppSettingsHelper.GoogleClientId;
authOptions.ClientSecret = AppSettingsHelper.GoogleClientSecret;
authOptions.CallbackPath = new PathString("/account/linklogincallback");
foreach (var scope in AppSettingsHelper.GoogleOAuthScope)
{
authOptions.Scope.Add(scope);
}
app.UseGoogleAuthentication(authOptions);

Replace app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); with app.UseCookieAuthentication(..) and specify the ExpireTimeSpan. UseExternalSignInCookie is just a helper for the cookie authentication method that uses certain defaults.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
SlidingExpiration = true,
ExpireTimeSpan = new System.TimeSpan(0, 5, 0),
LoginPath = new PathString("/Account/Login")
});
Notice that we are using DefaultAuthenticationTypes.ExternalCookie here instead of ApplicationCookie

Related

User unauthorized after Azure AD login to different application simultaneously

I have two MVC applications AppA and AppB, and implemented Azure AD authentication for login.
I am able to sign-in successfully to both applications.
But the issue is, after I login to AppA and then to AppB, after sometime when I return back to AppA I am facing the issue where user has been logged out, and it again redirects to login screen (in AppA).
After I login to AppA (second time) and go back to AppB (user in AppB is logged out).
Client IDs are different ; TenandID is same. Both apps are hosted in same server.
Startup file:
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
SlidingExpiration = true,
Provider = new CookieAuthenticationProvider
{
OnResponseSignIn = context =>
{
context.Properties.AllowRefresh = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(1);
},
OnValidateIdentity = MyCookieValidateIdentity
},
ExpireTimeSpan = TimeSpan.FromDays(2)
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = appId,
//CookieManager=new SameSiteCookieManager(new SystemWebCookieManager()),
Authority = "https://login.microsoftonline.com/xxxxxx/v2.0",
Scope = $"openid email profile offline_access {graphScopes}",
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
},
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.DomainHint = "xyz.com";
return Task.FromResult(0);
},
// SecurityTokenValidated = OnSecurityTokenValidated,
AuthenticationFailed = OnAuthenticationFailedAsync,
AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync
}
}
);
}
actionContext.RequestContext.Principal.Identity.IsAuthenticated is returning False
I am assuming it has to do something with the cookie. Can someone please help resolve this ?
Edit:
Debugged further and found:
Initially if the cookies for AppA are set as:
.AspNet.Cookies = A_abc123 ; ASP.NET_SessionId = A_def456
And for AppB .AspNet.Cookies = B_mno123 ; ASP.NET_SessionId = B_pqr456
Then after I click any link in AppA, the cookie's values are updated with AppB's cookies, i.e. .AspNet.Cookies = B_mno123 ; ASP.NET_SessionId = B_pqr456
.AspNet.Cookies ASP.NET_SessionId
AppA A_abc123 A_def456
AppB B_mno123 B_pqr456
AppA B_mno123 B_pqr456
One thing that you need to do is to configure the Data Protection API so that both services uses the same cookie protection key. Out of the box each service creates its own unique key, and a cookie from one service is not valid in a different service.
I also did a blog post about the data protection API here.
See
How to: Use Data Protection
Get started with the Data Protection APIs in ASP.NET Core
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
//AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,// DefaultAuthenticationTypes.ApplicationCookie,
CookieName = ".AspNet.AppA.Cookies",
SlidingExpiration = true,
CookieManager = new SystemWebCookieManager(),
Provider = new CookieAuthenticationProvider
{
OnResponseSignIn = context =>
{
context.Properties.AllowRefresh = true;
context.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddDays(1);
},
},
ExpireTimeSpan = TimeSpan.FromDays(2)
});
//... code removed for brevity //
}
The Default Cookie Name set by the application was: .AspNet.Cookies
And when I modified the default cookie name, the issue got resolved. Each application was generating its own cookiename and hence the other application was not signing out the user.

Protocol to implement Azure AD

I have a legacy ASP.NET Web Forms application. It is at present using on-prem ADFS with Cookie Authentication and WSFederation protocol.
We want to move it to Azure AD. I want to know whether I need to change WSFederation protocol or it too works with Azure AD. Also, is it required to change Cookie Authentication?
Code from Startup.CS is as below:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
//interactive logon process
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
//name of the authentication type
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
//TODO: Enable this to always send and receive cookies in SSL when in production
CookieSecure = CookieSecureOption.Always,
//enable sliding expiration
SlidingExpiration = true,
//Cookie expires in 4 hours
ExpireTimeSpan = TimeSpan.FromTicks(DateTime.Now.AddHours(4).Ticks)
});
app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions
{
MetadataAddress = adfsMetadata,
Wtrealm = realm
});
Edited *
Code modified as below:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
//interactive logon process
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
//name of the authentication type
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
//Login path should be below
// LoginPath = new PathString("login"),
//TODO: Enable this to always send and receive cookies in SSL when in production
CookieSecure = CookieSecureOption.Always,
//enable sliding expiration
SlidingExpiration = true,
//Cookie expires in 4 hours
ExpireTimeSpan = TimeSpan.FromTicks(DateTime.Now.AddHours(4).Ticks)
});
Microsoft.IdentityModel.Logging.IdentityModelEventSource.ShowPII = true;
app.UseWindowsAzureActiveDirectoryBearerAuthentication(new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
MetadataAddress = AzureMetaData,
Tenant = Tenant,
Realm = Realm
});
Now it is throwing error:
System.ArgumentNullException: 'Value cannot be null. Parameter name:
allowedAudience'
Yes, Azure AD supports WS-Fed. Ref: Integrating a web app with Azure AD using WS-Federation
For allowedAudiences, the value of this field has to match what is in the "audience" field of the token that is being sent to your service. You can go to the Azure AD app registration for your service and look in the manifest at the "identifierUris" field. The value here should match the value that you put in the Allowed Token Audiences list.
You can also go to https://resources.azure.com/ > drill down into the App Service resource > config > authsettings and correct allowedAudiences value:
"allowedAudiences":[
"https://mysite.azurewebsites.net"
]

ASP.NET OWIN Custom Cookie Authentication

We are running a classic asp web application, and want to it to work together with new developed MVC application. We want to make use of the authentication of the classic asp app in the MVC application.
The idea is when user log into the classic asp app, it will issue kind of auth cookie, the cookie is encrypted in our own method. Cookie will contain use identity.
Client then browse to the MVC app along with this auth cookie. The MVC app will check if the cookie present and validate it. With it is not redirect to the classic asp login page.
So I'm thinking to customize the OWIN cookie authentication to use my own authentication logic. I tried to implement the CookieAuthenicationProvider however I don't know where to put my logic.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
CookieName = ".classicauth",
CookieSecure = CookieSecureOption.SameAsRequest,
CookieHttpOnly = true,
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = context => {
//?? where I can extract the cookie and validate it??
context.RejectIdentity();
return Task.FromResult<int>(0);
},
OnApplyRedirect = context => {
context.Response.Redirect("classic_asp_login_url");
}
}
});
The CookieAuthenticationProvider have a OnValidateIdentity, however it seem not the right place to extract cookie and validate it.
Thanks.
Jason.
I haven't tested it my self in that particular context. But CookieManager works for me.
OnValidateIdentity = context => {
var cookie = context.Options.CookieManager.GetRequestCookie(context.OwinContext, context.Options.CookieName);
context.RejectIdentity();
return Task.FromResult<int>(0);
},

Make custom request when auth session is expired or user logged out

In one MVC project, I implemented asp.net identity based on cookies. Now I have a requirement to make a request to remote service when auth session is expired or when user logged off.
Is there any natural way to accomplish that? For now I managed to set two delegate properties from CookieAuthenticationProvider like below:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Login"),
CookieSecure = CookieSecureOption.SameAsRequest,
ExpireTimeSpan = TimeSpan.FromMinutes(expireInMinutes),
CookiePath = cookiePath,
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = c =>
{
if (c.Properties.ExpiresUtc.HasValue && c.Properties.ExpiresUtc.Value < c.Options.SystemClock.UtcNow)
{
c.RejectIdentity();
c.Request.Context.Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return Task.FromResult(0);
}
if (c.Options.SlidingExpiration)
{
// Reissue the auth cookie
}
return Task.FromResult(0);
},
OnResponseSignOut = c =>
{
// Make a custom request
}
}
});
At first glance it looks like it works but I don't like the idea of checking expiry date in here. Problem is that OnResponseSignOut is not called when auth cookie is simply expired but is called only when I explicitly call IAuthenticationManager.SignOut.
Is creating a custom CookieAuthenticationProvider the best option in here, or maybe there is another clean and natural solution for that case?

How do I ignore the Identity Framework magic and just use the OWIN auth middleware to get the claims I seek?

The OWIN middleware stuff to integrate third-party logins to your ASP.NET app is very cool, but I can't seem to figure out how to tear it out from the new ID framework that replaces the crappy Membership API. I'm not interested in persisting the resulting claims and user info in that EF-based data persistence, I just want the claims info so I can apply it to my own user accounts in existing projects. I don't want to adopt the new ID framework just to take advantage of this stuff.
I've been browsing the code on CodePlex, but there's a whole lot of static magic. Can you offer any suggestions?
Use the following code to setup OWIN security middlewares:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Application",
AuthenticationMode = AuthenticationMode.Passive,
LoginPath = new PathString("/Login"),
LogoutPath = new PathString("/Logout"),
});
app.SetDefaultSignInAsAuthenticationType("External");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "External",
AuthenticationMode = AuthenticationMode.Passive,
CookieName = CookieAuthenticationDefaults.CookiePrefix + "External",
ExpireTimeSpan = TimeSpan.FromMinutes(5),
});
app.UseGoogleAuthentication();
The code above sets up application cookie, external cookie and Google external login middlewares. External login middleware will convert external user login data as identity and set it to external cookie middleware. In your app, you need to get external cookie identity and convert it to external login data, then you can check it with your db user.
Here are some sample code.
Sign in with application cookie:
var authentication = System.Web.HttpContext.Current.GetOwinContext().Authentication;
var identity = new ClaimsIdentity("Application");
identity.AddClaim(new Claim(ClaimTypes.Name, "<user name>"));
authentication.AuthenticationResponseGrant = new AuthenticationResponseGrant(identity, new AuthenticationProperties() {
IsPersistent = false
});
Get application cookie identity:
var identity = System.Web.HttpContext.Current.User.Identity as ClaimsIdentity;
Get external cookie identity (Google):
var authentication = System.Web.HttpContext.Current.GetOwinContext().Authentication;
var result = await authentication.AuthenticateAsync("External");
var externalIdentity = result.Identity;
Extract external login data from identity:
public static ExternalLoginData FromIdentity(ClaimsIdentity identity)
{
if (identity == null)
{
return null;
}
Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier);
if (providerKeyClaim == null || String.IsNullOrEmpty(providerKeyClaim.Issuer)
|| String.IsNullOrEmpty(providerKeyClaim.Value))
{
return null;
}
if (providerKeyClaim.Issuer == ClaimsIdentity.DefaultIssuer)
{
return null;
}
return new ExternalLoginData
{
LoginProvider = providerKeyClaim.Issuer,
ProviderKey = providerKeyClaim.Value,
UserName = identity.FindFirstValue(ClaimTypes.Name)
};
}

Resources