Reputation: 785
I'm having a very similar issue to MS Dynamics SDK returning "Cannot access a disposed object" using the .NET dynamics SDK, but with a simpler system not using the IOrg service. This works the first times until the token expires, then if this is run a any time after the original token expires, it fails on the AcquireTokenAsync (the var authresult line) with a cannot access disposed object
error hitting the catch.
To make it clear this function is a MS Function, I do not have access to the source for this function, the objects being passed do not appear disposed and the authContext is not itself disposed.
I have verified that none of the objects here are disposed. I have looked at the internal objects of each one as well (as far as I could) and none of those are null/disposed that I could see.
internal static void Init(string _clientId, ISecureClientSecret _secret, string _authority = null, string _resourceUrl = null)
{
clientId = _clientId;
clientSecret = _secret;
if (!string.IsNullOrWhiteSpace(_authority))
{
authority = _authority;
}
if (!string.IsNullOrWhiteSpace(_resourceUrl))
{
resourceUrl = _resourceUrl;
}
initClient();
}
private static void initClient()
{
try
{
_client = new HttpClient();
var authContext = new AuthenticationContext(authority, false);
var clientCredentials = new ClientCredential(clientId, clientSecret); //Secret is of type ISecureClientSecret set in init.
var authResult = DynamicsLeadWorkflow.AsyncUtil.RunSync<AuthenticationResult>(() => authContext.AcquireTokenAsync(resourceUrl, clientCredentials));
if(authResult == null || String.IsNullOrWhiteSpace(authResult.AccessToken))
{
throw new UnauthorizedAccessException("Couldn't acquire the token from Dynamics");
}
_client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authResult.AccessToken);
_client.BaseAddress = new Uri(resourceUrl);
clientCreated = DateTime.Now;
}
catch (Exception ex)
{
ERROR(ex);
}
}
Some notes: initClient() is hit if the token expiry has passed (it is also run prior to this in other ways) - the ONYL time it fails is after the first token expires.
ClientSecret is an ISecureClientSecret set when the package initialises. (I am now wondering if this is going out of scope and being disposed of).
authority is the login authority url.
clientCreated is to be ignored
The runsync funcs:
public static class AsyncUtil
{
private static readonly TaskFactory _taskFactory = new
TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static void RunSync(Func<Task> task)
=> _taskFactory
.StartNew(task)
.Unwrap()
.GetAwaiter()
.GetResult();
public static TResult RunSync<TResult>(Func<Task<TResult>> task)
{
return _taskFactory
.StartNew(task)
.Unwrap()
.GetAwaiter()
.GetResult();
}
}
Upvotes: 0
Views: 177
Reputation: 785
Ok I have a result, the use of a ISecureClientSecret
will kill the AcquireTokenAsync
function (or at least the extension provided by the dynamics SDK) if you are using the same ISecureClientSecret as you have used to previously connect.
Essentially what appears to happen is when the token expires, it disposes of the token object, as a string is immutable this doesn't affect a string passed in as the credentials, but if you pass the ISecureClientSecret and it is disposed, it kills the original version in your project (probably by reference).
Solution: use a string, or regenerate the ISecureClientSecret each time the token expires when you are reacquiring.
Upvotes: 0
Reputation: 43860
you are calling async function using await inside of sync method. You have to call a method as sync
var authResult = authContext
.AcquireTokenAsync(resourceUrl, clientCredentials).Result;
or change a method
private static async Task initClient()
Upvotes: 0