/// <summary> /// Initializes a new instance of the <see cref="Client"/> class. /// </summary> /// <param name="channel">The channel.</param> /// <param name="certificate">The certificate.</param> /// <param name="guid">The unique identifier.</param> /// <param name="ipAddress">The ip address.</param> private Client(Channel channel, string certificate, string guid, IPAddress ipAddress) { this.channel = channel; this.httpClient = CreateHttpClient(certificate, guid); this.grpcClient = new ExternalInterfaceClient(channel); this.IPAddress = ipAddress; }
public async Task ConnectAsync(RobotConnectionInfo connectionInfo) { if (Client == null) { //create channel var ssl = new SslCredentials(connectionInfo.Certificate); var interceptor = new AsyncAuthInterceptor((context, metadata) => { metadata.Add("authorization", $"Bearer {connectionInfo.Token}"); return(Task.CompletedTask); }); var cred = ChannelCredentials.Create(ssl, CallCredentials.FromInterceptor(interceptor)); _channel = new Channel(connectionInfo.IpAddress, 443, cred, new ChannelOption[] { new ChannelOption("grpc.ssl_target_name_override", connectionInfo.RobotName) }); //connect to client try { await _channel.ConnectAsync(DateTime.UtcNow.AddSeconds(10)); } catch (TaskCanceledException ex) { throw new VectorConnectionException("could not connect to Vector. insure IP address is correct and that Vector is turned on", ex); } //create client Client = new ExternalInterfaceClient(_channel); IsConnected = true; } }
/// <summary> /// Gets the authentication token from the robot. /// </summary> /// <param name="sessionId">The session identifier.</param> /// <param name="certificate">The SSL certificate for the robot.</param> /// <param name="robotName">Name of the robot.</param> /// <param name="ipAddress">The IP address of the robot.</param> /// <returns>A task that represents the asynchronous operation; the task result contains the authentication token.</returns> /// <exception cref="ArgumentException"> /// Session ID must be provided - sessionId /// or /// SSL certificate must be provided - certificate /// </exception> /// <exception cref="Anki.Vector.Exceptions.VectorAuthenticationException"> /// Robot name must be provided. /// or /// Robot name is not in the correct format. /// or /// IP address must be provided /// or /// Unable to establish a connection to Vector. /// or /// Failed to authorize request. Please be sure to first set up Vector using the companion app. /// </exception> public static async Task <string> GetTokenGuid(string sessionId, string certificate, string robotName, IPAddress ipAddress) { if (string.IsNullOrEmpty(sessionId)) { throw new ArgumentException("Session ID must be provided.", nameof(sessionId)); } if (string.IsNullOrEmpty(certificate)) { throw new ArgumentException("SSL certificate must be provided.", nameof(certificate)); } if (string.IsNullOrEmpty(robotName)) { throw new ArgumentException("Robot name must be provided.", nameof(robotName)); } if (!RobotNameIsValid(robotName)) { throw new ArgumentException("Robot name is not in the correct format.", nameof(robotName)); } if (ipAddress == null) { throw new ArgumentNullException(nameof(ipAddress), "IP address must be provided."); } // Create the channel var channel = new Channel( ipAddress.ToString() + ":443", ChannelCredentials.Create(new SslCredentials(certificate), CallCredentials.FromInterceptor((context, metadata) => Task.CompletedTask)), new ChannelOption[] { new ChannelOption("grpc.ssl_target_name_override", robotName) } ); try { // Open the channel await channel.ConnectAsync(Robot.GrpcDeadline(15_000)).ConfigureAwait(false); } catch (Exception ex) { // If failed to open channel throw exception throw new VectorAuthenticationException(VectorAuthenticationFailureType.Connection, "Unable to establish a connection to Vector.", ex); } // Create the client and return the response var client = new ExternalInterfaceClient(channel); var response = await client.UserAuthenticationAsync(new UserAuthenticationRequest() { UserSessionId = Google.Protobuf.ByteString.CopyFromUtf8(sessionId), ClientName = Google.Protobuf.ByteString.CopyFromUtf8(Dns.GetHostName()) }); if (response.Code != UserAuthenticationResponse.Types.Code.Authorized) { throw new VectorAuthenticationException(VectorAuthenticationFailureType.Login, "Failed to authorize request. Please be sure to first set up Vector using the companion app."); } await channel.ShutdownAsync().ConfigureAwait(false); return(response.ClientTokenGuid.ToStringUtf8()); }
/// <summary> /// Required for first time use. Used to grant this device access to a Vector robot. The robot must be joined to the wifi network using the Vector Companion App before running this. /// </summary> /// <param name="robotName">Find your robot name (ex. Vector-A1B2) by placing Vector on the charger and double-clicking Vector's backpack button.</param> /// <param name="ipAddress">Find your robot ip address (ex. 192.168.42.42) by placing Vector on the charger, double-clicking Vector's backpack button, then raising and lowering his arms.If you see XX.XX.XX.XX on his face, reconnect Vector to your WiFi using the Vector Companion App.</param> /// <param name="serialNumber">Please find your robot serial number (ex. 00e20100) located on the underside of Vector, or accessible from Vector's debug screen.</param> /// <param name="userName">Enter your email. Make sure to use the same account that was used to set up your Vector through the Companion app.</param> /// <param name="password">Enter your password. Make sure to use the same account that was used to set up your Vector through the Companion app.</param> /// <returns>used to connect to the robot</returns> public static async Task <RobotConnectionInfo> GrantAsync(string robotName, string ipAddress, string serialNumber, string userName, string password) { var result = new RobotConnectionInfo(); robotName = FormatRobotName(robotName); serialNumber = FormatSerialNumber(serialNumber); //get the certificate var client = new HttpClient(); var response = await client.GetAsync(@"https://session-certs.token.global.anki-services.com/vic/" + serialNumber); if (!response.IsSuccessStatusCode) { throw new VectorAuthorizationException("invalid serial number"); } result.Certificate = await response.Content.ReadAsStringAsync(); //validate robot name var cert = new X509Certificate(Encoding.ASCII.GetBytes(result.Certificate)); var certSubject = cert.Subject.Split(',').Select(i => new KeyValuePair <string, string>(i.Split('=')[0].Trim(), i.Split('=')[1].Trim())); var commonName = certSubject.First(i => i.Key == "CN").Value; if (commonName != robotName) { throw new VectorAuthorizationException($"The name of the certificate ({commonName}) does not match the name provided robotName ({robotName}) Please verify the name, and try again."); } result.RobotName = robotName; //get session token response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Post, @"https://accounts.api.anki.com/1/sessions") { Headers = { { "User-Agent", "Vector-sdk/0.5.1" }, { "Anki-App-Key", AnkiAppKey } }, Content = new FormUrlEncodedContent(new Dictionary <string, string>() { { "username", userName }, { "password", password } }), }); if (!response.IsSuccessStatusCode) { throw new VectorAuthorizationException("invalid userName and password"); } var userSession = JsonConvert.DeserializeObject(await response.Content.ReadAsStringAsync()) as JObject; var sessionToken = userSession["session"]["session_token"].Value <string>(); //have robot authenticate with session token and retreive the robots client token var ssl = new SslCredentials(result.Certificate); var channel = new Channel(ipAddress, 443, ssl, new ChannelOption[] { new ChannelOption("grpc.ssl_target_name_override", robotName) }); try { await channel.ConnectAsync(DateTime.UtcNow.AddSeconds(10)); } catch (TaskCanceledException ex) { throw new VectorAuthorizationException("Timeout. Could not connect to vector. insure IP address is correct", ex); } var robotClient = new ExternalInterfaceClient(channel); var hostname = System.Net.Dns.GetHostName(); var authRequest = new UserAuthenticationRequest() { ClientName = ByteString.CopyFromUtf8(hostname), UserSessionId = ByteString.CopyFromUtf8(sessionToken) }; var authResult = await robotClient.UserAuthenticationAsync(authRequest); if (authResult.Code == UserAuthenticationResponse.Types.Code.Unauthorized) { throw new VectorAuthorizationException("unauthorized"); } result.Token = authResult.ClientTokenGuid.ToStringUtf8(); result.IpAddress = ipAddress; await channel.ShutdownAsync(); return(result); }