> For the complete documentation index, see [llms.txt](https://docs.lithnet.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.lithnet.io/okta-ma/authentication/oauth-with-an-x509-certificate.md).

# OAuth with an X.509 certificate

Authenticate to Okta using a Windows certificate, so the private key can be non-exportable or held in a TPM or HSM.

## Background

Okta authenticates a service app by verifying a signed JWT against a public key it holds as a JWK. There are no certificates anywhere in that flow.

The management agent bridges the two. You give it a certificate thumbprint, and it finds the certificate in a Windows store, derives the public JWK from the certificate's public key, and signs each OAuth client assertion using the certificate's private key through the Windows cryptographic provider. The derived JWK is what you register in Okta.

The benefit of this approach is that Windows holds the private key, so it can be marked as non-exportable, or held in a TPM or HSM, and never has to exist as a file anywhere. Nothing else changes: to Okta, this is an ordinary `private_key_jwt` service app.

This design has two consequences that are worth understanding before you start:

* **Okta never sees the certificate.** It receives one JWK containing a modulus and exponent, or a curve and coordinates. There is no chain, no CA, and no trust decision for Okta to make. A self-signed certificate is entirely normal here.
* **The JWK `kid` is the certificate thumbprint.** This is what ties the key registered in Okta to the certificate on the MIM server, and it's why replacing or renewing the certificate always means registering a new key in Okta.

## Certificate requirements

The management agent checks all of this when it connects, so a mistake here surfaces the first time you retrieve the schema. The certificate must be:

* Within its validity period
* Backed by a private key the MIM Synchronization Service account can use
* RSA of at least 2048 bits, or EC on P-256, P-384, or P-521
* Permitted to sign, if it carries a Key Usage extension

Issuer, subject, SANs, and extended key usage are not checked. Both self-signed and CA-issued certificates work.

RSA keys sign with `RS256`. EC keys sign with `ES256`, `ES384`, or `ES512`, matching the curve.

## Step 1: Choose the certificate store

This is the step that most often goes wrong, so it's worth taking the time to get it right.

The management agent looks for the thumbprint in two places, in this order:

1. `CurrentUser\My` **of the account running the MIM Synchronization Service**
2. `LocalMachine\My`

{% hint style="warning" %}
`CurrentUser` means the service account, not you. A certificate sitting in the interactive administrator's personal store is invisible to the management agent. This is the most common cause of "certificate not found" errors when the certificate is clearly visible in `certmgr.msc`.
{% endhint %}

Find out which account runs the service:

```powershell
(Get-CimInstance Win32_Service -Filter "Name='FIMSynchronizationService'").StartName
```

Then pick a store:

| Situation                                                                     | Store             | Extra work                                                   |
| ----------------------------------------------------------------------------- | ----------------- | ------------------------------------------------------------ |
| You can start PowerShell as the service account                               | `CurrentUser\My`  | None. A key created by that account is already usable by it. |
| Group managed service account, `LocalSystem`, or you do not have the password | `LocalMachine\My` | Grant the service account read access to the private key.    |
| The cryptographic provider requires the machine store, or the key is shared   | `LocalMachine\My` | Grant the service account read access to the private key.    |

Either way, the certificate needs to be installed on the MIM server that runs the management agent.

## Step 2: Create the certificate

Skip to [Step 3](#step-3-export-the-public-jwk) if you already have a certificate that meets the requirements above.

Download [OktaManagementAgentKeys.ps1](https://github.com/lithnet/okta-ma-docs/blob/master/scripts/OktaManagementAgentKeys.ps1) to the MIM server, review it, and load it:

```powershell
Invoke-WebRequest -Uri 'https://raw.githubusercontent.com/lithnet/okta-ma-docs/master/scripts/OktaManagementAgentKeys.ps1' -OutFile '.\OktaManagementAgentKeys.ps1'

. .\OktaManagementAgentKeys.ps1
```

`New-OktaManagementAgentCertificate` creates a non-exportable signing certificate and writes its public JWK. The output directory must already exist, and you'll need to add `-Force` to overwrite an existing output file.

Run whichever of the following applies, as the account whose store you chose in Step 1.

### RSA

```powershell
$result = New-OktaManagementAgentCertificate `
    -KeyType RSA `
    -RsaKeyLength 3072 `
    -StoreLocation CurrentUser `
    -OutputPath 'C:\Temp\okta-ma-public.jwk.json'

$result | Format-List Thumbprint, StoreLocation, JwkPath
```

### EC

```powershell
$result = New-OktaManagementAgentCertificate `
    -KeyType P-256 `
    -StoreLocation CurrentUser `
    -OutputPath 'C:\Temp\okta-ma-public.jwk.json'
```

`P-384` and `P-521` are also accepted.

### TPM-backed

Windows exposes the TPM through the Microsoft Platform Crypto Provider. Name it with `-Provider`:

```powershell
$result = New-OktaManagementAgentCertificate `
    -KeyType P-256 `
    -StoreLocation CurrentUser `
    -Provider 'Microsoft Platform Crypto Provider' `
    -OutputPath 'C:\Temp\okta-ma-public.jwk.json'
```

{% hint style="info" %}
Use an EC key in a TPM. TPM 2.0 caps RSA at 2048 bits, so a 3072-bit or 4096-bit RSA key can't be created, and P-256 is the only curve that every TPM 2.0 device supports. Support for anything beyond P-256 depends on the particular TPM.

RSA still works if you need it; use `-RsaKeyLength 2048`.
{% endhint %}

### HSM

Create or enroll the certificate with the HSM vendor's own tooling, then treat it as an existing certificate and continue at Step 3.

The key must be usable by the MIM Synchronization Service account without any interactive steps, as there is nobody at the console during a scheduled run to authorize the use of the key.

### Subject and expiry

The certificate is created with the subject `CN=Lithnet Okta Management Agent` and a validity of two years. Add `-Subject` and `-NotAfter` to any of the commands above to change either:

```powershell
$result = New-OktaManagementAgentCertificate `
    -Subject 'CN=Okta MA (MIM01)' `
    -NotAfter (Get-Date).AddYears(3) `
    -StoreLocation CurrentUser `
    -OutputPath 'C:\Temp\okta-ma-public.jwk.json'
```

Whatever expiry you choose, record the date somewhere you'll see it before it arrives. Renewing the certificate involves work in both Okta and MIM.

### If you used the LocalMachine store

Change `-StoreLocation` to `LocalMachine` in any of the commands above, and run PowerShell as an administrator. Then grant the service account access to the key:

1. Open `certlm.msc`.
2. Find the certificate under **Personal** > **Certificates**.
3. Right-click it, then **All Tasks** > **Manage Private Keys**.
4. Add the MIM Synchronization Service account with **Read** permission.

The certificate and its public JWK are ready. Go to [Step 4](#step-4-register-the-public-key-in-okta).

## Step 3: Export the public JWK

For a certificate you already have, load the tooling and convert it by thumbprint:

```powershell
. .\OktaManagementAgentKeys.ps1

ConvertTo-OktaPublicJwk `
    -Thumbprint '0123456789ABCDEF0123456789ABCDEF01234567' `
    -OutputPath 'C:\Temp\okta-ma-public.jwk.json'
```

The thumbprint form searches `CurrentUser\My` then `LocalMachine\My`, the same order the management agent uses. If you run PowerShell as the service account, it will resolve to exactly the same certificate the connector will find.

If you are running as a different account, name the store instead so there is no ambiguity:

```powershell
Get-Item 'Cert:\LocalMachine\My\0123456789ABCDEF0123456789ABCDEF01234567' |
    ConvertTo-OktaPublicJwk -OutputPath 'C:\Temp\okta-ma-public.jwk.json'
```

Only the public key is written. The private key stays in the certificate store or the hardware provider.

The result looks like this. It contains only public key material, so it's safe to email, paste into a ticket, or hand to your Okta administrator:

```json
{
  "kty": "RSA",
  "use": "sig",
  "kid": "0123456789ABCDEF0123456789ABCDEF01234567",
  "alg": "RS256",
  "n": "u3mF5vdnnF4ukOZtahidaki0wgPr62jwVBeeIa...",
  "e": "AQAB"
}
```

If you need a JWKS document instead, for a JWKS endpoint or the Okta management API, `ConvertTo-OktaPublicJwks` takes the same parameters and wraps the key in a `keys` array. Don't use this format when pasting the key into the Admin Console.

## Step 4: Register the public key in Okta

Work through [Creating the OAuth service app](/okta-ma/authentication/creating-the-oauth-service-app.md), pasting the contents of `okta-ma-public.jwk.json` at the public key step. Record the client ID.

{% hint style="warning" %}
Provide the JWK file and nothing else. There's no need to export the certificate, and a PFX file would contain the very private key you set out to protect.
{% endhint %}

Come back here once the app has its public key, scopes, and admin role.

## Step 5: Configure MIM

On the management agent Connectivity page:

1. Set **Authentication method** to **OAuth 2.0 (X.509 certificate)**.
2. Enter the app's client ID in **OAuth client ID**.
3. Enter the certificate thumbprint in **Certificate thumbprint**. Spaces are stripped, so pasting it out of the certificate dialog is fine.
4. Enter your Okta org URL in **Tenant URL**, for example `https://example.okta.com`. Don't use the `-admin` hostname.
5. Set the log file path and log level.
6. Save the page.

![Connectivity settings for X.509 certificate authentication.](https://2206708376-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F0u5rDWCokdBire3bUPyS%2Fuploads%2Fgit-blob-fb5d8665fa7a799eb6aa1a982eb696bc1acd37bb%2Fmim-connectivity-x509.png?alt=media)

The Connectivity page shows the fields for all three authentication methods at once. With this method selected, only **OAuth client ID**, **Certificate thumbprint**, and **Tenant URL** are used. Leave **API key** and **Private JWK file path** empty.

## Step 6: Retrieve the schema

Retrieve the management agent schema. This is the first point at which everything is exercised together: the certificate lookup, the private key, the OAuth token request, and the granted scopes. The object types and attribute directions MIM shows should match the scopes on the service app.

If it fails, see [X.509 certificate troubleshooting](/okta-ma/administration/troubleshooting.md#x509-certificate).

Then continue with [Creating the management agent](/okta-ma/configuration/creating-the-management-agent.md).

## Renewal and replacement

A renewed certificate normally has a new key pair, which means a new thumbprint, and therefore a new `kid`. You'll need to register a new public key in Okta and update the thumbprint in MIM. [Key and certificate rotation](/okta-ma/authentication/key-and-certificate-rotation.md) shows you how to do this without an outage.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.lithnet.io/okta-ma/authentication/oauth-with-an-x509-certificate.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
