c# - Renew access token at back-end when ajax call fails -


we have asp.net mvc 5 web app , use angularjs data mvc controllers (not apicontrollers). authentication linked azure ad using cookie authentication default expiry of after 1 hour.

the app spa. once user logs in not navigate other pages use ajax ($http) calls only.

so far, extended redirecttoidentityprovider method in startup.configuration() recognize ajax calls , return error 403 client-side when token expired. way, avoid redirecting authority page , cors error.

further, implemented persistent token cache helper tokencache (namespace microsoft.identitymodel.clients.activedirectory) in authorizationcodereceived of same class.

app.setdefaultsigninasauthenticationtype(cookieauthenticationdefaults.authenticationtype); app.usecookieauthentication(new cookieauthenticationoptions());  app.useopenidconnectauthentication(     new openidconnectauthenticationoptions     {         clientid = configurationhelper.clientid,         authority = configurationhelper.azureadauthorizationuri,          tokenvalidationparameters = new system.identitymodel.tokens.tokenvalidationparameters         {             validateissuer = true         },          notifications = new openidconnectauthenticationnotifications()         {             authorizationcodereceived = (context) =>             {                 var code = context.code;                  clientcredential credential = new clientcredential(configurationhelper.clientid, configurationhelper.appkey);                 string userobjectid = context.authenticationticket.identity.findfirst(claimtypes.nameidentifier).value;                  authenticationcontext authcontext = new authenticationcontext(configurationhelper.azureadauthorizationuri, new inmemorytokencache(userobjectid));                  authenticationresult result = authcontext.acquiretokenbyauthorizationcode(code, new uri(httpcontext.current.request.url.getleftpart(uripartial.path)), credential, configurationhelper.azureadgraphresourceuri);                  return task.fromresult(0);             },               redirecttoidentityprovider = (context) =>             {                 if (isajaxrequest(context.request))                 {                     context.response.statuscode = 401; // web api only!                     context.response.headers.remove("set-cookie");                     context.state = notificationresultstate.handledresponse;                 }                 else                 {                     string appbaseurl = context.request.scheme + "://" + context.request.host + context.request.pathbase;                      context.protocolmessage.redirecturi = appbaseurl + "/" + context.request.querystring;                     context.protocolmessage.postlogoutredirecturi = appbaseurl;                 }                  return task.fromresult(0);             },              authenticationfailed = (context) =>             {                 // suppress exception                 context.handleresponse();                  return task.fromresult(0);             }         }     }); } 
  • inmemorytokencache our wrapper around tokencache

  • isajaxrequest function recognizes ajax call. rest standard asp.net mvc 5 template.

our issue when user access token expires, want refresh , keep going without redirecting user login screen or returning 403 client-side. , how should that?

one way solve problem refresh token few moments before expires. in case, application composed of many single pages served node.js server. after login, store token.expires_in value in cookie, accessible on server-side.

when user navigate or hit f5 refresh page, server initialize client context tokenexpiresin. if token expires after 100 minutes, automatically refreshed after 90 minutes.

sample code

angular.module('app').run(function() {      var tokenexpiresin = context['tokenexpiresin'];     if (tokenexpiresin) {       refreshtoken(tokenexpiresin);     }      // automatically refresh token after delay     function refreshtoken(delay) {       $log.debug('token refreshed in ' + delay + ' ms');       $timeout(function () {         authenticationservice.refreshtoken().then(         function (token) {           // token refresh successful           // broadcast event can react if necessary           $rootscope.$broadcast(authenticationservice.events.refresh_token, token);           // refresh token again after 1 expires           refreshtoken(token.expires_in * 1000 * (90/100);         }, function (error) {           // token invalid, force logout           authenticationservice.logout();         });       }, delay);     }  }); 

another wayis use authentication interceptor

angular     .module('app')     .factory('authenticationhttp401interceptor', authenticationhttp401interceptor)      // intercept 401 unauthorized http response backend     authenticationhttp401interceptor.$inject = ['$q'];     function moauthenticationhttp401interceptor($q) {         return {             responseerror: function(rejection) {               if (rejection.status === 401                  && rejection.config.url                 && rejection.config.url.indexof(context.backend_base_url') === 0                 && rejection.headers("www-authenticate")                 && rejection.headers("www-authenticate").indexof('error="invalid_token"') !== -1                 && rejection.headers("www-authenticate").indexof('error_description="the access token expired"') !== -1               )               // or using regexp               // if (rejection.status === 401                //    && /invalid_token.*the access token  expired/.test(rejection.headers("www-authenticate"))               // )                 {                 // refresh token here                 // display overlay while doing if necessary               }               return $q.reject(rejection);             }         };     } 

source : rfc 6750


Comments