I see a lot of answers use Newtonsoft.Json package to update appsettings. I will provide some some solutions that use System.Text.Json package (built-in on .Net Core 3 and above).
OPTION 1
Before you start updating appsettings.json file dynamically, ask yourself a question, how comlex is that part of appsettings.json that needs to be updated. If the part that needs to be updated is not very complex, you can use appsettings transformation functionality just for that part that that needs to be updated. Here's an example:
Let's say my appsettings.json file looks like that:
{
    "Username": "Bro300",
    "Job": {
        "Title": "Programmer",
        "Type": "IT"
    }
}
And let's say I need to update only Job section. Instead of updating appsettings.json directly I can create a smaller file appsettings.MyOverrides.json that will look like this:
{
  "Job": {
    "Title": "Farmer",
    "Type": "Agriculture"
  }
}
And then make sure that this new file is added in my .Net Core app, and .Net Core will figure out how to load the new updated settings.
Now the next step is to create a wrapper class that will hold values from appsettings.MyOverrides.json like this:
public class OverridableSettings
{
    public JobSettings Job { get; set; }
}
public class JobSettings
{
    public string Title { get; set; }
    public string Type { get; set; }
}
And then I can create my updater class that will look like this (notice that it takes in OverridableSettings and completely overrides appsettings.MyOverrides.json file:
public class AppSettingsUpdater
{
    public void UpdateSettings(OverridableSettings settings)
    {
        // instead of updating appsettings.json file directly I will just write the part I need to update to appsettings.MyOverrides.json
        // .Net Core in turn will read my overrides from appsettings.MyOverrides.json file
        const string SettinsgOverridesFileName = "appsettings.MyOverrides.json";
        var newConfig = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true });
        File.WriteAllText(SettinsgOverridesFileName, newConfig);
    }
}
Finally this is the code that demonstrates how to use it:
public static class Program
{
    public static void Main()
    {
        // Notice that appsettings.MyOverrides.json will contain only the part that we need to update, other settings will live in appsettings.json
        // Also appsettings.MyOverrides.json is optional so if it doesn't exist at the program start it's not a problem
        var configuration = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .AddJsonFile("appsettings.MyOverrides.json", optional: true)
            .Build();
        // Here we read our current settings
        var settings = configuration.Get<OverridableSettings>();
        var settingsUpdater = new AppSettingsObjectUpdater();
        settings.Job.Title = "Farmer";
        settings.Job.Type = "Agriculture";
        settingsUpdater.UpdateSettings(settings);
        // Here we reload the settings so the new values from appsettings.MyOverrides.json will be read
        configuration.Reload(); 
        // and here we retrieve the new updated settings
        var newJobSettings = configuration.GetSection("Job").Get<JobSettings>();
    }
}
OPTION 2
If the appsetting transformation does not fit you case, and you have to update values only one level deep, you can use this simple implementation:
public void UpdateAppSetting(string key, string value)
{
    var configJson = File.ReadAllText("appsettings.json");
    var config = JsonSerializer.Deserialize<Dictionary<string, object>>(configJson);
    config[key] = value;
    var updatedConfigJson = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true });
    File.WriteAllText("appsettings.json", updatedConfigJson);
}
OPTION 3
Finally, if you have some complex case and you need to update appsettings, multiple levels deep, here is another implementation, that expands on the previous option, and uses recursion to update the settings at any level:
public class AppSettingsUpdater
{
    private const string EmptyJson = "{}";
    public void UpdateAppSetting(string key, object value)
    {
        // Empty keys "" are allowed in json by the way
        if (key == null)
        {
            throw new ArgumentException("Json property key cannot be null", nameof(key));
        }
        const string settinsgFileName = "appsettings.json";
        // We will create a new file if appsettings.json doesn't exist or was deleted
        if (!File.Exists(settinsgFileName))
        {
            File.WriteAllText(settinsgFileName, EmptyJson);
        }
        var config = File.ReadAllText(settinsgFileName);
        var updatedConfigDict = UpdateJson(key, value, config);
        // After receiving the dictionary with updated key value pair, we serialize it back into json.
        var updatedJson = JsonSerializer.Serialize(updatedConfigDict, new JsonSerializerOptions { WriteIndented = true });
        File.WriteAllText(settinsgFileName, updatedJson);
    }
    // This method will recursively read json segments separated by semicolon (firstObject:nestedObject:someProperty)
    // until it reaches the desired property that needs to be updated,
    // it will update the property and return json document represented by dictonary of dictionaries of dictionaries and so on.
    // This dictionary structure can be easily serialized back into json
    private Dictionary<string, object> UpdateJson(string key, object value, string jsonSegment)
    {
        const char keySeparator = ':';
        var config = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonSegment);
        var keyParts = key.Split(keySeparator);
        var isKeyNested = keyParts.Length > 1;
        if (isKeyNested)
        {
            var firstKeyPart = keyParts[0];
            var remainingKey = string.Join(keySeparator, keyParts.Skip(1));
            // If the key does not exist already, we will create a new key and append it to the json
            var newJsonSegment = config.ContainsKey(firstKeyPart) && config[firstKeyPart] != null
                ? config[firstKeyPart].ToString()
                : EmptyJson;
            config[firstKeyPart] = UpdateJson(remainingKey, value, newJsonSegment);
        }
        else
        {
            config[key] = value;
        }
        return config;
    }
}
You can use, like this:
var settingsUpdater = new AppSettingsUpdater();
settingsUpdater.UpdateAppSetting("OuterProperty:NestedProperty:PropertyToUpdate", "new value");