titledescription
Authenticate
This page shows how to setup authentication for the InfluxDB line protocol TCP endpoint.

This page shows how to authenticate to QuestDB using different programming languages or tools over InfluxDB line protocol for the TCP endpoint.

Prerequisites

Make sure you have QuestDB running and accessible, you can do so from Docker, the binaries or Homebrew for macOS users.

InfluxDB line protocol

QuestDB implements the InfluxDB line protocol, this endpoint is accessible on port 9009.

  1. const { Socket } = require("net")
  2. const { Crypto } = require("node-webcrypto-ossl")
  3. const crypto = new Crypto()
  4. const PORT = 9009
  5. const HOST = "localhost"
  6. const PRIVATE_KEY = "5UjEMuA0Pj5pjK8a-fa24dyIf-Es5mYny3oE_Wmus48"
  7. const PUBLIC_KEY = {
  8. x: "fLKYEaoEb9lrn3nkwLDA-M_xnuFOdSt9y0Z7_vWSHLU",
  9. y: "Dt5tbS1dEDMSYfym3fgMv0B99szno-dFc1rYF9t0aac",
  10. }
  11. const JWK = {
  12. ...PUBLIC_KEY,
  13. kid: "testUser1",
  14. kty: "EC",
  15. d: PRIVATE_KEY,
  16. crv: "P-256",
  17. }
  18. const client = new Socket()
  19. async function write(data) {
  20. return new Promise((resolve) => {
  21. client.write(data, () => {
  22. resolve()
  23. })
  24. })
  25. }
  26. async function authenticate(challenge) {
  27. // Check for trailing \n which ends the challenge
  28. if (challenge.slice(-1).readInt8() === 10) {
  29. const apiKey = await crypto.subtle.importKey(
  30. "jwk",
  31. JWK,
  32. { name: "ECDSA", namedCurve: "P-256" },
  33. true,
  34. ["sign"],
  35. )
  36. const signature = await crypto.subtle.sign(
  37. { name: "ECDSA", hash: "SHA-256" },
  38. apiKey,
  39. challenge.slice(0, challenge.length - 1),
  40. )
  41. await write(`${Buffer.from(signature).toString("base64")}\n`)
  42. return true
  43. }
  44. return false
  45. }
  46. async function sendData() {
  47. const rows = [
  48. `test,location=us temperature=22.4 ${Date.now() * 1e6}`,
  49. `test,location=us temperature=21.4 ${Date.now() * 1e6}`,
  50. ]
  51. for (row of rows) {
  52. await write(`${row}\n`)
  53. }
  54. }
  55. async function run() {
  56. let authenticated = false
  57. let data
  58. client.on("data", async function (raw) {
  59. data = !data ? raw : Buffer.concat([data, raw])
  60. if (!authenticated) {
  61. authenticated = await authenticate(data)
  62. await sendData()
  63. setTimeout(() => {
  64. client.destroy()
  65. }, 0)
  66. }
  67. })
  68. client.on("ready", async function () {
  69. await write(`${JWK.kid}\n`)
  70. })
  71. client.connect(PORT, HOST)
  72. }
  73. run()
  1. package main
  2. import (
  3. "bufio"
  4. "crypto"
  5. "crypto/ecdsa"
  6. "crypto/elliptic"
  7. "crypto/rand"
  8. _ "crypto/sha256"
  9. "encoding/base64"
  10. "fmt"
  11. "math/big"
  12. "net"
  13. "time"
  14. )
  15. func main() {
  16. host := "127.0.0.1:9009"
  17. tcpAddr, err := net.ResolveTCPAddr("tcp4", host)
  18. checkErr(err)
  19. rows := [2]string{
  20. fmt.Sprintf("test,location=uk temperature=12.4 %d", time.Now().UnixNano()),
  21. fmt.Sprintf("test,location=uk temperature=11.4 %d", time.Now().UnixNano()),
  22. }
  23. keyId := "testUser1"
  24. // Parse and create private key
  25. keyRaw, err := base64.RawURLEncoding.DecodeString("5UjEMuA0Pj5pjK8a-fa24dyIf-Es5mYny3oE_Wmus48")
  26. checkErr(err)
  27. key := new(ecdsa.PrivateKey)
  28. key.PublicKey.Curve = elliptic.P256()
  29. key.PublicKey.X, key.PublicKey.Y = key.PublicKey.Curve.ScalarBaseMult(keyRaw)
  30. key.D = new(big.Int).SetBytes(keyRaw)
  31. // Create connection and send key ID
  32. conn, err := net.DialTCP("tcp", nil, tcpAddr)
  33. checkErr(err)
  34. defer conn.Close()
  35. reader := bufio.NewReader(conn)
  36. _, err = conn.Write([]byte(keyId + "\n"))
  37. checkErr(err)
  38. raw, err := reader.ReadBytes('\n')
  39. // Remove the `\n` is last position
  40. raw = raw[:len(raw)-1]
  41. checkErr(err)
  42. // Hash the challenge with sha256
  43. hash := crypto.SHA256.New()
  44. hash.Write(raw)
  45. hashed := hash.Sum(nil)
  46. a, b, err := ecdsa.Sign(rand.Reader, key, hashed)
  47. checkErr(err)
  48. stdSig := append(a.Bytes(), b.Bytes()...)
  49. _, err = conn.Write([]byte(base64.StdEncoding.EncodeToString(stdSig) + "\n"))
  50. checkErr(err)
  51. // We are now authenticated, we can sed data
  52. for _, s := range rows {
  53. _, err := conn.Write([]byte(fmt.Sprintf("%s\n", s)))
  54. checkErr(err)
  55. }
  56. }
  57. func checkErr(err error) {
  58. if err != nil {
  59. panic(err)
  60. }
  61. }