Skip to content

[Detail Bug] CLI: Certificate refresh clears AssetID, dropping XFCC identity for asset-auth sessions #1126

Description

@detail-app

Detail Bug Report

https://app.detail.dev/org_bfc5015c-a884-452c-af11-b223af3a12fa/bugs/bug_4570642a-781f-487a-8a54-8e59db392206

Introduced in #962 by @martien-wdy on Jun 10, 2026

Summary

  • Context: The refreshCertsForAuth function re-issues mTLS certificates when they expire or need renewal, preserving identity fields from the server response.
  • Bug: When creating a new CertificateInfo after a successful refresh, the function fails to set AssetID from the server response. It copies OrganizationID and UserID from the existing cert, but omits AssetID entirely.
  • Actual vs. expected: After refresh, asset-authenticated sessions have AssetID=0 instead of the original asset ID, causing the XFCC identity header to be omitted from all subsequent cloud RPC calls.
  • Impact: Asset-authenticated CLI sessions (those using --api-key for local pki-core) lose their identity on the server after any certificate refresh, breaking cloud tunneling, device enrollment, and all cloud operations that depend on the XFCC header.

Code with Bug

// In refreshCertsForAuth, auth.go lines 575-583:
auth.Certificates = []config.CertificateInfo{
    {
        PemCertificate:      cert.GetPemCertificate(),
        PemCertificateChain: cert.GetPemCertificateChain(),
        PemPrivateKey:       newKeyPEM,
        OrganizationID:      existingCert.OrganizationID,
        UserID:              existingCert.UserID,
        // BUG: AssetID is not set at all - neither from response nor existing cert // <-- BUG 🔴 drops asset identity after refresh
    },
}

Explanation

certXFCC only emits an identity header when UserID != "" or AssetID != 0. After a refresh, the refreshed certificate is saved with AssetID=0, so certXFCC returns an empty string and cloudContext omits the XFCC headers. The refresh RPC itself succeeds because it runs using the pre-refresh cert (which still has AssetID); the breakage occurs on subsequent cloud RPC calls after the updated config is written.

Codebase Inconsistency

Initial asset login correctly reads AssetID from the server response, but refresh does not preserve/populate it:

certInfo := config.CertificateInfo{
    PemCertificate:      cert.GetPemCertificate(),
    PemCertificateChain: cert.GetPemCertificateChain(),
    PemPrivateKey:       privateKeyPEM,
    OrganizationID:      int(issueResp.GetOrganizationId()),
    AssetID:             int(issueResp.GetAssetId()),  // <-- expected identity handling
}

Recommended Fix

Populate identity fields from the RefreshCertificate response Certificate (preferred, consistent with login):

cert := refreshResp.GetCertificate()
// ...
auth.Certificates = []config.CertificateInfo{
    {
        PemCertificate:      cert.GetPemCertificate(),
        PemCertificateChain: cert.GetPemCertificateChain(),
        PemPrivateKey:       newKeyPEM,
        OrganizationID:      int(cert.GetOrganizationId()),
        UserID:              cert.GetUserId(),
        AssetID:             int(cert.GetAssetId()),  // <-- FIX
    },
}

History

This bug was introduced in commit 45aa4e9. The commit added AssetID to config.CertificateInfo and correctly populated it in performLocalLogin (for --api-key authentication), but failed to update refreshCertsForAuth to preserve or populate the AssetID field during certificate refresh.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions