Search code examples
c#asp.net-web-apioauthbearer-tokenrefresh-token

How to change claim values in refresh tokens and bearer authentication


I want to change the value of a claim via refresh tokens. My refresh token provider is like this:

public class MyRefreshTokenProvider : AuthenticationTokenProvider
{
  public override void Create(AuthenticationTokenCreateContext context)
  {
     ...
     var claim = context.Ticket.Identity.FindFirst(ClaimTypes.UserData);
     if (claim != null)
     {
         context.Ticket.Identity.RemoveClaim(claim);
         context.Ticket.Identity.AddClaim(new Claim(ClaimTypes.UserData, "New Value"));
     }

     context.SetToken(context.SerializeTicket());
  }

  public override void Receive(AuthenticationTokenReceiveContext context)
  {
     context.DeserializeTicket(context.Token);
  }
}

And in the startup class:

app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
  {
     ...
     RefreshTokenProvider = new MyRefreshTokenProvider()
  });

The refresh token request completes with no error. But when I use the new access token, the claim value is still the old one.

Is my approach right? Or how can I change a claim value in the Bearer authentication?


Solution

  • Finally I've found the solution. I have to extend the AccessTokenProvider OAuthAuthorizationServerOptions instead of the RefreshTokenProvider:

    app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions
    {
       ...
       AccessTokenProvider = new MyAccessTokenProvider(),
       RefreshTokenProvider = new MyRefreshTokenProvider()
    });
    
    public class MyAccessTokenProvider : AuthenticationTokenProvider
    {
      public override void Create(AuthenticationTokenCreateContext context)
      {
         ...
         var claim = context.Ticket.Identity.FindFirst(ClaimTypes.UserData);
         if (claim != null)
         {
             context.Ticket.Identity.RemoveClaim(claim);
             context.Ticket.Identity.AddClaim(new Claim(ClaimTypes.UserData, "New Value"));
         }
    
         context.SetToken(context.SerializeTicket());
      }
    
      public override void Receive(AuthenticationTokenReceiveContext context)
      {
         context.DeserializeTicket(context.Token);
      }
    }
    
    public class MyRefreshTokenProvider : AuthenticationTokenProvider
    {
      public override void Create(AuthenticationTokenCreateContext context)
      {
         context.SetToken(context.SerializeTicket());
      }
    
      public override void Receive(AuthenticationTokenReceiveContext context)
      {
         context.DeserializeTicket(context.Token);
      }
    }
    

    According to the OAuthAuthorizationServerHandler class in the Microsoft.Owin.Security.OAuth the AccessTokenProvider only can update the refreshing token. For changing the claims the AccessTokenProvider should be extended:

        private async Task InvokeTokenEndpointAsync()
        {
            ...
            var accessTokenContext = new AuthenticationTokenCreateContext(
                Context,
                Options.AccessTokenFormat,
                ticket);
    
            await Options.AccessTokenProvider.CreateAsync(accessTokenContext);
    
            string accessToken = accessTokenContext.Token;
            if (string.IsNullOrEmpty(accessToken))
            {
                accessToken = accessTokenContext.SerializeTicket();
            }
            DateTimeOffset? accessTokenExpiresUtc = ticket.Properties.ExpiresUtc;
    
            var refreshTokenCreateContext = new AuthenticationTokenCreateContext(
                Context,
                Options.RefreshTokenFormat,
                accessTokenContext.Ticket);
            await Options.RefreshTokenProvider.CreateAsync(refreshTokenCreateContext);
            string refreshToken = refreshTokenCreateContext.Token;
    
            var memory = new MemoryStream();
            byte[] body;
            using (var writer = new JsonTextWriter(new StreamWriter(memory)))
            {
                writer.WriteStartObject();
                writer.WritePropertyName(Constants.Parameters.AccessToken);
                writer.WriteValue(accessToken);
                writer.WritePropertyName(Constants.Parameters.TokenType);
                writer.WriteValue(Constants.TokenTypes.Bearer);
                if (accessTokenExpiresUtc.HasValue)
                {
                    TimeSpan? expiresTimeSpan = accessTokenExpiresUtc - currentUtc;
                    var expiresIn = (long)expiresTimeSpan.Value.TotalSeconds;
                    if (expiresIn > 0)
                    {
                        writer.WritePropertyName(Constants.Parameters.ExpiresIn);
                        writer.WriteValue(expiresIn);
                    }
                }
                if (!String.IsNullOrEmpty(refreshToken))
                {
                    writer.WritePropertyName(Constants.Parameters.RefreshToken);
                    writer.WriteValue(refreshToken);
                }
             ...