How to handle expiration of refresh token - c#

Hi I'm fiddling around Identity server 4. I went through some vids on plural sight and the instructor went through some code on how to refresh my access token using the refresh token.
My question is what happens when the refresh token expires? I tried to see what will happen by setting AbsoluteRefreshTokenLifetime=15 but I just get an error when the time elapses i.e. when I try to get data from the resource server. I would expect a redirect to the login page
What is the correct way to handle an expired refresh token? If the correct way is to force the user to login then please provide the code to do this as ids4 does not do this by default. I cannot seem to get it to work.
new Client
{
ClientId = "mvc2",
ClientName = "MVC Client2",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5001/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5001/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
AllowOfflineAccess = true,
RequireConsent = false,
AccessTokenLifetime = 15,
AbsoluteRefreshTokenLifetime = 15,
IdentityTokenLifetime = 15
}

The scope of the refresh token is offline_access. This means that the client can request access tokens on behalf of the user without having to interact with the user.
Depending on the flow, when the user logs in, the client will receive three tokens: the access token, the identity token and the refresh token.
Being an automated (offline) process, there is no login page. There is no active user, so the return is simply Unauthorized. Meaning that once expired the user has to login again to start the proces again.
It is possible that the user can still access the website (using the cookies) while the api is now unaccessable. You can simulate this with the hybrid flow sample.
Using the configuration of IdentityServer you have several options. You can choose sliding expiration, meaning that the refresh token 'never expires'. And you can set it to one time only use.
I believe that by default the token is set to one time use only, which will trigger IdentityServer to add a new refresh token each time a new access token is requested. Setting the fixed expiration will make sure that the user must login every now and then.
In terms of lifetime:
access token < refresh token <= AbsoluteRefreshTokenLifetime.
Just make sure the refresh token doesn't expire and isn't lost.
If you have a scenario where the refresh token was lost or expired, then logout the user. Go to a secured page which will redirect the user to the login page.
This depends on your configuration, but you could do something like this to logout the user:
await HttpContext.SignOutAsync("Cookies");
return LocalRedirect($"/secured");
In that case the user may not have to login as the user can still be active because of the SSO cookie.
If you want a full logout (all websites):
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");

Related

How to Invalidate AspNetCore.Identity.Application Cookie after user log out

