8 KiB
Enable Okta Verify on Windows using a SCEP configuration profile
Introduction
This guide explains how to enable Okta Verify on Windows using a SCEP client certificate delivered by the Windows ClientCertificateInstall CSP and then applied using an Exec command. This pattern is useful when your MDM payload cannot send Add or Replace nodes together with an Exec in one transaction.
You will deploy the SCEP profile first, then call Enroll via Exec to request the client certificate.
Files
Order at a glance
- Get your CA thumbprint, choose {yourCertName}, and locate your SCEP URL and Challenge.
- Create Fleet secrets for URL, Challenge, CA thumbprint, and API token.
- Use the Fleet repo XML CSP profile and replace only the required placeholders.
- Deploy the profile to devices.
- Update the Exec script to use the same
{yourCertName}and your secrets, then run it. - Verify the certificate is installed.
- Plan and automate renewal.
Prerequisites
- Windows devices enrolled to Fleet MDM
- Okta SCEP endpoint with a static challenge
- Root CA certificate thumbprint for the SCEP issuing CA
- Fleet API token stored as a secret
- Optional GitOps workflow if you manage Fleet configuration as code
Step 1. Collect your values
1.1 Get the CA thumbprint
Windows PowerShell
Get-FileHash -Path "C:\Path\To\ca.cer" -Algorithm SHA256 | Select-Object -ExpandProperty Hash
macOS or Linux
openssl x509 -in ~/Downloads/ca.cer -noout -fingerprint -sha256
# Output looks like:
# SHA256 Fingerprint=E2:18:D7:A7:B0:DF:ED:79:B2:05:73:BA:79:CB:14:B1:FE:EA:D2:7B
# Remove the colons:
# E218D7A7B0DFED79B20573BA79CB14B1FEEAD27B
Use the hex string without colons or spaces in the secret you will create below.
1.2 Choose your SCEP node name
Pick a simple value for {yourCertName}, for example OKTA or OKTAVERIFY. You will use this exact value:
- in the XML profile path
.../SCEP/{yourCertName}/Install/... - in the Exec path
.../SCEP/{yourCertName}/Install/Enroll
1.3 Get your SCEP URL and Challenge
{yourScepUrl}is your Okta SCEP endpoint.{yourScepChallenge}is your static SCEP challenge. This profile expects plain text. Avoid special characters that can break XML or transport. Recommended: letters, numbers, underscore. If your challenge contains characters such as! @ # $ % ^ & * ( ), rotate to a simpler value.
Quick checklist
- {yourCertName} chosen
- {yourScepUrl} confirmed
- {yourScepChallenge} validated (plain text, simple characters)
- {yourScepCAThumbprint} ready (no colons, no spaces)
Step 2. Create Fleet secrets
Follow Fleet’s guide: https://fleetdm.com/guides/secrets-in-scripts-and-configuration-profiles
Create these secrets in Fleet (Controls → Variables) or via GitOps:
| Secret name | Value you collected |
|---|---|
FLEET_SECRET_OKTA_SCEP_URL |
{yourScepUrl} |
FLEET_SECRET_OKTA_SCEP_CHALLENGE |
{yourScepChallenge} (plain text, simple characters) |
FLEET_SECRET_OKTA_CA_THUMBPRINT |
SHA256 thumbprint with no colons, no spaces |
FLEET_SECRET_API |
Fleet API token used by the Exec script |
Optional convenience secret:
FLEET_SECRET_OKTA_CERT_NAMEset to{yourCertName}
Security notes
- Fleet does not hide the secret in script results. Don't print/echo your secrets to the console output.
Step 3. Use Fleet’s XML CSP profile
Source file in the Fleet repo:
docs/solutions/Windows/configuration-profiles/install Okta attestation certificate - [Bundle].xml
Only change the following placeholders:
{yourCertName}set to the SCEP node name you chose in Step 1.2{yourScepUrl}replaced with$FLEET_SECRET_OKTA_SCEP_URL{yourScepChallenge}replaced with$FLEET_SECRET_OKTA_SCEP_CHALLENGE(plain text, simple characters){yourScepCAThumbprint}replaced with$FLEET_SECRET_OKTA_CA_THUMBPRINT(no colons, no spaces)
Important
Use the same {yourCertName} in both the profile path and the Exec path. If they differ, the Exec will 404.
Replace just these lines in the profile
<!-- SCEP Server URL -->
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/{yourCertName}/Install/ServerURL</LocURI>
</Target>
<Meta><Format xmlns="syncml:metinf">chr</Format></Meta>
<Data>$FLEET_SECRET_OKTA_SCEP_URL</Data>
</Item>
<!-- SCEP Challenge (plain text) -->
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/{yourCertName}/Install/Challenge</LocURI>
</Target>
<Meta><Format xmlns="syncml:metinf">chr</Format></Meta>
<Data>$FLEET_SECRET_OKTA_SCEP_CHALLENGE</Data>
</Item>
<!-- SCEP CA Thumbprint (no colons) -->
<Item>
<Target>
<LocURI>./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/{yourCertName}/Install/CAThumbprint</LocURI>
</Target>
<Meta><Format xmlns="syncml:metinf">chr</Format></Meta>
<Data>$FLEET_SECRET_OKTA_CA_THUMBPRINT</Data>
</Item>
Keep the other defaults from the file (KeyLength 2048, KeyUsage 160, HashAlgorithm SHA-1, SubjectName CN=$FLEET_VAR_HOST_UUID managementAttestation, EKUMapping, RetryCount, RetryDelay).
Deploy the profile to your Windows hosts using Fleet.
Step 4. Update the Exec script and run Enroll
Script location in repo:
docs/solutions/Windows/scripts/trigger-scep-enrollment.ps1
Your Exec must target the same {yourCertName} as in the profile. Example path:
./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/{yourCertName}/Install/Enroll
Update your PowerShell script to read the API token from the secret, set your node name, and build the correct LocURI.
# ----- USER SETTINGS -----
# Add your secrets in Fleet (Controls > Variables) or via GitOps.
# The variable named "API" becomes FLEET_SECRET_API
# Full guidance: https://fleetdm.com/guides/secrets-in-scripts-and-configuration-profiles
$NODE_NAME = "OKTA" # must match {yourCertName} in the XML
$FLEET_API = "$FLEET_SECRET_API" # injected by Fleet
$locUri = "./Device/Vendor/MSFT/ClientCertificateInstall/SCEP/$NODE_NAME/Install/Enroll"
# ...construct and send the Exec command body referencing $locUri...
Run the script from Fleet so secrets inject automatically.
Step 5. Verify enrollment
PowerShell
Get-ChildItem Cert:\LocalMachine\My |
Where-Object { $_.Subject -like "*managementAttestation*" } |
Format-List Subject, Thumbprint, NotAfter
GUI
- Open
certlm.msc - Personal > Certificates
- Confirm a certificate whose Subject contains
managementAttestation
Step 6. Renewal
- Automated workflow. Use a Fleet query to find certificates expiring within 30 days and trigger the Exec command for those hosts.
Find certs expiring within 30 days:
Troubleshooting
- Exec returns 404: node name mismatch. Ensure
{yourCertName}in XML equals$NODE_NAMEin the script. - Enrollment fails immediately: check
ServerURL,CAThumbprintformat, and that the device can reach the SCEP URL. - Challenge rejected: try a simpler plain text challenge, or base64 encode and update the XML
<Data>. - Nothing in
Cert:\LocalMachine\My: review Device Management logsGet-WinEvent -LogName Microsoft-Windows-DeviceManagement-Enterprise-Diagnostics-Provider/Admin -MaxEvents 50