Skip to main content

JWT Authentication on nRF Cloud

This guide provides a walkthrough of how JSON Web Tokens (JWTs) can be used to authenticate requests to the nRF Cloud REST API. It builds upon the JWT section of the REST API Authentication documentation.

This guide does not model real-world use cases that involve hardware devices and the use of the nRF9160 modem firmware for creating JWTs; see the Getting Started with Your Devices guide for that information. Instead, this guide intends to provide a conceptual understanding of JWT authentication on nRF Cloud using tokens that you generate via an online tool.

The goals of this guide are two-fold:

  1. Improve your understanding about JWTs in general by providing steps to create tokens usable on nRF Cloud.
  2. Reinforce the concepts in the section "Signing (Private) and Verification (Public) Keys" by walking you through how the process works.

Prerequisites#

If you want to execute these steps you will need the following:

Key Extraction from Device Certificates#

For JWTs involving a device that is provisioned on nRF Cloud, the public key is extracted from the certificate and stored on nRF Cloud so that it can be used for verifying signatures of JWTs signed by the certificate's private key.

Let's walk through that process:

  1. Use the scripts in the nRF Cloud Utils project to create a CA certificate and then a device certificate. These steps use the NodeJS scripts, but we also provide Python scripts. If you do not want to use the scripts directly, you can use the OpenSSL commands therein, with a little modification.
export DEVICE_ID=<your device's id (see above)>
# You may use a subject and organizational unit (ou) of your own choosing.node dist/create-ca-cert.js --cnSubjectPrefix '/C=NO/ST=Norway/L=Trondheim/O=Nordic Semiconductor' --ou 'Test Devices'
# Now create the device certificate using the CA certificate.node dist/create-device-cert.js --deviceId $DEVICE_ID 
info

If you were doing these steps with a real hardware device, you would need to flash the certificate to the device as explained here. Again, some of the steps in this guide are not what you would do for real-world API usage. These steps are mainly for educational purposes.

  1. Following the documentation for the ProvisionDevices endpoint, create a CSV file that contains the certificate that you just created. This is the PEM-formatted string in the *.crt.pem file. Then call the ProvisionDevices endpoint to upload the CSV data. The example below shows CSV data encoded as a base64 string, which is one of the ways to send the CSV data.
export API_KEY=<your API key>curl --request POST \  --url https://api.nrfcloud.com/v1/devices \  --header 'Authorization: Bearer $API_KEY' \  --header 'content-type: text/csv' \  --data ZjY5YzBlNDUtN2YwNC00OTQ5LThkZWYtYmIyMjE1YjQyMjNlLG15LXRoaW5nLXR5cGUsdGFnMXx0YWcyLEFQUHxNT0RFTSwiLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUI0VENDQVlnQ0NRQ2EyWkxudGk5dFpEQUtCZ2dxaGtqT1BRUURBakJvTVFzd0NRWURWUVFHRXdKT1R6RVAKTUEwR0ExVUVDQXdHVG05eWQyRjVNUkl3RUFZRFZRUUhEQWxVY205dVpHaGxhVzB4SFRBYkJnTlZCQW9NRkU1dgpjbVJwWXlCVFpXMXBZMjl1WkhWamRHOXlNUlV3RXdZRFZRUUxEQXhVWlhOMElFUmxkbWxqWlhNd0lCY05NakV3Ck9ERTVNakExTWpRM1doZ1BNakExTVRBNE1USXlNRFV5TkRkYU1JR0hNUXN3Q1FZRFZRUUdFd0pPVHpFU01CQUcKQTFVRUNBd0pWSEp2Ym1SbGJHRm5NUkl3RUFZRFZRUUhEQWxVY205dVpHaGxhVzB4SVRBZkJnTlZCQW9NR0U1dgpjbVJwWXlCVFpXMXBZMjl1WkhWamRHOXlJRUZUUVRFdE1Dc0dBMVVFQXd3a1l6WTFNbVk1WVdRdFpUQmlPUzAwCk9HWm1MV0pqTmpJdE1qSXdPREZqT0RBME1UVTRNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUUKQnM5SC9BRG1tNTZuZS90OW9FT1BhRWtHVzNOczllaGtpVUVwbXgxbjdpQWwyRVRKYm5hcGZ6NklWWHVwa3ZtSApDQlBKRU1UWVR3Mnc2bisrMGV1Zkd6QUtCZ2dxaGtqT1BRUURBZ05IQURCRUFpQnpBc3ZyNzZpK0x1TFR2QndGCm5vTy9LQUh6bmJhTE1DLzlwU25mSi9HWitRSWdOZStWcXd0aUNuMzlIRnUyb3NDaElzRlVSTWtvNGl0VWIwOFcKUWFtZHM5QT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQoi
  1. This will return a bulkOpsRequestId, which allows you to check the status of the bulk operations request via the FetchBulkOpsRequest endpoint. In this case, the process will go very quickly, and you should now be able to retrieve information about your device:
