I have an Azure Web App that authenticates a user which then navigates to a page where some Sharepoint documents are retrieved and displayed in the app.
Most of the time the application works fine, but ocassionally App Insights will highlight that Failed to acquire token silently as no token was found in the cache. Call method AcquireToken. Some users report issues from time to time on this page (it's inconsistent so it might happen a few times a day with a somewhat large user base). The problem is that currently the error isn't handled and I'm trying to figure out how to make the call to AcquireTokenAsync.
The following is the method that returns the token (or doesnt):
private async Task<string> GetUserAccessToken()
{
try
{
// Credentials for app
// _clientId and _clientSecret represent the app info - not shown here in code
ClientCredential credential = new ClientCredential(_clientId, _clientSecret);
//Construct token cache
ITokenCacheFactory cacheFactory = Request.HttpContext.RequestServices.GetRequiredService<ITokenCacheFactory>();
TokenCache cache = cacheFactory.CreateForUser(Request.HttpContext.User);
AuthenticationContext authContext = new AuthenticationContext(_authority, cache);
// guid of the user currently logged into the app
string objectID = _userObjectId;
UserIdentifier userIdentifier = new UserIdentifier(objectID, UserIdentifierType.UniqueId);
string resource = "https://test.sharepoint.com";
AuthenticationResult result = await authContext.AcquireTokenSilentAsync(resource, credential, userIdentifier);
return result.AccessToken;
}
catch (Exception ex)
{
throw ex;
}
}
If I understand the flow correctly, the web app here will request a token using it's own credentials on behalf of the user currently logged in. (Am I right in understanding this based on the method signature which states - Identifier of the user token is requested for. This parameter can be Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier.Any.)
Now when this fails, I would need to make a call to AcquireTokenAsync. There are a number of these methods available and I can't seem to find the one that will fulfill this requirement.
Before the suggestion comes, I can't use AcquireTokenAsync(resource, clientId, redirectUri,new PlatformParameters(PromptBehavior.Auto)); because the constructor on PlatformParameters has changed and requires an implementation of a ICustomWebUi and this flow isn't supported on .Net Core 3.1 as far as I'm aware which makes this unusable.
AcquireTokenAsync(resource, credentials) works and returns a token, however, when using that token I get a 401 Unauthorized when accessing the Sharepoint resources, most likely because the token is different and it is now requested on behalf of the application and not the user logged into the application (if I'm following this train of thought correctly...).
My question is - which method do I call? Is there something I would need to add before making the call to AcquireTokenAsync and if so, which of the 10 or so overloads should I use? I tried using AcquireTokenAsync(resource, credenetial, userAssertion) and passed in the AccessToken that I retrieved on the User logged in, but then I got Assertion failed signature validation or variations on that. If I understood correctly, the UserAssertion can be initialized with 1,2 or 3 parameters and I tried providing the AccessToken currently on the user that is logged in the app, but with no success.
Any help is greatly appreciated as I've been looking at this for two days now.
I spent more time investigating this, but none of the methods available would have worked in my case. The auth flow wasn't an on-behalf-of flow, but an auth-code flow. The link is to the newer MSAL library, but the concept is there. The application, a .net core web app, directs the user to sign in. When they sign in, an auth-code is passed into the response once they successfully authenticate.
The auth-code is then used to call AcquireTokenByAuthorizationCodeAsync(AuthCode, Uri, ClientCredential, UserIdentifier). This returns the valid access token that can be stored in the distributed token cache and then used to authenticate in order to access a given resource.
My biggest issue was that the error says you need to use AcquireTokenAsync to retrieve a new token. This is correct to a certain point, because in order to make any calls to any of the 14 or so methods you will need different bits of information, which will be dependent on the way you have setup your authentication flow in your application.
Because the application I worked on used auth code flow, I would need to get a new auth code. This would mean redirecting the user to login, capture the auth code in the response if the login was successful and then call the appropriate AcquireTokenAsync method that takes in an auth code as parameter along with app info, uri and so on.
To solve this, I used the information provided by the Microsoft Github page on Acquiring tokens with auth codes in web apps. Here I found samples on how auth flow is setup, but most importantly, how to trigger a new authentication flow if the user needs to be re-authenticated.
I wrapped the code that would throw the AdalSilentTokenAcquisitionException, catch the error and return a RedirectToAction.
return RedirectToAction("ActionName", "Controller", new RouteValues);
The above redirects the user to a given action, in a particular controller and passes through an object that can hold additional parameters. In my case it's a new { redirectUri = redirectUriString}, which is a string object that holds the URL the user would try to navigate this. I constructed this with a little method that uses the current HttpRequest to find the url the user was trying to get to.
Next, the controller that I had setup which responds to that redirect:
[HttpGet("/SignIn")]
public IActionResult SignIn([FromQuery(Name ="redirectUri")]string redirectUri)
{
return Challenge
(
new AuthenticationProperties { RedirectUri = WebUtility.UrlDecode(redirectUri) },
OpenIdConnectDefaults.AuthenticationScheme
);
}
Here, a Challenge is returned. The challenge triggers a call to the authentication flow that was setup in the Startup class. I think the entire flow here is that the method will send people to go through whatever is in that startup, which, in the case of the application I worked on, it prompts the user to sign in, captures the auth code, requests a new access token and once this is received and saved in the distributed token cache, the user is redirected to the redirectUri that I passed through.
I hope this helps or at least gives a starting point to anyone who might encounter a similar issue.
I'm using Azure AD B2C with OpenIdConnect for authentication to a web application. I've got it mainly working except that the authentication times out after an hour, even if the user is actively using the application.
It is an old webapp built mostly with ASPX pages. I'm just using an identity token and I'm using cookies. I am not using an access token at all in my app. Access is done a pre-existing way, based on the user claims. I'm using the MSAL.Net library in Microsoft.Identity.Client. Logging in works fine. I get a code back which then gets exchanged for an identity token
AuthenticationResult result = await confidentialClient.AcquireTokenByAuthorizationCode(Globals.Scopes, notification.Code).ExecuteAsync();
Everything was working fine, except that the token would expire after 1 hour, no matter what I did. Even if I was using the app, the first request after an hour would be unauthenticated. I tried adding a call to silently acquire a token to see if that would refresh it, but it did not. With OpenIdConnect the offline_access scope is always included. If I try to include it explicitly it throws an error saying so. But I've never seen any evidence that there is a refresh token, even behind the scenes.
I found this question on StackOverflow - Azure AD B2C OpenID Connect Refresh token - and the first answer referenced an OpenIdConnect property called UseTokenLifetime. If I set that to false, then I wouldn't lose authentication after an hour, but the now it was too far the other way. It seemed like the token/cookie would never expire, and I could stay logged in forever.
My desire is that as long as the user is actively using the application, they stay logged in, but if they stop using it for some time (an hour), they have to re-authenticate. I found a way to make that happen through hours of trial and error, I'm just not sure if it makes sense and/or is secure. What I'm doing now is that on each authenticated request, I update the "exp" claim of the user (not sure this matters), and then generate a new AuthenticationResponseGrant, setting the ExpiresUtc to the new time. In my testing, if I hit this code in less than an hour, it keeps me logged in, and then if I wait beyond an hour, I'm no longer authenticated.
HttpContext.Current.User.SetExpirationClaim(DateTime.Now.AddMinutes(60.0));
public static void SetExpirationClaim(this IPrincipal currentPrincipal, DateTime expiration)
{
System.Diagnostics.Debug.WriteLine("Setting claims expiration to {0}", expiration);
int seconds = (int)expiration.Subtract(epoch).TotalSeconds;
currentPrincipal.AddUpdateClaim("exp", seconds.ToString(), expiration);
}
public static void AddUpdateClaim(this IPrincipal currentPrincipal, string key, string value, DateTime expiration)
{
var identity = currentPrincipal.Identity as ClaimsIdentity;
if (identity == null)
return;
// check for existing claim and remove it
var existingClaim = identity.FindFirst(key);
if (existingClaim != null)
identity.RemoveClaim(existingClaim);
// add new claim
identity.AddClaim(new Claim(key, value));
var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
authenticationManager.AuthenticationResponseGrant = new AuthenticationResponseGrant(new ClaimsPrincipal(identity),
new AuthenticationProperties() {
IsPersistent = true,
ExpiresUtc = new DateTimeOffset(expiration).UtcDateTime,
IssuedUtc = new DateTimeOffset(DateTime.Now).UtcDateTime
});
}
My question is, does this make sense? Is there any downside? I've never seen any suggestions to do it this way, but it was the only thing I found that worked. If there is a better way to do it, I'd like to know what it is. I considered making my current code an "answer" instead of including it in the question, but I'm not confident that it is correct.
To refresh ID token, you need to use refresh token. Refresh token is opaque to client, but could be cached by MSAL. Then when ID token is expired, MSAL will use the cached refresh token to get a new ID token.
However, you need to implement the cache logic by yourself like instructed in official sample.
Core code snipet:
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
AuthorizationCodeReceived = OnAuthorizationCodeReceived,
AuthenticationFailed = OnAuthenticationFailed,
},
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification notification)
{
try
{
/*
The `MSALPerUserMemoryTokenCache` is created and hooked in the `UserTokenCache` used by `IConfidentialClientApplication`.
At this point, if you inspect `ClaimsPrinciple.Current` you will notice that the Identity is still unauthenticated and it has no claims,
but `MSALPerUserMemoryTokenCache` needs the claims to work properly. Because of this sync problem, we are using the constructor that
receives `ClaimsPrincipal` as argument and we are getting the claims from the object `AuthorizationCodeReceivedNotification context`.
This object contains the property `AuthenticationTicket.Identity`, which is a `ClaimsIdentity`, created from the token received from
Azure AD and has a full set of claims.
*/
IConfidentialClientApplication confidentialClient = MsalAppBuilder.BuildConfidentialClientApplication(new ClaimsPrincipal(notification.AuthenticationTicket.Identity));
// Upon successful sign in, get & cache a token using MSAL
AuthenticationResult result = await confidentialClient.AcquireTokenByAuthorizationCode(Globals.Scopes, notification.Code).ExecuteAsync();
}
catch (Exception ex)
{
throw new HttpResponseException(new HttpResponseMessage
{
StatusCode = HttpStatusCode.BadRequest,
ReasonPhrase = $"Unable to get authorization code {ex.Message}."
});
}
}
I don't know if there is a problem with my code but; using google-apis nodejs client for Drive REST API.
However, when I refresh my access token with the refresh token provided, it gives me an expiry time as seconds. (3600 more seconds from request)
I store them in a text file. Then I set credentials before a new request.
oauth2Client.setCredentials({
access_token: gTokens[0],
refresh_token: gTokens[1],
expiry_date: gTokens[2]
});
var expiryDate = oauth2Client.credentials.expiry_date;
var isTokenExpired = expiryDate ? expiryDate <= (new Date()).getTime() : false;
Then I check wheter access token is expired or not. The code above gives me true for isTokenExpired. Even If my access token is expired, I can upload a file to Google Drive with an access token which is expired long time ago.
What's the problem?
There is no problem - you're also saving the refresh_token, it sounds like, and the client library has logic that uses the refresh_token to get a new access_token if the first one you use fails.
What it does not do, however, is save this token automatically. You need to take action to setup hooks to do the save if necessary.
I need my app to support custom authentication against our private database, and following the advices on Adrian Hall book here https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter2/custom/ I got no problem authenticating my users. The problem comes when I need to refresh the access token.
As I'm forced to stay with custom auth, I have the following questions:
1) Should I call MobileServicesClient.RefreshUserAsync(), and if so, what kind of endpoint should I implement on the server? Will it reissue another token every time, invalidating the old? When should be the refresh call be made?
2) I've read about using a never-expiring refresh token, but I can't really find a sample implementation, or instructions on how to implement it in a custom auth scenario, could someone point me to the right direction?
Many thanks in advance
Should I call MobileServicesClient.RefreshUserAsync(), and if so, what kind of endpoint should I implement on the server? Will it reissue another token every time, invalidating the old? When should be the refresh call be made?
I have checked the method RefreshUserAsync from Microsoft.WindowsAzure.Mobile.dll which would send a get request against the /.auth/refresh endpoint for refreshing the access token with the provider for your logged in user. Since you are using custom authentication, you could not use this method for refreshing the authenticationToken.
I've read about using a never-expiring refresh token, but I can't really find a sample implementation, or instructions on how to implement it in a custom auth scenario, could someone point me to the right direction?
When using the CreateToken method from AppServiceLoginHandler, you could specify the lifetime as null, then you would retrieve a never-expiring authenticationToken as follows:
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(claims, signingKey, audience, issuer,null);
Additionally, you could try to build your endpoint for creating new token based on the old valid token as follows:
[Route(".auth/login/customRefreshToken")]
public IHttpActionResult RefreshToken([FromBody] RefreshTokenInput body)
{
string tokenString = body.AuthenticationToken;
try
{
var jwtSecurityToken = new JwtSecurityToken(tokenString);
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(jwtSecurityToken.Claims, signingKey, audience, issuer, TimeSpan.FromDays(30));
return Ok(new LoginResult()
{
AuthenticationToken = token.RawData
});
}
catch (Exception e)
{
return BadRequest("$Error = {e.Message}, StackTrace = {e.StackTrace}");
}
}
Note: For your mobile client, you could use MobileServiceClient.InvokeApiAsync for retrieving the new token, then parse the authenticationToken and update it to MobileServiceClient.CurrentUser.MobileServiceAuthenticationToken.
RESULT
I would like to implement JWT-based authentication to our new REST API. But since the expiration is set in the token, is it possible to automatically prolong it? I don't want users to need to sign in after every X minutes if they were actively using the application in that period. That would be a huge UX fail.
But prolonging the expiration creates a new token (and the old one is still valid until it expires). And generating a new token after each request sounds silly to me. Sounds like a security issue when more than one token is valid at the same time. Of course I could invalidate the old used one using a blacklist but I would need to store the tokens. And one of the benefits of JWT is no storage.
I found how Auth0 solved it. They use not only JWT token but also a refresh token:
https://auth0.com/docs/tokens/refresh-tokens
But again, to implement this (without Auth0) I'd need to store refresh tokens and maintain their expiration. What is the real benefit then? Why not have only one token (not JWT) and keep the expiration on the server?
Are there other options? Is using JWT not suited for this scenario?
I work at Auth0 and I was involved in the design of the refresh token feature.
It all depends on the type of application and here is our recommended approach.
Web applications
A good pattern is to refresh the token before it expires.
Set the token expiration to one week and refresh the token every time the user opens the web application and every one hour. If a user doesn't open the application for more than a week, they will have to login again and this is acceptable web application UX.
To refresh the token, your API needs a new endpoint that receives a valid, not expired JWT and returns the same signed JWT with the new expiration field. Then the web application will store the token somewhere.
Mobile/Native applications
Most native applications do login once and only once.
The idea is that the refresh token never expires and it can be exchanged always for a valid JWT.
The problem with a token that never expires is that never means never. What do you do if you lose your phone? So, it needs to be identifiable by the user somehow and the application needs to provide a way to revoke access. We decided to use the device's name, e.g. "maryo's iPad". Then the user can go to the application and revoke access to "maryo's iPad".
Another approach is to revoke the refresh token on specific events. An interesting event is changing the password.
We believe that JWT is not useful for these use cases, so we use a random generated string and we store it on our side.
In the case where you handle the auth yourself (i.e don't use a provider like Auth0), the following may work:
Issue JWT token with relatively short expiry, say 15min.
Application checks token expiry date before any transaction requiring a token (token contains expiry date). If token has expired, then it first asks API to 'refresh' the token (this is done transparently to the UX).
API gets token refresh request, but first checks user database to see if a 'reauth' flag has been set against that user profile (token can contain user id). If the flag is present, then the token refresh is denied, otherwise a new token is issued.
Repeat.
The 'reauth' flag in the database backend would be set when, for example, the user has reset their password. The flag gets removed when the user logs in next time.
In addition, let's say you have a policy whereby a user must login at least once every 72hrs. In that case, your API token refresh logic would also check the user's last login date from the user database and deny/allow the token refresh on that basis.
Below are the steps to do revoke your JWT access token:
1) When you do login, send 2 tokens (Access token, Refresh token) in response to client .
2) Access token will have less expiry time and Refresh will have long expiry time .
3) Client (Front end) will store refresh token in his local storage and access token in cookies.
4) Client will use access token for calling apis. But when it expires, pick the refresh token from local storage and call auth server api to get the new token.
5) Your auth server will have an api exposed which will accept refresh token and checks for its validity and return a new access token.
6) Once refresh token is expired, User will be logged out.
Please let me know if you need more details , I can share the code (Java + Spring boot) as well.
I was tinkering around when moving our applications to HTML5 with RESTful apis in the backend. The solution that I came up with was:
Client is issued with a token with a session time of 30 mins (or whatever the usual server side session time) upon successful login.
A client-side timer is created to call a service to renew the token before its expiring time. The new token will replace the existing in future calls.
As you can see, this reduces the frequent refresh token requests. If user closes the browser/app before the renew token call is triggered, the previous token will expire in time and user will have to re-login.
A more complicated strategy can be implemented to cater for user inactivity (e.g. neglected an opened browser tab). In that case, the renew token call should include the expected expiring time which should not exceed the defined session time. The application will have to keep track of the last user interaction accordingly.
I don't like the idea of setting long expiration hence this approach may not work well with native applications requiring less frequent authentication.
An alternative solution for invalidating JWTs, without any additional secure storage on the backend, is to implement a new jwt_version integer column on the users table. If the user wishes to log out or expire existing tokens, they simply increment the jwt_version field.
When generating a new JWT, encode the jwt_version into the JWT payload, optionally incrementing the value beforehand if the new JWT should replace all others.
When validating the JWT, the jwt_version field is compared alongside the user_id and authorisation is granted only if it matches.
jwt-autorefresh
If you are using node (React / Redux / Universal JS) you can install npm i -S jwt-autorefresh.
This library schedules refresh of JWT tokens at a user calculated number of seconds prior to the access token expiring (based on the exp claim encoded in the token). It has an extensive test suite and checks for quite a few conditions to ensure any strange activity is accompanied by a descriptive message regarding misconfigurations from your environment.
Full example implementation
import autorefresh from 'jwt-autorefresh'
/** Events in your app that are triggered when your user becomes authorized or deauthorized. */
import { onAuthorize, onDeauthorize } from './events'
/** Your refresh token mechanism, returning a promise that resolves to the new access tokenFunction (library does not care about your method of persisting tokens) */
const refresh = () => {
const init = { method: 'POST'
, headers: { 'Content-Type': `application/x-www-form-urlencoded` }
, body: `refresh_token=${localStorage.refresh_token}&grant_type=refresh_token`
}
return fetch('/oauth/token', init)
.then(res => res.json())
.then(({ token_type, access_token, expires_in, refresh_token }) => {
localStorage.access_token = access_token
localStorage.refresh_token = refresh_token
return access_token
})
}
/** You supply a leadSeconds number or function that generates a number of seconds that the refresh should occur prior to the access token expiring */
const leadSeconds = () => {
/** Generate random additional seconds (up to 30 in this case) to append to the lead time to ensure multiple clients dont schedule simultaneous refresh */
const jitter = Math.floor(Math.random() * 30)
/** Schedule autorefresh to occur 60 to 90 seconds prior to token expiration */
return 60 + jitter
}
let start = autorefresh({ refresh, leadSeconds })
let cancel = () => {}
onAuthorize(access_token => {
cancel()
cancel = start(access_token)
})
onDeauthorize(() => cancel())
disclaimer: I am the maintainer
Today, lots of people opt for doing session management with JWTs without being aware of what they are giving up for the sake of perceived simplicity. My answer elaborates on the 2nd part of the questions:
What is the real benefit then? Why not have only one token (not JWT) and keep the expiration on the server?
Are there other options? Is using JWT not suited for this scenario?
JWTs are capable of supporting basic session management with some limitations. Being self-describing tokens, they don't require any state on the server-side. This makes them appealing. For instance, if the service doesn't have a persistence layer, it doesn't need to bring one in just for session management.
However, statelessness is also the leading cause of their shortcomings. Since they are only issued once with fixed content and expiration, you can't do things you would like to with a typical session management setup.
Namely, you can't invalidate them on-demand. This means you can't implement a secure logout as there is no way to expire already issued tokens. You also can't implement idle timeout for the same reason. One solution is to keep a blacklist, but that introduces state.
I wrote a post explaining these drawbacks in more detail. To be clear, you can work around these by adding more complexity (sliding sessions, refresh tokens, etc.)
As for other options, if your clients only interact with your service via a browser, I strongly recommend using a cookie-based session management solution. I also compiled a list authentication methods currently widely used on the web.
Good question- and there is wealth of information in the question itself.
The article Refresh Tokens: When to Use Them and How They Interact with JWTs gives a good idea for this scenario. Some points are:-
Refresh tokens carry the information necessary to get a new access
token.
Refresh tokens can also expire but are rather long-lived.
Refresh tokens are usually subject to strict storage requirements to
ensure they are not leaked.
They can also be blacklisted by the authorization server.
Also take a look at auth0/angular-jwt angularjs
For Web API. read Enable OAuth Refresh Tokens in AngularJS App using ASP .NET Web API 2, and Owin
I actually implemented this in PHP using the Guzzle client to make a client library for the api, but the concept should work for other platforms.
Basically, I issue two tokens, a short (5 minute) one and a long one that expires after a week. The client library uses middleware to attempt one refresh of the short token if it receives a 401 response to some request. It will then try the original request again and if it was able to refresh gets the correct response, transparently to the user. If it failed, it will just send the 401 up to the user.
If the short token is expired, but still authentic and the long token is valid and authentic, it will refresh the short token using a special endpoint on the service that the long token authenticates (this is the only thing it can be used for). It will then use the short token to get a new long token, thereby extending it another week every time it refreshes the short token.
This approach also allows us to revoke access within at most 5 minutes, which is acceptable for our use without having to store a blacklist of tokens.
Late edit: Re-reading this months after it was fresh in my head, I should point out that you can revoke access when refreshing the short token because it gives an opportunity for more expensive calls (e.g. call to the database to see if the user has been banned) without paying for it on every single call to your service.
I solved this problem by adding a variable in the token data:
softexp - I set this to 5 mins (300 seconds)
I set expiresIn option to my desired time before the user will be forced to login again. Mine is set to 30 minutes. This must be greater than the value of softexp.
When my client side app sends request to the server API (where token is required, eg. customer list page), the server checks whether the token submitted is still valid or not based on its original expiration (expiresIn) value. If it's not valid, server will respond with a status particular for this error, eg. INVALID_TOKEN.
If the token is still valid based on expiredIn value, but it already exceeded the softexp value, the server will respond with a separate status for this error, eg. EXPIRED_TOKEN:
(Math.floor(Date.now() / 1000) > decoded.softexp)
On the client side, if it received EXPIRED_TOKEN response, it should renew the token automatically by sending a renewal request to the server. This is transparent to the user and automatically being taken care of the client app.
The renewal method in the server must check if the token is still valid:
jwt.verify(token, secret, (err, decoded) => {})
The server will refuse to renew tokens if it failed the above method.
How about this approach:
For every client request, the server compares the expirationTime of the token with (currentTime - lastAccessTime)
If expirationTime < (currentTime - lastAccessedTime), it changes the last lastAccessedTime to currentTime.
In case of inactivity on the browser for a time duration exceeding expirationTime or in case the browser window was closed and the expirationTime > (currentTime - lastAccessedTime), and then the server can expire the token and ask the user to login again.
We don't require additional end point for refreshing the token in this case.
Would appreciate any feedack.
Ref - Refresh Expired JWT Example
Another alternative is that once the JWT has expired, the user/system will make a call to
another url suppose /refreshtoken. Also along with this request the expired JWT should be passed. The Server will then return a new JWT which can be used by the user/system.
The idea of JWT is good, you put what you need in JWT and go stateless.
Two problems:
Lousy JWT standardization.
JWT is impossible to invalidate or if created fast-expiring it forces the user to log in frequently.
The solution to 1. Use custom JSON:
{"userId": "12345", "role": "regular_user"}
Encrypt it with a symmetric (AES) algorithm (it is faster than signing with an asymmetric one) and put it in a fast-expiring cookie. I would still call it JWT since it is JSON and used as a token in a Web application. Now the server checks if the cookie is present and its value can be decrypted.
The solution to 2. Use refresh token:
Take userId as 12345, encrypt it, and put it in the long-expiring cookie. No need to create a special field for the refresh token in DB.
Now every time an access token (JWT) cookie is expired server checks the refresh token cookie, decrypts, takes the value, and looks for the user in DB. In case the user is found, generate a new access token, otherwise (or if the refresh token is also expired) force the user to log in.
The simplest alternative is to use a refresh token as an access token, i.e. do not use JWT at all.
The advantage of using JWT is that during its expiration time server does not hit DB. Even if we put an access token in the cookie with an expiration time of only 2 min, for a busy application like eBay it will results in thousands of DB hits per second avoided.
I know this is an old question, but I use a hybrid of both session and token authentication. My app is a combination of micro-services so I need to use token-based authentication so that every micro-service doesn't need access to a centralized database for authentication. I issue 2 JWTs to my user (signed by different secrets):
A standard JWT, used to authenticate requests. This token expires after 15 minutes.
A JWT that acts as a refresh token that is placed in a secure cookie. Only one endpoint (actually it is its own microservice) accepts this token, and it is the JWT refresh endpoint. It must be accompanied by a CSRF token in the post body to prevent CRSF on that endpoint. The JWT refresh endpoint stores a session in the database (the id of the session and the user are encoded into the refresh JWT). This allows the user, or an admin, to invalidate a refresh token as the token must both validate and match the session for that user.
This works just fine but is much more complicated than just using session-based auth with cookies and a CSRF token. So if you don't have micro-services then session-based auth is probably the way to go.
If you are using AWS Amplify & Cognito this will do the magic for you:
Use Auth.currentSession() to get the current valid token or get new if the current has expired. Amplify will handle it
As a fallback, use some interval job to refresh tokens on demand every x minutes, maybe 10 min. This is required when you have a long-running process like uploading a very large video which will take more than an hour (maybe due to a slow network) then your token will expire during the upload and amplify will not update automatically for you. In this case, this strategy will work. Keep updating your tokens at some interval.
How to refresh on demand is not mentioned in docs so here it is.
import { Auth } from 'aws-amplify';
try {
const cognitoUser = await Auth.currentAuthenticatedUser();
const currentSession = await Auth.currentSession();
cognitoUser.refreshSession(currentSession.refreshToken, (err, session) => {
console.log('session', err, session);
const { idToken, refreshToken, accessToken } = session;
// do whatever you want to do now :)
});
} catch (e) {
console.log('Unable to refresh Token', e);
}
Origin: https://github.com/aws-amplify/amplify-js/issues/2560
services.Configure(Configuration.GetSection("ApplicationSettings"));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<AuthenticationContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("IdentityConnection")));
services.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<AuthenticationContext>();
services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 4;
}
);
services.AddCors();
//Jwt Authentication
var key = Encoding.UTF8.GetBytes(Configuration["ApplicationSettings:JWT_Secret"].ToString());
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x=> {
x.RequireHttpsMetadata = false;
x.SaveToken = false;
x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
};
});
}