Device Identity

Certificate-based device enrollment and authentication

Back to Docs

What is Device Identity?

Device Identity is Orion's certificate-based authentication primitive for machines, agents, and IoT devices. Instead of a static API key, a device holds an Orion-issued X.509 certificate that proves its identity on every request via a signed assertion.

Each organisation has its own Certificate Authority (CA) — generated lazily by Orion and encrypted at rest. When an admin registers a device, Orion issues a one-time enrollment code. The device generates a key pair locally, submits a Certificate Signing Request (CSR) with the code, and receives a signed leaf certificate. The private key never leaves the device.

Use device identity when:

  • You need stronger auth than shared secrets (no API keys to rotate or leak)
  • You want hardware-bound keys via TPM attestation (non-exportable, tamper-resistant)
  • Sovereign signing agents need a cert-based identity for the agent→Orion channel
  • You're enrolling servers, edge devices, or CI/CD runners that need machine-to-machine auth

Admin UI available.

Manage devices at /devices/manage/{your-org} — register devices, view enrollment status, revoke certificates. The steps below show the API/SDK equivalent.

Security: Two separate PKIs.

The device CA (Orion-held) issues certs for device→Orion transport authentication only. The sovereign signing trust root (external/HSM) is validated SDK-side for token signing. A compromised Orion can forge device-auth certs but cannot forge sovereign-signed tokens. These two PKIs share no key, CA, or trust store.


1 Register a Device (Admin)

An admin generates a one-time enrollment code for the device. The code is short-lived (default 15 minutes) and shown only once.

UI: go to /devices/manage/your-org, fill in the device name, and click "Generate Enrollment Code".

The admin can optionally:

  • Require TPM attestation for this specific enrollment (checkbox)
  • Constrain the device's certificate subject CN
  • Pre-bind the enrollment to a sovereign signing agent

2 Enroll the Device

One line — macOS / Linux (curl + openssl)

The server hosts a per-org enrollment script with the base URL and org baked in:

curl -sSL https://login.shanecraven.com/oauth/your-org/devices/enroll.sh | sh -s -- YOUR-CODE

One line — Windows (PowerShell 7+)

Native PowerShell variant using .NET crypto (no openssl needed). Requires PowerShell 7 (pwsh) — Windows PowerShell 5.1 lacks the needed APIs:

