The GraphQL API requires authentication for requests. Remote.It authentication uses HTTP Request Signature. The advantages of this method are:
Keys do not expire
Keys can be disabled and revoked (deleted) by the user at any time
Keys are not dependent on password
Keys are more secure (the secret is never transmitted because the request is signed)
With 2FA enabled, you'll need to provide an authentication code when accessing Remote.It through your browser, desktop and mobile apps. If you access Remote.It using other methods, such as the the CLI, you'll need to sign in with credentials using and access key and secret.
Key Management
You can generate, enable, disable and delete keys in the Account section of the web portal here .
Please note: Generation of keys is crypto-random and the secret is only available immediately after creation by clicking the “Show secret access key” link or downloading the key as a CSV file (containing the Access Key ID and Secret Access Key)
You are limited to 2 active access keys. The account page will also show when the key was created and last used for authentication. If you suspect your key has been compromised, generate a new one, replace it in your code and disable it. If desired you can delete the compromised key after disabling it.
In addition, if you will be using the REST-API you will also need to retrieve your Developer API Key. This can also be found in the Account section of the web portal.
Create a remote.it Credentials File
You will need to follow the steps above with Key Management to generate your access key and secret before proceeding. Then, create a file to save your Remote.It credentials. The file name should be credentials with no extension. The folder depends on your operating system.
You can save more than one key pair under different profiles (sections) in the Remote.It credentials file. DEFAULT is the default profile name. Profiles name is case sensitive and should not have a "." in the name.
API Request Signing
Timezones
Request signing is done for each request independently and is sensitive to clock drift based on the system time of the machine making the API call.
Best practice is to use UTC or GMT when using the signature methods to avoid ambiguous time zones when dates are generated by your code.
Examples
These are examples of query requests only for the purposes of how to do the request signing. Please refer to the schema and usage documentation for requests which are supported.
The examples reads the ~/.remoteit/credentials file for the variables of your access key, secret, and developer key.
This example is using a helper library requests_http_signature==v0.7.1 for Python 3 which will sign the request before submitting it to the server. This demonstrates how to safely reference the key and secret from the ~/.remoteit/credentials file.
Reference functions create a connection and have a disconnect.
import configparser
import json
import os.path
from base64 import b64decode
import requests
from requests_http_signature import HTTPSignatureAuth, algorithms
# File and URL Constants
CREDENTIALS_FILE = "~/.remoteit/credentials"
REMOTEIT_URL = 'https://api.remote.it/graphql/v1'
def remoteit_authorizer(profile="DEFAULT"):
"""Authorize remote.it API access using credentials from a file.
Args:
profile (str, optional): Profile name in credentials file. Defaults to "DEFAULT".
Returns:
HTTPSignatureAuth: Auth object to be used in requests.
"""
# Resolve the full path for the credentials file
file = os.path.expanduser(CREDENTIALS_FILE)
# Check if the file exists
if not os.path.exists(file):
raise FileNotFoundError(f"remote.it credentials file not found: {file}")
# Parse the credentials file
config = configparser.ConfigParser()
try:
config.read(file)
except Exception as e:
raise Exception(f"remote.it credentials file error: {e}")
# Fetch credentials for the selected profile
credentials = config[profile]
# Validate existence of required keys
key_id = credentials.get('R3_ACCESS_KEY_ID')
key = credentials.get('R3_SECRET_ACCESS_KEY')
if not key_id or not key:
raise Exception("Missing required credentials")
# Create and return auth object
return HTTPSignatureAuth(key_id=key_id, key=b64decode(key), signature_algorithm=algorithms.HMAC_SHA256)
def remoteit_api(auth, query, variables=None):
"""Send a GraphQL query to remote.it API.
Args:
auth (HTTPSignatureAuth): Auth object.
query (str): GraphQL query string.
variables (dict, optional): Variables for the GraphQL query. Defaults to None.
Returns:
dict: Parsed JSON response from the API.
"""
response = requests.post(REMOTEIT_URL, json={"query": query, "variables": variables}, auth=auth)
if response.status_code != 200:
raise Exception(f"remote.it API error: {response.status_code}")
return json.loads(response.text)
def connect(auth, service_id):
"""Connect to a remote.it service and return session details.
Args:
auth (HTTPSignatureAuth): Auth object.
service_id (str): The service ID.
Returns:
tuple: Session ID, host, and port.
"""
query = "mutation connect($id: String!) {connect(serviceId: $id) {id host port}}"
response = remoteit_api(auth, query, variables={"id": service_id})
connection = response["data"]["connect"]
return connection["id"], connection["host"], connection["port"]
def disconnect(auth, session_id):
"""Disconnect a remote.it session.
Args:
auth (HTTPSignatureAuth): Auth object.
session_id (str): The session ID to disconnect.
Returns:
bool: True if successful, False otherwise.
"""
query = "mutation disconnect($id: String!) {disconnect(connectionId: $id)}"
response = remoteit_api(auth, query, variables={"id": session_id})
return response["data"]["disconnect"]
# Example usage
if __name__ == "__main__":
# Initialize authorizer
authorizer = remoteit_authorizer()
# Connect to a service
service_id = "80:XX:XX:XX:XX:XX:XX:XX"
session_id, host, port = connect(authorizer, service_id)
print(f"Connected: session_id={session_id}, host={host}, port={port}")
# Disconnect from the service
status = disconnect(authorizer, session_id)
print(f"Disconnected: {status}")
This example is a port of the bash/cURL example assuming a C# program and that you have stored your credentials in a file as indicated from the credential file generation and only ONE profile. If there is more than one, it will use the first. You could change this to load them from an environment variable instead.
using System.Security.Cryptography;
using System.Text;
class Program
{
const string credsFile = ".remoteit/credentials";
class KeyAndSecret
{
public bool HasError;
public string Error;
public string Key;
public string Secret;
}
private static async Task Main()
{
KeyAndSecret keyAndSecret = readKeyAndSecret();
if (keyAndSecret.HasError)
{
Console.WriteLine(keyAndSecret.Error);
return;
}
string host = "api.remote.it";
string urlPath = "/graphql/v1"; // Ensure proper formatting with a trailing slash
string url = $"https://{host}{urlPath}";
string verb = "POST";
string contentType = "application/json";
string date = DateTime.UtcNow.ToString("r"); // RFC 1123 format
string data = "{ \"query\": \"{ login { email devices (size: 1000, from: 0) { items { id name services { id name} } } } }\" }";
// string data = "{\"query\":\"{ applicationTypes { id name description port proxy protocol } }\"}";
// line to sign
string lineToSign = $"(request-target): {verb.ToLower()} {urlPath}\n"
+ $"host: {host}\n"
+ $"date: {date}\n"
+ $"content-type: {contentType}\n"
+ $"content-length: {data.Length}";
byte[] signedLine = generateSignature(lineToSign, keyAndSecret.Secret);
string signedLineAsBase64 = Convert.ToBase64String(signedLine);
// signature line
string signatureLine = $"keyId=\"{keyAndSecret.Key}\""
+ $",algorithm=\"hmac-sha256\""
+ $",headers=\"(request-target) host date content-type content-length\""
+ $",signature=\"{signedLineAsBase64}\"";
// create HTTP request
var content = new StringContent(data, Encoding.UTF8, contentType);
content.Headers.Remove("Content-Type");
content.Headers.Remove("Content-Length");
content.Headers.Add("Content-Type", contentType);
content.Headers.Add("Content-Length", data.Length.ToString());
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Add("User-Agent", "sample-sdk-c#");
request.Headers.Add("Date", date);
request.Headers.Add("Authorization", "Signature " + signatureLine);
request.Content = content;
// execute + read response
var client = new HttpClient();
HttpResponseMessage response = await client.SendAsync(request);
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine("\nResponse: {0}", response.ToString());
Console.WriteLine("\nResponse Body: {0}", responseBody);
}
private static byte[] generateSignature(string lineToSign, string secret)
{
byte[] secretAsBytes = Convert.FromBase64String(secret);
using (var hmac = new HMACSHA256(secretAsBytes))
{
byte[] lineToSignAsBytes = Encoding.UTF8.GetBytes(lineToSign);
return hmac.ComputeHash(lineToSignAsBytes);
}
}
private static KeyAndSecret readKeyAndSecret()
{
//Reads the credentials from the credentials file. If there is more than one, it will take the last; or you can read from environment variables instead.
string homeFolder = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
string credentialsPath = Path.Combine(homeFolder, credsFile);
if (!File.Exists(credentialsPath))
{
return new KeyAndSecret { HasError = true, Error = "err: credentials file not found" };
}
var credentials = File.ReadAllLines(credentialsPath);
string accessKeyId = "";
string secretAccessKey = "";
//assumes that there is one profile. If you have more than one, adapt this part as needed.
bool accessKeyFound = false;
bool secretKeyFound = false;
foreach (var line in credentials)
{
if (line.StartsWith("R3_ACCESS_KEY_ID="))
{
accessKeyId = line.Split(new char[] { '=' }, 2)[1];
accessKeyFound = true;
}
else if (line.StartsWith("R3_SECRET_ACCESS_KEY="))
{
secretAccessKey = line.Split(new char[] { '=' }, 2)[1];
secretKeyFound = true;
}
if (accessKeyFound && secretKeyFound)
break;
}
if (string.IsNullOrEmpty(accessKeyId) || string.IsNullOrEmpty(secretAccessKey))
{
return new KeyAndSecret { HasError = true, Error = "err: missing API credentials in the credentials file" };
}
return new KeyAndSecret { Key = accessKeyId, Secret = secretAccessKey };
}
}
To authenticate an API request, the client must generate a signature using the previously created key and secret. The REST-API example you will also need your Developer API Key which you can get from your account page
You can reference different standard implementations of the signature in different languages from w3c-ccg