Authentication#
User authentication#
For user authentication Magistrala uses Authentication keys. There are two types of authentication keys:
- User key - keys issued to the user upon login request
- Recovery key - password recovery key
Authentication keys are represented and distributed by the corresponding JWT. User keys are issued when user logs in. Each user request (other than registration and login) contains user key that is used to authenticate the user.
Recovery key is the password recovery key. It's short-lived token used for password recovery process.
The following actions are supported:
- create (all key types)
- verify (all key types)
Federated authentication#
Federated authentication is a process of authenticating users using external identity providers. Magistrala supports federated authentication using OpenID Connect protocol. Magistrala is a resource provider and it uses Google Identity Platform as an identity provider. To use federated authentication, you need to create a project in Google Cloud Platform and enable Google Identity Platform API. After that, you need to create OAuth 2.0 credentials and configure the consent screen. This can be done by following Google's documentation. Once you have created OAuth 2.0 credentials, you need to set the following environment variables:
MG_USERS_GOOGLE_CLIENT_ID=985229335584-m2mft8lqbgfn5gfw9ftrm3r2sgu4tsrw.apps.googleusercontent.com
MG_USERS_GOOGLE_CLIENT_SECRET=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YUIpw
MG_UI_GOOGLE_REDIRECT_URL=http://localhost/google-callback
MG_USERS_GOOGLE_STATE=pGXVNhEeKfycuBzk5InlSfMlEU9UrhlkTUOSqhsgDzXP2Y4RsN
MG_USERS_UI_REDIRECT_URL=http://localhost:9090
MG_USERS_GOOGLE_CLIENT_ID
- Google OAuth 2.0 client IDMG_USERS_GOOGLE_CLIENT_SECRET
- Google OAuth 2.0 client secretMG_UI_GOOGLE_REDIRECT_URL
- Google OAuth 2.0 redirect URL to handle callback after successful authentication. This URL must be registered in the Google Cloud Platform.MG_USERS_GOOGLE_STATE
- Random string used to protect against cross-site request forgery attacks.MG_USERS_UI_REDIRECT_URL
- URL to redirect user after successful authentication. This can be your Magistrala UI URL.
Magistrala handles the authentication callback at <MG_BASE_URL>/google-callback
endpoint, where <MG_BASE_URL>
is the base URL of your Magistrala instance. This endpoint needs to be registered in the Google Cloud Platform and it must match the value of MG_UI_GOOGLE_REDIRECT_URL
environment variable. If an error occurs, the error message is sent from the backend using a query paramters with the key error
. The UI will read the error message from the query parameter and display it to the user. When a user signs up, Magistrala creates a local copy of the user with an ID provided by Google, the name and email address provided by Google and the password is left empty as the user is authenticated using Google, i.e. external user. The user can be created only once, so if the user already exists, the error will be sent to the UI via the error cookie. Finally, the user is redirected to the URL provided in MG_USERS_UI_REDIRECT_URL
environment variable upon successful authentication. This should be the base URL of your UI.
The MG_USERS_GOOGLE_CLIENT_ID
, MG_USERS_GOOGLE_CLIENT_SECRET
, MG_UI_GOOGLE_REDIRECT_URL
and MG_USERS_GOOGLE_STATE
environment variables should be the same for the UI and users service. The MG_USERS_UI_REDIRECT_URL
environment variable should be the URL of your UI which is used to redirect the user after successful authentication.
Magistrala uses the access_token
provided by Google only to fetch user information which includes user id, name, given name, family name, picture and locale. The access_token
is not stored in the database and it's not used for any other purpose. The id_token
is not used as it presents challenges on refreshing it, thus Magistrala issues its own access_token
and refresh_token
stored in the HTTP-only cookie and it's used to authenticate the user in the subsequent requests.
Authentication with Magistrala keys#
By default, Magistrala uses Magistrala Thing secret for authentication. The Thing secret is a secret key that's generated at the Thing creation. In order to authenticate, the Thing needs to send its secret with the message. The way the secret is passed depends on the protocol used to send a message and differs from adapter to adapter. For more details on how this secret is passed around, please check out messaging section. This is the default Magistrala authentication mechanism and this method is used if the composition is started using the following command:
docker-compose -f docker/docker-compose.yml up
Mutual TLS Authentication with X.509 Certificates#
In most of the cases, HTTPS, WSS, MQTTS or secure CoAP are secure enough. However, sometimes you might need an even more secure connection. Magistrala supports mutual TLS authentication (mTLS) based on X.509 certificates. By default, the TLS protocol only proves the identity of the server to the client using the X.509 certificate and the authentication of the client to the server is left to the application layer. TLS also offers client-to-server authentication using client-side X.509 authentication. This is called two-way or mutual authentication. Magistrala currently supports mTLS over HTTP, WS, MQTT and MQTT over WS protocols. In order to run Docker composition with mTLS turned on, you can execute the following command from the project root:
AUTH=x509 docker-compose -f docker/docker-compose.yml up -d
Mutual authentication includes client-side certificates. Certificates can be generated using the simple script provided here. In order to create a valid certificate, you need to create Magistrala thing using the process described in the provisioning section. After that, you need to fetch created thing secret. Thing secret will be used to create x.509 certificate for the corresponding thing. To create a certificate, execute the following commands:
cd docker/ssl
make ca CN=<common_name> O=<organization> OU=<organizational_unit> emailAddress=<email_address>
make server_cert CN=<common_name> O=<organization> OU=<organizational_unit> emailAddress=<email_address>
make thing_cert THING_SECRET=<thing_secret> CRT_FILE_NAME=<cert_name> O=<organization> OU=<organizational_unit> emailAddress=<email_address>
These commands use OpenSSL tool, so please make sure that you have it installed and set up before running these commands. The default values for Makefile variables are
CRT_LOCATION = certs
THING_SECRET = d7cc2964-a48b-4a6e-871a-08da28e7883d
O = Magistrala
OU = magistrala
EA = info@magistrala.com
CN = localhost
CRT_FILE_NAME = thing
Normally, in order to get things running, you will need to specify only THING_SECRET
. The other variables are not mandatory and the termination should work with the default values.
- Command
make ca
will generate a self-signed certificate that will later be used as a CA to sign other generated certificates. CA will expire in 3 years. - Command
make server_cert
will generate and sign (with previously created CA) server cert, which will expire after 1000 days. This cert is used as a Magistrala server-side certificate in usual TLS flow to establish HTTPS or MQTTS connection. - Command
make thing_cert
will finally generate and sign a client-side certificate and private key for the thing.
In this example <thing_secret>
represents secret of the thing and <cert_name>
represents the name of the certificate and key file which will be saved in docker/ssl/certs
directory. Generated Certificate will expire after 2 years. The key must be stored in the x.509 certificate CN
field. This script is created for testing purposes and is not meant to be used in production. We strongly recommend avoiding self-signed certificates and using a certificate management tool such as Vault for the production.
Once you have created CA and server-side cert, you can spin the composition using:
AUTH=x509 docker-compose -f docker/docker-compose.yml up -d
Then, you can create user and provision things and channels. Now, in order to send a message from the specific thing to the channel, you need to connect thing to the channel and generate corresponding client certificate using aforementioned commands. To publish a message to the channel, thing should send following request:
WSS#
const WebSocket = require("ws");
// Do not verify self-signed certificates if you are using one.
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
// Replace <channel_id> and <thing_secret> with real values.
const ws = new WebSocket(
"wss://localhost/ws/channels/<channel_id>/messages?authorization=<thing_secret>",
// This is ClientOptions object that contains client cert and client key in the form of string. You can easily load these strings from cert and key files.
{
cert: `-----BEGIN CERTIFICATE-----....`,
key: `-----BEGIN RSA PRIVATE KEY-----.....`,
}
);
ws.on("open", () => {
ws.send("something");
});
ws.on("message", (data) => {
console.log(data);
});
ws.on("error", (e) => {
console.log(e);
});
As you can see, Authorization
header does not have to be present in the HTTP request, since the secret is present in the certificate. However, if you pass Authorization
header, it must be the same as the key in the cert. In the case of MQTTS, password
filed in CONNECT message must match the key from the certificate. In the case of WSS, Authorization
header or authorization
query parameter must match cert key.
HTTPS#
curl -s -S -i --cacert docker/ssl/certs/ca.crt --cert docker/ssl/certs/<thing_cert_name>.crt --key docker/ssl/certs/<thing_cert_key>.key -X POST -H "Content-Type: application/senml+json" https://localhost/http/channels/<channel_id>/messages -d '[{"bn":"some-base-name:","bt":1.276020076001e+09, "bu":"A","bver":5, "n":"voltage","u":"V","v":120.1}, {"n":"current","t":-5,"v":1.2}, {"n":"current","t":-4,"v":1.3}]'
MQTTS#
Publish#
mosquitto_pub -u <thing_id> -P <thing_secret> -t channels/<channel_id>/messages -h localhost -p 8883 --cafile docker/ssl/certs/ca.crt --cert docker/ssl/certs/<thing_cert_name>.crt --key docker/ssl/certs/<thing_cert_key>.key -m '[{"bn":"some-base-name:","bt":1.276020076001e+09, "bu":"A","bver":5, "n":"voltage","u":"V","v":120.1}, {"n":"current","t":-5,"v":1.2}, {"n":"current","t":-4,"v":1.3}]'
Subscribe#
mosquitto_sub -u <thing_id> -P <thing_secret> --cafile docker/ssl/certs/ca.crt --cert docker/ssl/certs/<thing_cert_name>.crt --key docker/ssl/certs/<thing_cert_key>.key -t channels/<channel_id>/messages -h localhost -p 8883