Skip to content

[Docs] Document expected wire format for authentication_jwt auth switch response #71564

@nkulkarni03

Description

@nkulkarni03

Enhancement

Summary

The authentication_jwt plugin requires the auth switch response in a specific wire format ([1-byte capability] [MySQL length-encoded JWT string]), but this format is not documented. The JWT Authentication docs only cover MySQL 9.2 CLI with --authentication-openid-connect-client-id-token-file. Programmatic clients (Node.js, Python, Go, custom JDBC plugins) have no way to know the expected format without reading the FE source code.

The Problem

When connecting programmatically with a authentication_jwt user, the client handles the authentication_openid_connect_client auth switch. Sending raw JWT bytes as the response produces a misleading error:

Invalid serialized unsecured/JWS/JWE object: Missing second delimiter

The JWT is well-formed (3 parts, 2 dots, valid base64). The error occurs because JWTAuthenticationProvider.authenticate() expects a specific format:

// JWTAuthenticationProvider.java lines 50-53
ByteBuffer authBuffer = ByteBuffer.wrap(authResponse);
MysqlCodec.readInt1(authBuffer);                          // 1 byte: capability flag
byte[] idToken = MysqlCodec.readLenEncodedString(authBuffer);  // MySQL length-encoded string

If raw JWT bytes are sent (e.g., eyJ0eXAiOi...), the first byte e (0x65 = 101) is interpreted as the string length. Only 101 bytes of the ~1300-byte token are read, truncating it past the first dot — hence "Missing second delimiter."

Expected Auth Switch Response Format

[1 byte]              Capability flag (0x00)
[length-encoded str]  JWT token in MySQL length-encoded string format:
                        - Token < 251 bytes:   [1-byte length] [token bytes]
                        - Token < 65536 bytes: [0xFC] [2-byte LE length] [token bytes]
                        - Token < 16MB:        [0xFD] [3-byte LE length] [token bytes]

Working Example (Node.js mysql2)

const mysql = require('mysql2/promise');

function encodeLenEncString(str) {
  const buf = Buffer.from(str, 'utf8');
  if (buf.length < 251) return Buffer.concat([Buffer.from([buf.length]), buf]);
  const header = Buffer.alloc(3);
  header[0] = 0xfc;
  header.writeUInt16LE(buf.length, 1);
  return Buffer.concat([header, buf]);
}

const conn = await mysql.createConnection({
  host: 'fe-host',
  port: 9030,
  user: 'my_jwt_user',
  authPlugins: {
    authentication_openid_connect_client: () => (data) => {
      return Buffer.concat([
        Buffer.from([0x00]),          // capability byte
        encodeLenEncString(jwtToken)   // length-encoded JWT
      ]);
    },
  },
});

const [rows] = await conn.query('SELECT CURRENT_USER()');
// Returns: 'my_jwt_user'@'%'

Steps to Reproduce (raw JWT — fails)

  1. Create a JWT user:
CREATE USER testjwt IDENTIFIED WITH authentication_jwt AS '{
  "jwks_url": "https://login.microsoftonline.com/<tenant>/discovery/v2.0/keys",
  "principal_field": "preferred_username",
  "required_issuer": "https://login.microsoftonline.com/<tenant>/v2.0",
  "required_audience": "<client_id>"
}';
  1. Connect with raw JWT bytes as auth switch response:
authPlugins: {
  authentication_openid_connect_client: () => (data) => Buffer.from(jwt, 'utf8')  // RAW — fails
}
  1. Error: Invalid serialized unsecured/JWS/JWE object: Missing second delimiter

Steps to Reproduce (length-encoded — works)

  1. Connect with properly formatted response (capability byte + length-encoded JWT):
authPlugins: {
  authentication_openid_connect_client: () => (data) => {
    return Buffer.concat([Buffer.from([0x00]), encodeLenEncString(jwt)]);  // WORKS
  }
}
  1. SELECT CURRENT_USER() returns the JWT user.

Suggestion

Add a section to the JWT Authentication page titled "Connecting from programmatic clients" documenting:

  1. StarRocks sends an auth switch to plugin authentication_openid_connect_client
  2. Client must respond with: [1-byte capability flag] [MySQL length-encoded JWT string]
  3. Code examples for Node.js (mysql2) and Python (pymysql)

Alternatively, consider improving the error message — instead of the Nimbus JOSE "Missing second delimiter" error, StarRocks could detect that the first byte looks like an ASCII character (not a valid length prefix) and return: "Auth response format error: expected [capability byte][length-encoded JWT], received raw bytes. See <docs-link>."

Environment

  • StarRocks version: 3.5.15 (select current_version()3.5.15-5abb1cb)
  • Also verified: Code unchanged on main branch as of 2026-04-12
  • Client: Node.js mysql2 v3.22.0
  • IdP: Azure AD (Entra ID), JWT size ~1292 bytes
  • Use case: Healthcare analytics platform integrating Azure AD SSO with StarRocks JWT authentication

Impact

Any developer integrating StarRocks authentication_jwt with a programmatic client (web app backends, custom JDBC drivers, BI tool plugins, data pipeline connectors) will hit this undocumented format requirement. The error message provides no clue about the expected format. We spent 2 days debugging this before reading the FE source code.

Happy to contribute a docs PR if that would be helpful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    type/enhancementMake an enhancement to StarRocks

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions