HTTP Proxy

The TAG Neuron® now (from build 2026-05-12) has a new API: A generic HTTP Proxy at /HttpProxy. It allows clients to use the broker as a proxy to access resources on the Internet. This can be useful in multiple cases, for instance in environments where access to certain resources are limited, but the Neuron has free access. One such example is accessing HTTP-only resources from a smart phone environment, which requires use of HTTPS, and the remote web server does not have HTTPS enabled. (This is the case for certain ICAO Certificate Revocation Lists for example.)

Authentication

The HTTP Proxy resource requires authentication. The reason for this restriction is to avoid the broker to become a point for third parties to commit cyber-crime. The resource supports multiple form of authentication: Normal WWW-Authenticate is supported, mTLS (for brokers where this is enabled), or JWT Bearer tokens. Session login is also supported. This makes it possible to integrate the resource in web pages hosted by the Neuron.

JWT Bearer tokens is usedful in environments based on XMPP, where access to HTTP-based resources is required, but restricted by the operating system, such as the Neuro-Access smart phone app.

Since the app, in this case, has an XMPP connection to the broker, it can get a JWT token from the broker by using the HttpxClient class (HTTP over XMPP) in the Waher.Networking.XMPP.HTTPX library. Calling the GetJwtToken method returns a JWT token that can be used to make HTTP requests to the broker, authenticated as the XMPP account.

If using InternetContent in the Waher.Content library to access content on the Internet, you can redirect HTTP-only links to a custom resource by providing an event handler to the WebGetter.HttpUriEventHandler event. Take the opportunity to provide a new URI in the event arguments, redirecting the request to the HTTP proxy resource of the Nueron to which the client is connected using XMPP. You also need to add the Bearer token to the Request headers available in the event arguments. The URI itself is then URI encoded and added as a sub-path to the /HttpProxy/ resource.

Example

The first step is to get a JWT token from the Neuron, identifying the XMPP connection the client has. We use the HttpxClient method GetJwtTokenAsync method, providing the number of seconds we want the token to be valid:

string Token = await this.httpxClient.GetJwtTokenAsync(60);

We then create an event handler to reroute HTTP-only requests to the HTTP proxy on the Neuron to which the client is using, using HTTPS. Creating an event handler for this purpose is simple (replacing DOMAIN in the example with the domain of the Neuron). We also need to add the Bearer JWT token by using an Authorization header:

private static void ViaProxy(object Sender, HttpUriEventArgs e)
{
	e.Uri = new Uri("https://DOMAIN/HttpProxy/" + WebUtility.UrlEncode(e.Uri.ToString()));
	e.Request.Headers.Add("Authorization", "Bearer " + Token);
}

We need to assign the event handler to the event:

WebGetter.HttpUriEventHandler += ViaProxy;

Later, when we do not need to redirect HTTP requests any longer, we need to unregister it:

WebGetter.HttpUriEventHandler -= ViaProxy;

While the event handler is registered, any access to web resources using HTTP, by using the static InternetContent class, will be seamlessly redirected to the HTTP proxy. Example:

ContentResponse Response = await InternetContent.GetAsync(
	new Uri("http://example.org/"),
	new KeyValuePair<string, string>("Accept", HtmlCodec.DefaultContentType));

#new, #features, #api, #security, #neuron


HTTP-XMPP Bridge

The IoTBridgeHttp repository provides an IoT bridge between external devices that POST sensor data to the bridge using HTTP, for use in closed intra-networks or enterprise networks, or the public Internet, and the harmonized XMPP-based Neuro-Foundation network, for open and secure cross-domain interoperation on the Internet.

Bridge topology
Bridge topology

To run the bridge, you need access to an XMPP broker that supports the Neuro-Foundation extensions. You can use the TAG Neuron for XMPP.

Running and configuring the bridge

The code is written using .NET Standard, and compiled to a .NET Core console application that can be run on most operating systems. Basic configuration is performed using the console interface during the first execution, and persisted. You can also provide the corresponding configuration using environment variables, making it possible to run the bridge as a container. If an environmental variable is missing, the user will be prompted to input the value on the console.

Environmental Variable Type Description
XMPP_HOST String XMPP Host name
XMPP_PORT Integer Port number to use when connecting to XMPP (default is 5222)
XMPP_USERNAME String User name to use when connecting to XMPP.
XMPP_PASSWORD String Password (or hashed password) to use when connecting to XMPP. Empty string means a random password will be generated.
XMPP_PASSWORDHASHMETHOD String Algorithm or method used for password. Empty string means the password is provided in the clear.
XMPP_APIKEY String API Key. If provided together with secret, allows the application to create a new account.
XMPP_APISECRET String API Secret. If provided together with key, allows the application to create a new account.
REGISTRY_COUNTRY String Country where the bridge is installed.
REGISTRY_REGION String Region where the bridge is installed.
REGISTRY_CITY String City where the bridge is installed.
REGISTRY_AREA String Area where the bridge is installed.
REGISTRY_SRTEET String Street where the bridge is installed.
REGISTRY_STREETNR String Street number where the bridge is installed.
REGISTRY_BUILDING String Building where the bridge is installed.
REGISTRY_APARTMENT String Apartment where the bridge is installed.
REGISTRY_ROOM String Room where the bridge is installed.
REGISTRY_NAME String Name associated with bridge.
REGISTRY_LOCATION Boolean If location has been completed. (This means, any location-specific environment variables not provided, will be interpreted as intensionally left blank, and user will not be prompted to input values for them.
JWT_SECRET String Secret used to create JWT tokens. If not provided, a random secret will be created.
X509_FILENAME String File name to X.509 certificate to use. If not provided, HTTPS will not be enabled.
X509_PASSWORD String Password to X.509 certificate.
HTTP_PORT Integer Port number to use for unencrypted HTTP. (default is 80)
HTTPS_PORT Integer Port number to use for encrypted HTTPS. (default is 443)
ADMIN_NAME String Administrator User name.
ADMIN_PASSWORD String Administrator Password.
USER_COUNT Integer Number of users to create. (Default is 0, which will trigger manual input of users.)
USER_N_NAME String User name for user N. (where N is a number between 1 and USERS_COUNT)
USER_N_PASSWORD String Password for user N. (where N is a number between 1 and USERS_COUNT)
USER_N_PRIVILEGE String Regular expression specifying the privilege or privileges held by the user. (Default is Admin.SensorData.Post.)

Running in a Docker container

You can run the bridge in a Docker container. When doing so it is important to either configure all settings via the environment variables, or to redirect stdin and allocate a TTY. You do this using the -it switch to docker run. For example:

docker run -it iot-bridge-http

Setting up persistent storage

Persistent storage is required to store configuration, as well as data about the bridge, its nodes, and ownerships, etc. You can do this by mapping the local folder /var/lib/IoT Gateway to a Volume when creating the container.

Using an environment file

You can provide environment variables using an environment file. Create a new text file based on the IoTBridgeHttp.env file in the repository. Set the values you want to provide, and then use the --env-file switch when creating the container. For example:

docker run -it --env-file IoTBridgeHttp.env -v /my/local/folder:/var/lib/IoT\ Gateway iot-bridge-http

Note: If providing credentials, make sure the file is not accessible by others, and make sure it is not checked in to any repository. An alternative to providing credentials in an environment file, is to enable standard and terminal input, and provide it via the prompt (see above).

Claiming ownership of bridge

Once the bridge has been configured, it will generate an iotdisco URI, and save it to its programd data folder. It will also create a file with extension .url, containing a shortcut with the iotdisco URI inside. A .png file with a QR code will also be generated. All three files contain information about the bridge, and allows the owner to claim ownership of it. This can be done by using the Neuro-Access App. This app is also downloadable for Android and iOS. You scan the QR code (or enter it manually), and claim the device. Once the device is claimed by you, you will receive notifications when someone wants to access the deice. They will only be able to access it with the owner’s permission. For more information, see:

Configuring the bridge

The bridge can be configured in detail by a client that implements the concentrator interface. Concentrators consist of data sources, each containing tree structures of nodes. Nodes may be partitioned into partitions, which permits the nesting of subsystems seamlessly into container systems. Each node can be of different types, and have different properties and underlying functionality. They can each implement then sensor interface and actuator interface.

You can use the Simple IoT Client to configure concentrators and their nodes in detail. An initial setup is done using the initial configuration of the bridge. The client is also available in the IoTGateway repository, in the Clients folder.

Node Types

The bridge includes several different node types that can be used to configure its operation:

  • The Local Web Server Node represents the local web server in the gateway. This node hosts the web service that allows external devices to POST sensor data to the bridge. It also acts as the root node for the subtree of nodes representing devices that receive sensor data via HTTP POST requests.

  • The XMPP Broker maintains a connection to an XMPP Broker. It allows the bridge to connect to other entities on the federated network and communicate with them. It supports communication with remote standalone sensors and actuators, as well as remote concentrators embedding devices into data sources and nodes. Such concentrators can be bridges to other protocols and networks.

    Note: The bridge has a client-to-server connection by default, setup during initial configuration. Through this connection, the bridge acts as a concentrator. Through the use of XMPP Broker nodes you can setup additional XMPP connections to other brokers. In these cases the bridge will only act as a client, to connect to remove devices for the purposes of interacting with them.

  • IP Host nodes allow you to monitor network hosts accessible from the bridge.

  • Script nodes allow you to create nodes with custom script logic. They can be used to interface bespoke devices in the network accessible from the bridge, for example.

  • Virtual nodes are placeholders where external logic (or script logic) can aggregate information in a way that makes them accessible by others in the federated network.

API Reference

The local web service registers a series of web resources that external devices can use. Following is a brief overview, with references for more details.

Sensor Data Receptor

The Sensor data receptor resource /ReportSensorData is used by external devices to POST sensor data to the bridge. The device needs to authenticate with the bridge, before it can be authorized to access this resource. The device can use different mechanisms to authenticate itself with the bridge:

  • Use of Mutual TLS (mTLS). This requires the bridge to be configured with a certificate.
  • Use of WWW-Authenticate web login procedure.
  • Use of JSON Web Tokens (JWT) Bearer tokens for authentication. This requires the device to login first using the Login resource (see below).

For details on how the resource works, see the Sensor Data Receptor API endpoint on lab.tagroot.io as an example. The same page can be viewed on the bridge, once it is up and running.

Login resource

The Login resource /Login is used to login to the bridge. If successful, a Bearer JWT token is returned. External devices need to login to the bridge before they can POST sensor data to it. A token is valid for 1 hour. The external device needs to renew the token by loggin in again, if accessing the bridge for a longer period of time.

Input payload is expected to be a JSON object of the following type.

{
	"UserName": Required(Str(PUserName)),
	"PasswordHash": Required(Str(PPasswordHash)),
	"Nonce": Required(Str(PNonce))
}

The response is a JSON object of the following type:

{
	"Ok": Required(Bool(POK)),
	"Message": Required(Str(PMessage)),
	"Token": Optional(Str(PToken))
}

See the Web login procedure article for more details on how to compute the password hash and nonce values, and use them in the login process.

Root folder resource

The Root folder resource /. If no specific resource above is referenced, the default is to look for a file resource in the Root folder, and return it if found. This allows you to host custom web content on the bridge.

Web Page

The root resource / makes a temporary redirection to /Index.md, which is the landing page for the bridge. It contains basic information about the bridge, as well as a login-mechanism to access the setup of the bridge. Once logged in, you can manually edit roles and users.

Security

The HTTP bridge supports multiple levels of security:

  • Access to things published on the federated network is protected by provisioning. When the gateway starts, it generates an iotdisco URI, which can be used to claim ownership of the device. Once claimed, the owner receives notifications when someone wants to access the device, and can decide whether to allow access or not.

  • The bridge supports a set of users and roles, each defining a set of privileges. During first start, the initial users and roles are configured. This can be done either using environment variables, or by providing input on the console. An administrator user is created, which will have all privileges. Once the bridge is up and running, the administrator can login and configure existing users and roles, and create new ones.

  • A Web-Application Firewall (WAF) is included in the bridge. The administrator can configure the WAF to block or allow access to specific resources, based on application-level rules. This can be used to restrict access to certain pages (such as the administrative pages) to certain IPs, for instance, or rate-limit access to certain resources, such as sensor-data publication resrouces.

    The Web-Application Firewall is defined in the WAF.xml file. It is an XML file that needs to validate against the https://waher.se/Schema/WAF.xsd namespace. The default version restricts access to the administration pages to local area network IP addresses.

#new, #repository, #iot, #http, #xmpp, #bridge


Age-verification without leaking birth date

This article describes the process of how to create a Legal Identity that can be used to demonstrate you have reached a certain age, without having to disclose your actual birth date. This is an important mechanism to protect the integrity of personal information, especially if the person is a child.

Creating a Preview ID application

The first step is to create a Preview Legal Identity application containing sufficient personal information so that the age can be verified. This application will have to contain sensitive personal information, including birth date. But creating a Preview application ensures the data is not stored in a searchable database or logs. Instead it is stored in encrypted form, and only during the process to validate the information. Once the preview application has been validated, a new application can be created, containing a subset of the sensitive information, and once that is completed, the sensitive information will be removed.

The personal data necessary to include in the Preview application is:

Property Description
BDAY Birth Day
BMONTH Borth Month
BYEAR Birth Year
AGEABOVE Age above the stated number of years

Creating real ID application

Once the Preview application has been approved, a new, real ID application is made. This new application has to have a reference to the Preview application, and should only contain a subset of the properties. At a minimum, the following properties should be included:

Property Description
PREVIEW Reference to the earlier Preview application.
AGEABOVE Age above the stated number of years

Once this application has been approved, the sensitive personal information will be removed as the preview application is deleted.

Proving your age

Proving your age is as simple as performing a Quick Login with the new identity. If property filters are used, include the AGEABOVE property. As the new identity contains a reduced set of information, and no birth-date (if it was removed in the second step above), only the AGEABOVE property is received, together with proof the Neuron has validated the claim.

How to set AGEABOVE

The AGEABOVE property should not be set to the age of the user, as that can leak personal information. Instead, it should be set to a legal limit required for one or more specific purposes. If there’s an age requirement in a country for a particular purpose, that age should be encoded in the AGEABOVE property. The property will be accepted if the personal information provided in the preview can be validated, and the age of the person deduced is at or above the indicated number.

As there are different age limits for different purposes and countries (for instance, 13, 15, 16, 17, 18, 20 and 21 are common limits), the site that tests the age of a user should not expect the number to be a specific value. Instead, it should expect the number to be at least a specific number. If there’s an age requirement for a service of 16 years of age, identities with any number for AGEABOVE greater or equal to 16 should be accepted as proof ther person is at least 16 years old.

Ensuring you have reached a certain age to create an identity

The AGEABOVE property can also be used to ensure the user of the app attempting to create an identity, has reached a certain age. Adding the property ensures the identity attempted to be created only gets approved, if the person actually has reached the age.

If the person has not reached the corresponding age, the property will become invalid, and an error with error code AgeNotReached will be logged on the application.

#new, #neuron, #features, #id, #security, #privacy


Validating Electronic Travel Documents & ICAO PKI Certificates

When validating electronic Travel Documents, applications need to validate against corresponding issuer certificates not managed by the operating system. To do that, the application needs to get access to these certificates somehow, and build custom X.509 chains during validation. To avoid having such an application to embed the entire list of all these issuer certificates, a list which also get regularly updated, a new repository and package is available that publishes these certificates on Neurons where the package gets installed. Applications can easily check with the corresponding neuron and download the required certificates, based on the Authority Key Identifiers available in the Electronic Travel Document certificates.

Information about the ICAO PKI Certificate package
Package IcaoPkiCertificates.package
Installation key vAa0l/iFHVogQYUzm+Zs6qPsw+7lYrnyFn4MNAGA7+Gso442gJJMKjknHqka/YjM6gZZSS65HL8Adbfba1067a1a27163b905869469d6f0d
More information https://github.com/Trust-Anchor-Group/IcaoPkiCertificates

#features, #id, #kyc, #neuron, #api, #repository, #package, #new


Creating broker accounts using script

One way to prepare a set of Neuro-Access accounts for a predefined set of users, is to pre-create them using script and then generate onboarding codes that can be sent to the users. Once they receive their corresponding code, they can download the app, scan the code, and connect directly to the predefined account. This method allows you to create accounts on a predefined Neuron for a known set of users, without having to create API keys, and minimizing the risk of the users creating accounts on other neurons. It also allows you to predefine the account names.

To try the following tutorial, you need access to the script prompt on your neuron. The script prompt is available from the administrative page via this option:

Script Prompt from Admin portal
Script Prompt from Admin portal

Creating the account

The main module of the XMPP broker, hosting the accounts, is Waher.Service.IoTBroker.XmppServerModule, or XmppServerModule for short. It has a property called PersistenceLayer that acts as an interface to the database for the XMPP server. This interface has a method called CreateAccount that allows us to create accounts. This method performs necessary checks to ensure the name is not occupied elsewhere, to protect integrity. Typing its fully qualified name into the prompt allows us to find its arguments:

CreateAccount method arguments
CreateAccount method arguments

Note: Finding the names of the types, properties and methods is a combination of using the Namespaces & Types option from the administrative portal, and/or using the script prompt together with the methods and properties functions. You can also use the Neuron Documentation with its DoxyGen code documentation site where you can search for classes and other relevant names.

We are now ready to create accounts. We will generate account names that are GUIDs, using the NewGuid() script function. Each account will receive a random password with sufficient entropy, generated using the RandomPassword() script function. We will not use an API Key (leaving it empty), making the account behave as a manually created account. We will not assign e-mail adddresses or phone numbers to the accounts either. To ensure the account object has a proper remote endpoint associated with its creation, we will use the Request.RemoteEndPoint value, which will contain the remote endpoint of the person executing the script function via the script prompt. (If executing the script via some other interface, you might have to change this reference to something else.) The result of calling the CreateAccount method, is a KeyValuePair<IAccount,string[]> result, where the Key portion contains the new account, if created (or null if not), and the Value portion any alternative name suggestions in case the account could not be created.

P:=Waher.Service.IoTBroker.XmppServerModule.PersistenceLayer.CreateAccount("",Str(NewGuid()),RandomPassword(),"","",Request.RemoteEndPoint);
Account:=P.Key;
AlternativeNames:=P.Value;

Note: You can call properties(Account) to review the properties of the account. You can also see that access to the generated password is protected to reduce the risk of unauthorized access to the account.

Note 2: If you want, you can create a separate API key, and reference it when you create the account. This way, you can separate the accounts from other manually created accounts. You can also control the amount of accounts that can be created.

Creating an onboarding code

Next step is to create an onboarding code. This is done in cooperation with the onboarding neuron configured for the neuron on which the account was created. The onboarding code is a URI with the obinfo: URI scheme, that references the onboarding neuron, and contains a key and a secret. The onboarding neuron on the other hand receives an encrypted BLOB containg the information necessary to access the account. This information can only be decrypted using the secret key available in the onboarding code. Furthermore, the onboarding code can only be used once. You can also define a point in time when the code expires, if it has not been used.

Note: Since the code can only be used once, it is important to transfer the code in a way where it cannot be previewed by mistake. Previewing it will result in the code automatically expiring.

To create the code, we will call the GetOnboardingUrl method on the Account object. It takes one argument: The point in time when the code will expire if not used before. In the following example, we create a code that will be valid for three days:

Code:=Account.GetOnboardingUrl(Now.AddDays(3));

The Code will contain a string representing the onboarding URI. It will be of the form:

obinfo:id.tagroot.io:14gsjgK56X6.................................:NKxDui0EMoLb1....................................

The Onboarding URI contains four parts, delimited by colons (:):

  1. The obinfo URI scheme identifying the URI as an onboarding URI.
  2. The onboarding neuron that contains the encrypted BLOB (but not the key).
  3. The identifier of the encrypted BLOB.
  4. The secret key necessary to decrypt the BLOB.

Creating an onboarding QR Code

The Neuron has a QR Code API that permits you to create QR codes for the onboarding URIs generated. The procedure is simple: A URL is generated pointing to the /QR resource on the neuron. After the resource name you add the contents you want to encode into the URI, prefixed by a /. The contents must be URL encoded. After the contents, you can add query parameters controlling how the QR code should be generated. This includes controlling the quality of the image, resolution and colors. Displaying a QR code on screen, or in an email requires less resolution, that including the image in print, for instance. A QR code for light mode displays (or white paper) should look different from QR codes for dark mode displays, for instance.

In the following example, we use the Waher.IoTGateway.Gateway.GetUrl(LocalResource) method to generate an absolute URL to an image containing a 400x400 pixel QR code encoding the onboarding URI just created. It uses a quality setting of 2, meaning 2x2 points are evaluated for every pixel generated, creating an anti-aliasing effect to create smoother edges.

CodeUrl:=Waher.IoTGateway.Gateway.GetUrl("/QR/"+UrlEncode(Code)+"?w=400&h=400&q=2");

For our example, this would result in a URL similar to the following:

https://lab.tagroot.io/QR/obinfo%3Aid.tagroot.io%3A14gsjgK56X6.................................%3ANKxDui0EMoLb1....................................?w=400&h=400&q=2

As an image, this QR code would look like:

QR code of onboarding URI
QR code of onboarding URI

The QR Code API documents parameters that can be used to control the colors used when generating the QR code. By appending &fg=white&bg=black&c=c0ffc0 to the URL, for instance, a QR code for dark-mode can be generated instead, as an example:

QR code of onboarding URI - Dark Mode
QR code of onboarding URI - Dark Mode

If higher resolution images are required, you can increase the w (width), h (height) and q (quality) settings. For example, an 800x800 image with 3x3 points per pixel quality would look like:

QR code of onboarding URI - Print
QR code of onboarding URI - Print

Batch creation of accounts

We are now ready to create script for the batch creation of accounts, and generate QR codes that can be distributed separately, for each account created. We will use the preview() function to display current progress in the prompt (and the batch procedure may require some time to execute, depending on the number of accounts and QR codes to create), the Get() function to retrieve the image from the generated QR Code URLs, and the SaveFile() function to save the QR codes generated, and the results of the batch procedure into a single JSON file. We will use the Waher.IoTGateway.Gateway.AppDataFolder property to save files in a location that is easily accessible, and where the Neuron has write-access. We will create a subfolder called Batch for the generated files. Finally, we generate an XML document with the same information, and store it as an XML file also. At the end we have a set of onboarding QR code image files, a JSON file with an index of all accounts created, and a similar XML file with the same information. We create the following batch account-creation function:

CreateAccounts([N]):=
(
	Created:=NowUtc;
	Accounts:=
	{
		"NrAccounts":N,
		"Created":Created
	};
	Records:=[];
	AccountNr:=1;

BatchFolder:=Waher.IoTGateway.Gateway.AppDataFolder+"Batch";
if !System.IO.Directory.Exists(BatchFolder) then
	System.IO.Directory.CreateDirectory(BatchFolder);

while AccountNr<=N do
(
	AccountNrStr:=Str(AccountNr);
	preview("Creating account "+Str(AccountNrStr)+"...");

do
(
	P:=Waher.Service.IoTBroker.XmppServerModule.PersistenceLayer.CreateAccount("",Str(NewGuid()),RandomPassword(),"","",Request.RemoteEndPoint);
	Account:=P.Key;
	AlternativeNames:=P.Value
)
while !exists(Account);

Code:=Account.GetOnboardingUrl(Now.AddDays(3));
CodeUrl:=Waher.IoTGateway.Gateway.GetUrl("/QR/"+UrlEncode(Code)+"?w=400&h=400&q=2");
QrCode:=Get(CodeUrl);

QrCodeFileName:=BatchFolder+"\\"+Left("00000000",9-len(AccountNrStr))+AccountNrStr+".png";
SaveFile(QrCode,QrCodeFileName);

Record:=
{
	"AccountNr":AccountNr,
	"UserName":Account.UserName,
	"OnboardingUrl":Code,
	"QrCodeUrl":CodeUrl,
	"QrCode":QrCodeFileName
};

PushLast(Record,Records);
AccountNr++;
);

Accounts["Records"]:=Records;

SaveFile(Accounts,BatchFolder+"\\Accounts.json");

AccountsXml:=<Accounts nrAccounts=N created=Created>
	<[[foreach Record in Records do 
		<Account accountNr=(Record.AccountNr) userName=(Record.UserName) onboardingUrl=(Record.OnboardingUrl) qrCodeUrl=(Record.QrCodeUrl) qrCode=(Record.QrCode) />
	]]>
</Accounts>;

SaveFile(AccountsXml,BatchFolder+"\\Accounts.xml");
);

You can download the script file from the following link: CreateAccount.script

#tutorial, #script, #account, #neuro-access, #onboarding


Posts by user

No more posts authored by the user could be found. You can go back to the main view by selecting Home in the menu above.