1

We want to generate a new access token using a refresh token in the identity server. We want to implement a similar scenario as of SPA application(Silent renew token in react, angular). For now, we have implemented it in the Index section but the problem here is whenever I load the page only by new access token will be generated with the help of a refresh token.

public async Task<IActionResult> IndexAsync()
        {
            string accessToken = string.Empty;
            var currentContext = _httpContextAccessor.HttpContext;            
            var expires_at = await currentContext.GetTokenAsync("expires_at");            
            if (string.IsNullOrWhiteSpace(expires_at)
                || ((DateTime.Parse(expires_at).AddSeconds(-60)).ToUniversalTime()
                < DateTime.UtcNow))
            {
                accessToken = await RenewTokens();
            }
    else
    {
        accessToken = await HttpContext.GetTokenAsync("access_token");
    }

    var client = new HttpClient();
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    var content = await client.GetStringAsync("https://xxxxxxx");
    ViewData["Token"] = content;
    ViewData["AccessToken"] = accessToken;


    return View();
}
public async Task<String> RenewTokens()
        {
           
            var currentContext = _httpContextAccessor.HttpContext;
            var refreshToken = await HttpContext.GetTokenAsync("refresh_token");
            var client = new HttpClient();
            var values = new Dictionary<string, string>
            {
                { "client_id", "xxx" },
                { "client_secret", "xxxx" },
                { "grant_type", "refresh_token" },                
                { "scope", "xxxx" },              
                {"refresh_token", refreshToken }
            };

            var content = new FormUrlEncodedContent(values);
            var response = await client.PostAsync("https:///xxxxxx/token", content);
            var jsonContent = await response.Content.ReadAsStringAsync();
            Token tok = JsonConvert.DeserializeObject<Token>(jsonContent);
 
                string access_token = tok.AccessToken;
            var ExpiresAt = DateTime.UtcNow + TimeSpan.FromSeconds(tok.ExpiresIn);
            var authenticationInfo = await currentContext.AuthenticateAsync("Cookies");
            authenticationInfo.Properties.UpdateTokenValue("expires_at", ExpiresAt.ToString("o", CultureInfo.InvariantCulture));
            authenticationInfo.Properties.UpdateTokenValue("access_token", tok.AccessToken);
            authenticationInfo.Properties.UpdateTokenValue("refresh_token", tok.RefreshToken);
            await currentContext.SignInAsync("Cookies", authenticationInfo.Principal, authenticationInfo.Properties);
            return  access_token;
        }
Pritom Sarkar
  • 2,154
  • 3
  • 11
  • 26
Harshitha
  • 11
  • 1

1 Answers1

0

Avoid basing renewal solely on expires_at since it is not resilient due to clock differences and race conditions. In some setups you may get a 401 for other reasons, such as token signing certificate renewal.

The responsibilities should be like this:

  • Client tries an API request with an access token
  • If client receives a 401 it attempts to silently refresh the access token and retry the API request with the new token
  • If your 'SPA' sends all requests for data via a Web back end you can maybe do this in C# code

Here is some sample code for resilient clients:

Gary Archer
  • 22,534
  • 2
  • 12
  • 24
  • We are looking solution for .net core not SPA's – Harshitha Jul 15 '21 at 10:45
  • It's the same design pattern though - if you are using an [HttpClient in C#](https://bekenty.com/quick-and-easy-way-to-set-authorization-header-of-httpclient/) to make API requests you would write similar code – Gary Archer Jul 16 '21 at 09:33