Skip to content

Machine-to-machine authentication

Boundary

The public review API uses a deployment-owned service token to authenticate the calling Drupal application. This credential is only for the client-to- server transition. It is not an LLM provider credential and is not reused for the server-to-APISIX transition.

The canonical route accepts only the ai_code_review_server_service_token Drupal authentication provider. Cookie sessions, HTTP Basic authentication, provider API keys, and APISIX credentials do not authenticate this route.

Token format

Clients send an opaque bearer credential with this format:

Authorization: Bearer <client-id>.<secret>
  • client-id is a lowercase deployment identifier containing 3 to 64 ASCII letters, digits, underscores, or hyphens. It starts with a letter, avoiding PHP's integer coercion for numeric settings.php array keys.
  • secret contains 32 to 512 base64url characters.
  • The complete header is sensitive. Never log, persist, trace, or include it in an error response.

The server uses the client ID only to select one password hash. The secret is verified by Drupal's password service. Unknown client IDs are checked against a deployment-owned dummy hash so they do not take a shortcut around password verification. Every configured client hash and the dummy hash must use the same password algorithm and work-factor options. Configuration validation rejects a mixed hash profile because it would make client existence measurable by timing. Recognized metadata is not sufficient: each complete bcrypt or Argon encoding is also validated before it can become a timing-equalization path.

Drupal account

Create a dedicated, active Drupal user for each calling application. Assign a role containing only the access ai code review server api permission and any later permissions that the client actually requires. Do not use user 1 or an administrative account.

The token definition maps its client ID to that Drupal account name. Successful authentication therefore uses Drupal's normal account and permission system. Missing, malformed, expired, or unresolvable credentials return the same 401 response. A valid service identity without the route permission returns 403.

The authenticated client ID is available to later server services in the request attribute _ai_code_review_server_client_id. It must be used to bind jobs, idempotency records and quotas to their caller. Best-effort operation logging may include this validated client ID; logging availability does not control authentication or authorization.

Secret provisioning

Generate separate random values for the client secret and dummy value. Hash both with the PHP password implementation used by the Drupal deployment. Keep the clear-text client secret only in the client deployment secret store. Use the same algorithm and options for every generated hash in one deployment.

For example, this local command prints one generated value and its hash without placing the generated value in the shell command line:

php -r '$value=rtrim(strtr(base64_encode(random_bytes(48)), "+/", "-_"), "="); printf("Value: %s\nHash: %s\n", $value, password_hash($value, PASSWORD_DEFAULT));'

Add only hashes and account names to the server's settings.php:

$settings['ai_code_review_server.authentication'] = [
  'dummy_secret_hash' => '<password hash for a separate random dummy value>',
  'clients' => [
    'drupal-client' => [
      'account_name' => 'ai_code_review_ui_client',
      'secret_hash' => '<password hash for the client secret>',
    ],
  ],
];

This setting is deployment secret material even though it contains hashes. Do not commit it, export it through Drupal configuration, bake it into an image, or print it in CI. An empty or absent setting fails closed and authenticates no client. Malformed settings fail validation rather than silently weakening the boundary.

The client combines its configured ID and clear-text secret at request time:

Bearer drupal-client.<clear-text-client-secret>

The UI client and Server are designed for separate Drupal sites. The UI sends its service token to the Server; it does not send an LLM provider or APISIX credential.

Rotation and revocation

  1. Generate a new client secret and hash.
  2. Update the server setting and the client secret store during a controlled overlap or maintenance window.
  3. Verify one synthetic authenticated request without logging its header.
  4. Remove the old hash immediately after traffic moves.
  5. Block the mapped Drupal account or remove its client entry for emergency revocation.

Use a new client ID when an overlap requires two simultaneously valid secrets. Never place multiple secrets in one client definition.

Changing the password algorithm or work factor requires replacing the dummy hash and every client hash in one maintenance change. Mixed profiles fail closed so unknown and known client IDs cannot take measurably different hash paths.