curl --request GET \  --url https://api.nrfcloud.com/v1/devices/$DEVICE_ID \  --header 'Authorization: Bearer $API_KEY'
  1. You are now ready to create a JWT using the private key of the device certificate. To do this without having to write code, go to jwt.io.
info

In a real-world scenario JWT creation would be handled by the nRF9160 modem via the AT%JWT command.

  1. Their cryptographic algorithm for JWT signing defaults to HS256. nRF Cloud only supports ES256, therefore select that from the Algorithm menu:

jwt.io algo selection

  1. Edit the payload to conform to what is required for the "Device Key" use case, as specified here:
{    "sub": "f69c0e45-7f04-4949-8def-bb2215b4223e"}
info

You could also use iss (which is what the modem library will use if sub is not passed to the AT%JWT command), but the device id format requires prepending a hardware type such as nrf9160., e.g.:

{    "iss": "nrf9160.f69c0e45-7f04-4949-8def-bb2215b4223e"}
  1. Into the lower right field of the Verify Signature pane, paste in the contents of your *.key.pem, which contains the private key in PEM format:

jwt.io set private key

When you do this the text in the left pane will change. This is the actual JWT, encoded as a base64 string. You can now present this token when calling an nRF Cloud REST API endpoint that accepts JWTs, allowing the API to authenticate the request.

Also notice that "Invalid Signature" is displayed in red. You can disregard this: the warning appears because the public key in the right pane is the default key for the jwt.io sample. It is not the public key corresponding to your private key, and therefore signature verification failed.

It is, however, instructive to understand how the public key is used for JWTs. Let's remove the warning by pasting the correct public key into field above the private key. When you created the device certificate, however, a file containing the public key was not created. Instead, you can extract the public key from the certificate, which is exactly what nRF Cloud does when you send device certificates to the ProvisionDevices endpoint.

This example uses the files generated for the device id in the above steps:

openssl x509 -pubkey -noout -in ./certs/$DEVICE_ID.crt.pem > ./certs/$DEVICE_ID.public-key.pem

This results in:

-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBs9H/ADmm56ne/t9oEOPaEkGW3Ns9ehkiUEpmx1n7iAl2ETJbnapfz6IVXupkvmHCBPJEMTYTw2w6n++0eufGw==-----END PUBLIC KEY-----
info

The blank line below the text is part of the PEM format and should be retained.

Pasting this info jwt.io verifies the token's signature:

jwt.io verify signature

  1. You are now ready to use the JWT you have created to authenticate against an nRF Cloud endpoint that requires JWTs. This example shows an invocation of the GetAssistanceData endpoint using the JWT you see above in last jwt.io screenshot:
curl --request GET \  --url 'https://api.nrfcloud.com/v1/location/agps?requestType=custom&customTypes=1%2C3%2C4%2C6%2C7%2C8%2C9&mcc=310&mnc=410&tac=36874&eci=84485647' \  --header 'Accept: application/octet-stream' \  --header 'Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmNjljMGU0NS03ZjA0LTQ5NDktOGRlZi1iYjIyMTViNDIyM2UifQ.VRXRvIAxrdNml4zzUHboaE3oSFbjtUwAEa8QN2DuWrTsIUVVlmZFapY93w-ocwS5SpEdXFP8twMB3T8xTunGsQ' \  --header 'range: bytes=0-500'

The expected response is in binary format with an HTTP 206 status code. This indicates that your request was authenticated. An HTTP status code of 401 would indicate an authorization failure.

Public Key Registration#

The foregoing example signed JWTs using the private key associated with the device certificate, and verified the JWT signatures with the corresponding public key extracted from the certificate during the nRF Cloud provisioning process.

However, what if you do not want to provision devices on nRF Cloud, but still allow your devices to call our JWT-authenticated endpoints? For example, your devices may be provisioned on another IoT platform, yet you also want to take advantage of nRF Cloud's Location Services.

For this scenario you can register with nRF Cloud the public key of each your devices' asymmetric key pairs. This is a simpler process, because device certificates used for cloud provisioning are not involved.

Let's walk through that process:

  1. Create a key pair using OpenSSL:
openssl ecparam -out private-key.pem -name prime256v1 -genkeyopenssl ec -in private-key.pem -outform PEM -pubout -out public-key.pem

You should now have two files containing the private and public keys of an asymmetric key pair in PEM format.

 # private-key.pem -----BEGIN EC PARAMETERS-----BggqhkjOPQMBBw==-----END EC PARAMETERS----------BEGIN EC PRIVATE KEY-----MHcCAQEEIJjdPMD9lReeM+ixAkch0p7OQcDgqmdy8ZM1Ck4g/tX1oAoGCCqGSM49AwEHoUQDQgAEmw+7td4SD8LAvRs3cDZu8clz0zJpuvhTmYSBRL6YP72nAIw82DCpbBp8VFheVg2OeDO172Hvv+H8OAf9xHMoOg==-----END EC PRIVATE KEY-----
# public-key.pem-----BEGIN PUBLIC KEY-----MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGjAfgCvLVs5HUZoK/O2TN3ZjgyxuajMH+V3QNvAsyXFIM+CjfdjenQWcuww5CRX/kDwFEdILyE+jdScVbulkhw==-----END PUBLIC KEY-----
info

In a real-world scenario key generation would be handled by the nRF9160 modem via the AT%KEYGEN command.

The blank line below the text in each file is part of the PEM format and should be retained.

  1. Following the documentation for the RegisterPublicKeys endpoint, create a CSV file that contains the public key that you just created. Then call the RegisterPublicKeys endpoint to upload the data. For example:
curl --request POST \  --url https://api.nrfcloud.com/v1/devices/public-keys \  --header 'Authorization: Bearer $API_KEY' \  --header 'content-type: text/csv' \  --data ZjY5YzBlNDUtN2YwNC00OTQ5LThkZWYtYmIyMjE1YjQyMjNlLC0tLS0tQkVHSU4gUFVCTElDIEtFWS0tLS0tCk1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRUdqQWZnQ3ZMVnM1SFVab0svTzJUTjNaamd5eHUKYWpNSCtWM1FOdkFzeVhGSU0rQ2pmZGplblFXY3V3dzVDUlgva0R3RkVkSUx5RStqZFNjVmJ1bGtodz09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=

See the previous example, above, regarding use of the bulkOpsRequestId.

  1. Repeat the steps above for jwt.io, using the private and public keys from this example to create a JWT. Then try using it to authenticate a request to any nRF Cloud endpoint that specifies "JSON Web Token" in its "Authorizations" section.

Next Steps#

This guide served to increase your understanding of how JWTs work on nRF Cloud. For real-world use of JWTs from actual devices, consult the Getting Started with Your Devices guide.