& ([scriptblock]::Create((irm https://login.shanecraven.com/oauth/your-org/devices/enroll.ps1))) YOUR-CODE

Both generate a key pair + CSR locally (the private key never leaves the device), enroll, and write device-cert.pem, device-key.pem, ca-cert.pem to ~/.orion/devices/{server}/{org}/ (%USERPROFILE%\.orion\devices\… on Windows).

Standard enrollment (SDK)

using OrionDotNetCore.Device; var client = new DeviceEnrollmentClient("https://login.shanecraven.com", "your-org"); // Generate key pair + CSR locally (private key stays on this machine) var (csrPem, privateKey) = client.GenerateKeyPairAndCsr("my-agent-01"); // Redeem the enrollment code var result = await client.EnrollAsync(code: "BCDF-GHJK", csrPem: csrPem, deviceName: "my-agent-01"); // Save the certificate + private key File.WriteAllText("device-cert.pem", result.LeafCertPem); File.WriteAllText("device-key.pem", privateKey.ExportRSAPrivateKeyPem()); File.WriteAllText("ca-cert.pem", result.CaCertPem); Console.WriteLine($"Enrolled as device {result.DeviceId}");

TPM-attested enrollment (hardware-bound key)

When the admin or org policy requires TPM attestation, the enrollment uses a two-step challenge-response flow that proves the key is non-exportable and lives in a genuine TPM.

using OrionDotNetCore.Device; using OrionDotNetCore.Device.Tpm; var client = new DeviceEnrollmentClient("https://login.shanecraven.com", "your-org"); // Use the platform TPM provider (Linux example) using var tpm = new Tpm2ToolsKeyProvider(); // The SDK handles the full begin → activate-credential → certify → complete flow var result = await client.EnrollWithTpmAsync( code: "BCDF-GHJK", tpm: tpm, subjectCn: "my-agent-01"); // The private key is in the TPM (non-exportable); save the cert for auth File.WriteAllText("device-cert.pem", result.LeafCertPem); File.WriteAllText("ca-cert.pem", result.CaCertPem);

3 Authenticate as a Device

The device authenticates by sending a short-lived signed assertion JWT with its certificate chain in the x5c header.

using OrionDotNetCore.Device; var auth = new DeviceAuthenticator( serverBaseUrl: "https://login.shanecraven.com", orgPublicId: "your-org", deviceId: result.DeviceId, leafCertPem: File.ReadAllText("device-cert.pem"), privateKeyPem: File.ReadAllText("device-key.pem"), caCertPem: File.ReadAllText("ca-cert.pem")); // Use in any HTTP request httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("DeviceAssertion", auth.CreateAssertion());

For sovereign signing agents, the SovereignAgentClient handles this automatically when you use the device-auth constructor — see the Sovereign Signing Guide.

The high-level SDK client

Easiest path — OrionDeviceClient auto-detects the local registration and handles token caching:

using OrionDotNetCore.Device; var dev = new OrionDeviceClient("https://login.shanecraven.com", "your-org"); if (!dev.IsEnrolled) await dev.EnrollAsync(code); // one-time string token = await dev.GetAccessTokenAsync(); // cached, auto-refreshed

Credentials are stored per server+org under ~/.orion/devices/{server}/{org}/ (override with ORION_DEVICE_DIR), so one device can be enrolled with multiple Orion servers.

Manage Local Registrations

List, get tokens for, or remove the device registrations stored on a machine — across all servers it's enrolled with:

# list every registration on this machine curl -sSL https://login.shanecraven.com/devices.sh | sh -s -- list # get a token for a specific registration curl -sSL https://login.shanecraven.com/devices.sh | sh -s -- token login.craven.dev your-org # remove a local registration curl -sSL https://login.shanecraven.com/devices.sh | sh -s -- remove login.craven.dev your-org

Windows (PowerShell 7+):

# list / remove & ([scriptblock]::Create((irm https://login.shanecraven.com/devices.ps1))) list & ([scriptblock]::Create((irm https://login.shanecraven.com/devices.ps1))) remove login.craven.dev your-org

TPM Attestation Policy

TPM attestation can be required at three levels with inheritance. The most specific level wins:

Level Setting Scope
Organisation RequireTpmAttestation Default for all devices in the org
Sovereign Signing Pool RequireTpmAttestation Override for agents in this pool
Enrollment Code RequireTpmAttestation Override for this specific device

Resolution: enrollment.RequireTpm ?? pool.RequireTpm ?? org.RequireTpm ?? false.

When TPM is required, the single-shot /devices/enroll endpoint returns tpm_required. The device must use the two-step /devices/enroll/begin + /devices/enroll/complete flow with attestation evidence.

How TPM Attestation Works

The two-step flow proves three things: the device key lives in a real TPM, it's non-exportable, and the TPM is genuine (vendor-rooted).

  • begin: The device sends its EK certificate + attestation key (AK) public area. Orion validates the EK chains to a trusted TPM vendor root (Infineon/STM/Intel/AMD/Microsoft), then issues a MakeCredential challenge — a nonce encrypted to the EK, bound to the AK.
  • ActivateCredential: Only the TPM holding both the EK and AK can recover the nonce. This proves the AK is co-resident with the genuine EK.
  • complete: The device creates a non-exportable signing key (fixedTPM|fixedParent) in the TPM, has the AK certify it (proving the key's attributes), generates a CSR from it, and sends everything back. Orion verifies the certify signature, that the key is hardware-bound, and that the CSR public key matches the attested key — then issues the certificate.

Revocation

Revoke a device from the admin UI or API. Revocation is instant — the device's certificate thumbprint is checked on every authentication request against the database. No CRL/OCSP infrastructure is needed because Orion is the sole relying party. Revoking a device also deactivates any linked sovereign signing agent.

Configuration

VariableDefaultDescription
ORION_CA_KEKRequired256-bit AES key (base64) for encrypting the CA private key. Must be different from ORION_SIGNING_KEK. Generate: openssl rand -base64 32
ORION_CA_VALIDITY_YEARS10How long the per-org device CA certificate is valid
ORION_DEVICE_CERT_VALIDITY_DAYS365How long an issued device certificate is valid
ORION_DEVICE_ENROLLMENT_TTL_MINUTES15How long an enrollment code is valid
ORION_DEVICE_ASSERTION_MAX_AGE_SECONDS120Max age of a device assertion JWT before it's rejected
ORION_TPM_VENDOR_ROOTSPath to a directory of TPM vendor root certificates (PEM/CRT). Required when TPM attestation is enabled.
ORION_TPM_REQUIRE_EK_CERTtrueRequire the TPM to provide an EK certificate that chains to a vendor root. Set false only for testing.

Troubleshooting

SymptomCause & Fix
invalid_code at enrollmentCode expired (default 15 min), already used, or wrong org. Generate a new one.
tpm_required returned from /enrollTPM policy is active. Use the begin/complete flow with EnrollWithTpmAsync.
ek_untrusted at beginEK certificate doesn't chain to any root in ORION_TPM_VENDOR_ROOTS. Add your TPM vendor's root cert to the directory.
activation_failed nonce mismatchThe TPM couldn't recover the nonce. Ensure the AK and EK are co-resident in the same TPM. Session may have expired.
attestation_failed: not hardware-boundThe device key was created without fixedTPM|fixedParent. Recreate it with the correct attributes.
Assertion replay detectedSame jti used twice. The SDK generates a new jti per assertion; ensure you're calling CreateAssertion() fresh, not caching.
Device is not active (401)Device was revoked by an admin. Re-enroll with a new code.

API Endpoints

  • POST /oauth/{org}/devices/enroll — Standard enrollment (code + CSR → cert)
  • POST /oauth/{org}/devices/enroll/begin — TPM enrollment step 1 (code + EK cert + AK pub → credential blob)
  • POST /oauth/{org}/devices/enroll/complete — TPM enrollment step 2 (session + CSR + attestation → cert)
  • GET /oauth/{org}/devices/ca.pem — Org device CA certificate (public)
  • GET /devices/manage/{org} — Admin: list devices + register form
  • POST /devices/manage/{org}/register — Admin: generate enrollment code
  • POST /devices/manage/{org}/{id}/revoke — Admin: revoke a device
← Sovereign Signing Security Features →