I am having trouble invalidating .AspNetCore.Identity.Application cookie in ASP.NET Core Identity once the user log out.
Once user clicks on log out below code will execute.
public async Task<IActionResult> Logout(LogoutInputModel model)
{
// build a model so the logged out page knows what to display
LoggedOutViewModel loggedOutViewModel = await BuildLoggedOutViewModelAsync(model.LogoutId);
_logger.LogInformation($"loggedOutViewModel : {JsonConvert.SerializeObject(loggedOutViewModel)}");
if (User?.Identity.IsAuthenticated == true)
{
// delete local authentication cookie
await _norskTakstSignInManager.SignOutAsync();
//clear cookies
var appCookies = Request.Cookies.Keys;
foreach (var cookie in appCookies)
{
Response.Cookies.Delete(cookie);
}
// raise the logout event
await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
}
// check if we need to trigger sign-out at an upstream identity provider
if (loggedOutViewModel.TriggerExternalSignout)
{
// build a return URL so the upstream provider will redirect back
// to us after the user has logged out. this allows us to then
// complete our single sign-out processing.
string url = Url.Action("Logout", new { logoutId = loggedOutViewModel.LogoutId });
// this triggers a redirect to the external provider for sign-out
return SignOut(new AuthenticationProperties { RedirectUri = url }, loggedOutViewModel.ExternalAuthenticationScheme);
}
return View("LoggedOut", loggedOutViewModel);
}
This successfully clears all the cookies in the browser, however, if I grab the value of the cookie named ".AspNetCore.Identity.Application" prior to signing out, then add it back in on to the browser, then i can log in to the application without entering user credentials.
I tested few flows setting up cookie expiration time in different ways but non of them seem to work correctly.
I want to know way to invalidate the cookie without just clearing to resolve this issue.Then user should not be able to enter cookie manually and log in to the system. Any help is hugly appreciated. Thank you.
That's by design... one thing you can do is try updating the user's security stamp after logout, using UserManager.UpdateSecurityStampAsync.
This way the cookie's security stamp won't match the one in the database and the cookie will no longer be valid (however, no other cookie issued to that user will, even if they haven't "signed out"... so if a user has several sessions opened, all of those cookies will stop being valid, not just the one you signed out).
Identity doesn't track specific user sessions (it just validates the cookie against the user, and if it matches, it matches). If you want to be able to selectively remove sessions, you'll have to track them yourself
For me the best security practice is save every login and logout in one record with an unique random ID as GUID, then save this "id session" into the claims, and check this everytime the user access, if the ID in the claim is correct to that session.

Use Refresh Token When Access Token is Expired

I'm using OAuth in .Net-Core 2.1 to Login to Coinbase, I've configured my authenticaton like so:
services.AddAuthentication(COOKIE_AUTH)
.AddCookie(options => options.ExpireTimeSpan = TimeSpan.FromMinutes(60))
.AddCoinbase(options => {
options.SendLimitAmount = 1;
options.SendLimitCurrency = "USD";
options.SendLimitPeriod = SendLimitPeriod.day;
options.ClientId = Configuration["Coinbase:ClientId"];
options.ClientSecret = Configuration["Coinbase:ClientSecret"];
COINBASE_SCOPES.ForEach(scope => options.Scope.Add(scope));
options.SaveTokens = true;
options.ClaimActions.MapJsonKey("urn:coinbase:avatar", "avatar_url");
});
Using Postman I see that I'm getting an access token and a refresh token. My token expires within two hours and never refreshes.
I know I can manually refresh the token, but I would expected this to be build into .net some where
Is there a way to refresh my token built into .net?
This doesn’t make sense since the client is responsible for sending a valid token in order to expect the request to be authorized. When clients typically send tokens , they typically do so in a header. That header only contains a single access token, not a refresh token. Instead the refresh token is persisted at the client and used to get an access token that IS valid. Then it can make a different request and expect a different outcome. The flow is important.

When using a HTTP-only session cookie, how does the client know when its session is going to expire?

My website is an AngularJS SPA with a Web API/SignalR back-end running on Owin. Authentication was managed with a JSON Web Token (JWT) stored in the browser's local storage.
Per corporate directive, I'm moving from JWT in local storage to HTTP-only same-site session cookies with a sliding expiration. Everything works great, except for one snag:
My application displays personal health information (PHI), so I must close the application automatically as soon as the session expires. With JWT, I could inspect the "exp" claim to automatically determine that the session has expired and remove the PHI from the screen. But with a HTTP-only cookie, I can't access any part of the token.
When I issue the cookie, I know the expiration and can inform the browser accordingly. However, when Owin refreshes the cookie, the browser will need to be notified of the new expiration, and I'm not sure how to do that.
I could store a second cookie that isn't HTTP-only, containing only the session expiration time, but I would have to refresh that cookie whenever I refresh my primary auth cookie.
How do I tell the browser when the session is going to expire, and keep the browser updated when that expiration changes? How can I attach an event handler to when the cookie is refreshed by Owin?
Here is my current cookie configuration:
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationMode = AuthenticationMode.Active,
CookieHttpOnly = true,
ExpireTimeSpan = TimeSpan.FromHours(2),
SlidingExpiration = true,
CookieName = "Auth"
});
Quite reliable and simple approach with HTTP is making endoint for this and check it i.e. once per second:
module.run(($interval) = > {
$interval(() => {
$http.get('/checkToken').then((result) => {
if (result.data.expired) {...}
}, (error) => ...)
}, 1000);
})
And on server in checkToken endpoint you can do all needed cheks.
For better real-time client-server interaction you may consider using sockets, but this is another story.
I'm not intimately familiar with Angular or Owin, but working with legally-protected education data, the we solve it by ticking down the seconds, then resetting in the handler after an AJAX call completes.
A bare-bones version looks something like this:
var Countdown = {};
var Countdown.length = 3600 /* your session timeout here */;
var Countdown.seconds = Countdown.length;
var Countdown.tick = function() {
Countdown.seconds--;
if (Countdown.seconds == 0) {
/* handle timeout */
}
/* any additional processing code here */
}
var Countdown.reset = function() {
Countdown.seconds = Countdown.length;
/* any additional processing code here */
}
window.setInterval(Countdown.tick, 1000);

