public static async Task <ClientTokenCloudConnection> Create( ITokenCredentials tokenCredentials, Action <string, CloudConnectionStatus> connectionStatusChangedHandler, ITransportSettings[] transportSettings, IMessageConverterProvider messageConverterProvider, IClientProvider clientProvider, ICloudListener cloudListener, TimeSpan idleTimeout, bool closeOnIdleTimeout, TimeSpan operationTimeout) { Preconditions.CheckNotNull(tokenCredentials, nameof(tokenCredentials)); var cloudConnection = new ClientTokenCloudConnection( tokenCredentials.Identity, connectionStatusChangedHandler, transportSettings, messageConverterProvider, clientProvider, cloudListener, idleTimeout, closeOnIdleTimeout, operationTimeout); ITokenProvider tokenProvider = new ClientTokenBasedTokenProvider(tokenCredentials, cloudConnection); ICloudProxy cloudProxy = await cloudConnection.CreateNewCloudProxyAsync(tokenProvider); cloudConnection.cloudProxy = Option.Some(cloudProxy); return(cloudConnection); }
/// <summary> /// This method does the following - /// 1. Updates the Identity to be used for the cloud connection /// 2. Updates the cloud proxy - /// i. If there is an existing device client and /// a. If is waiting for an updated token, and the Identity has a token, /// then it uses that to give it to the waiting client authentication method. /// b. If not, then it creates a new cloud proxy (and device client) and closes the existing one /// ii. Else, if there is no cloud proxy, then opens a device client and creates a cloud proxy. /// </summary> public async Task <ICloudProxy> UpdateTokenAsync(ITokenCredentials newTokenCredentials) { Preconditions.CheckNotNull(newTokenCredentials, nameof(newTokenCredentials)); using (await this.identityUpdateLock.LockAsync()) { // Disable callbacks while we update the cloud proxy. // TODO - instead of this, make convert Option<ICloudProxy> CloudProxy to Task<Option<ICloudProxy>> GetCloudProxy // which can be awaited when an update is in progress. this.callbacksEnabled = false; try { ITokenProvider tokenProvider = new ClientTokenBasedTokenProvider(newTokenCredentials, this); // First check if there is an existing cloud proxy ICloudProxy proxy = await this.CloudProxy.Map( async cp => { // If the Identity has a token, and we have a tokenGetter, that means // the connection is waiting for a new token. So give it the token and // complete the tokenGetter if (this.tokenGetter.HasValue) { if (TokenHelper.IsTokenExpired(this.Identity.IotHubHostName, newTokenCredentials.Token)) { throw new InvalidOperationException($"Token for client {this.Identity.Id} is expired"); } this.tokenGetter.ForEach( tg => { // First reset the token getter and then set the result. this.tokenGetter = Option.None <TaskCompletionSource <string> >(); tg.SetResult(newTokenCredentials.Token); }); return(cp); } // Else this is a new connection for the same device Id. So open a new connection, // and if that is successful, close the existing one. else { ICloudProxy newCloudProxy = await this.CreateNewCloudProxyAsync(tokenProvider); await cp.CloseAsync(); return(newCloudProxy); } }) // No existing cloud proxy, so just create a new one. .GetOrElse(() => this.CreateNewCloudProxyAsync(tokenProvider)); // Set Identity only after successfully opening cloud proxy // That way, if a we have one existing connection for a deviceA, // and a new connection for deviceA comes in with an invalid key/token, // the existing connection is not affected. this.cloudProxy = Option.Some(proxy); Events.UpdatedCloudConnection(this.Identity); return(proxy); } catch (Exception ex) { Events.CreateException(ex, this.Identity); throw; } finally { this.callbacksEnabled = true; } } }