Combine server-side and client-side authentication with WebAPI

I have a legacy ASP.NET webforms application in which users login via a form that is processed server-side. If the entered username + password match to credentials in the database, I set some values in the sessions (e.g., the current user ID) and perform a Response.Redirect afterwards. I'm also creating a HttpCookie for a "automatically relog me next time I visit" functionality.
Currently, I'm also adding WebApi support into that web application. I've managed to implement token authentication which allows me to login on the client side.
How can I combine both authentication approaches? I want to the user to enter his credentials once, get authenticated on the server side and on the client side an redirect the users to another page after authenticating.
The following code will create a cookie to keep user logged in.
// login etc
if (chkRemember.Checked)
{
// calculate the total number of minutes in 20 days to use as the time out.
int timeout = (int)TimeSpan.FromDays(30).TotalMinutes;
// create an authentication ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(txtUserName.Text, true, timeout);
// Encrypt the ticket
string encrptedTicked = FormsAuthentication.Encrypt(ticket);
// create the cookie for the ticket, and put the ticket inside
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encrptedTicked);
// give cookie and ticket same expiration
cookie.Expires = ticket.Expiration;
// Attach cookie to current response. it will now to the client and then back to the webserver with every request
HttpContext.Current.Response.Cookies.Set(cookie);
// send the user to the originally requested page.
string requestedPage = FormsAuthentication.GetRedirectUrl(txtUserName.Text, false);
Response.Redirect(requestedPage, true);
}
else
{
// login without saving cookie to client
FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, false);
}
You can use token based authentication in webapi using Angular JS. Visit following link
http://www.dotnetcurry.com/aspnet/1223/secure-aspnet-web-api-using-tokens-owin-angularjs

Session Expiring in 1 hour due to Access Token in Azure Ad OpenIdConnectAuthentication

In our application we are using Azure Ad OpenIdConnectAuthentication to sign in which will redirect to "https://login.microsoftonline.com/" when calling our application
I think some reason refresh tokens are not generating in our single page application and forcing the user to sign out after 1 hour because of the access token(which will expire in 1 hour).
I've read a too many blogs but I can't get my answers.Any suggestion much appreciated.
Here is my code :
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = Authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthorizationCodeReceived = (context) =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string userObjectID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new CustomTokenCache(userObjectID));
//getting the tokens from below line (Access token with expiry time)
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
return Task.FromResult(0);
},
AuthenticationFailed = context =>
{
context.HandleResponse();
context.Response.Redirect("/");
return Task.FromResult(0);
}
}
We usually acquiring the token via the implicit flow instead of authorization code grant flow for the SPA application.
The token will return from the authorization endpoint directly instead of from token endpoint. And we can enable it by modify the app's manifest oauth2AllowImplicitFlow property to true to enable the implicit flow.
To renew the access token when it is expired in the implicit flow, we can perform the a hidden iframe request and add the prompt parameter and set its value to none so that users not required to enter their credential again.
To develop easily, we can use the ADAL library provided by Microsoft for the SPA application. We can renew the token via the AuthenticationContext.prototype._renewToken methoed.
More detail about implicit flow, you may refer the links below:
Authentication Scenarios for Azure AD -(Single Page Application (SPA) section)
Understanding the OAuth2 implicit grant flow in Azure Active Directory (AD)
v2.0 Protocols - SPAs using the implicit flow
If using the Authorization Code grant flow you still can solve this problem by requesting a refresh token. The recent versions of ADAL automatically handles refreshing the access token if it it has expired. The OpenIDConnect request should contain the 'offline_access' scope within it's Scope parameter. OpenIdConnectAuthenticationOptions in your question has a property called Scope which can be set accordingly.

Categories