Okta SAML 2.0 app setup: fix NameID, audience, and signed assertions
This is for developers wiring an SP to Okta with SAML 2.0 and hitting the usual failures: invalid audience, wrong NameID, or signature validation errors. You’ll finish with an Okta app that emits the exact NameID your SP expects, uses the correct audience/entity ID, and signs the part of the SAML response your SP actually validates.
TL;DR — Most Okta SAML failures come from three mismatches: the SP expects a different NameID format/value, the Audience URI in Okta does not exactly match the SP entity ID, or your SP validates a signed assertion while Okta is only signing the response. Set the SP values first, copy them literally into Okta, then verify the decoded assertion before testing login. Reading time: ~5 min
Goal
When you are done, an Okta SAML 2.0 application will successfully log a user into your service provider, and the captured SAML assertion will show the exact expected NameID, the correct AudienceRestriction/Audience, and a valid XML signature on the response or assertion your SP is configured to validate.
Prerequisites
- Okta admin access that can create or edit applications in your org
- Your SP’s SAML settings from its docs or admin UI: ACS URL, Entity ID / Audience URI, required NameID format, required NameID value source, and whether it validates a signed response, signed assertion, or both
- A test user assigned to the Okta app
openssl >= 3— check with:
openssl version
python >= 3.9for quick Base64/URL decoding — check with:
python3 --version
- A browser with developer tools or a SAML tracer extension if you want to inspect the posted
SAMLResponse - Optional but useful:
xmlstarletfor XML inspection — check with:
xmlstarlet --version
Steps
Step 1: Collect the exact SP values before touching Okta
Record these values from your SP configuration or documentation in a scratch file. Use the SP’s literal values, including case and trailing slashes.
ACS URL: https://app.example.com/saml/acs
Entity ID / Audience URI: https://app.example.com/saml/metadata
NameID format: urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
NameID value: user.email
Signature required by SP: assertion
Signed requests required by SP: no
Default relay state: /dashboard
What you should see when this step succeeds: you have a complete set of SP values with no placeholders left.
Step 2: Create the SAML app in Okta with the SP’s literal ACS and audience values
In Okta Admin Console, go to:
Applications → Applications → Create App Integration → SAML 2.0
Enter a label, then on the SAML settings page set these fields exactly:
Single sign-on URL: https://app.example.com/saml/acs
Use this for Recipient URL and Destination URL: enabled
Audience URI (SP Entity ID): https://app.example.com/saml/metadata
Default RelayState: /dashboard
Name ID format: EmailAddress
Application username: Email
Response: Signed
Assertion Signature: Signed
Signature Algorithm: RSA-SHA256
Digest Algorithm: SHA256
Honor Force Authentication: disabled
If your SP requires an assertion but not a signed response, still prefer signing both when Okta allows it; most SPs accept that. If your SP is strict, match its documented requirement.
What you should see when this step succeeds: the app saves and Okta shows a “View SAML setup instructions” or equivalent summary page with metadata and sign-on details.
Step 3: Set the NameID value to the exact user attribute your SP expects
In the same app, edit the SAML settings and set the NameID value source to the exact Okta user field that maps to your SP requirement.
Use one of these literal mappings:
If SP expects email address:
Application username: Email
Name ID format: EmailAddress
If SP expects persistent opaque ID:
Application username: Okta username
Name ID format: Persistent
If SP expects a custom immutable ID:
Application username: Custom
Expression: user.employeeNumber
Name ID format: Unspecified
Do not guess. If the SP says “NameID must equal email”, use the email field. If it says “persistent”, do not send email with Persistent format.
What you should see when this step succeeds: the app summary reflects the selected NameID format and username mapping without validation errors.
Step 4: Assign the test user and download IdP metadata or certificate
In Okta Admin Console, go to:
Applications → Applications → <your app> → Assignments → Assign → Assign to People
Assign your test user.
Then go to:
Applications → Applications → <your app> → Sign On
Copy the following values or download the metadata XML if your SP supports metadata import:
Identity Provider Single Sign-On URL
Identity Provider Issuer
X.509 Certificate
If your SP needs a PEM certificate file, save the certificate text exactly as shown by Okta:
-----BEGIN CERTIFICATE-----
MIID...
-----END CERTIFICATE-----
What you should see when this step succeeds: the user is assigned and you have the IdP SSO URL, issuer, and signing certificate available for the SP.
Step 5: Configure the SP with Okta’s IdP values
In your SP’s admin UI or config file, enter the IdP values from Okta exactly. A typical SP config looks like this:
saml:
idp_sso_url: "https://your-org.okta.com/app/xxxxxxxx/sso/saml"
idp_issuer: "http://www.okta.com/xxxxxxxx"
idp_x509_cert_pem: |
-----BEGIN CERTIFICATE-----
MIID...
-----END CERTIFICATE-----
sp_entity_id: "https://app.example.com/saml/metadata"
acs_url: "https://app.example.com/saml/acs"
want_assertions_signed: true
want_response_signed: true
nameid_format: "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
If your SP has separate toggles for want_assertions_signed and want_response_signed, set them to match what Okta is sending. If you do not know, start with both enabled and inspect the actual XML in Step 6.
What you should see when this step succeeds: the SP accepts the IdP configuration without certificate parse errors or entity ID validation errors.
Step 6: Run a login and inspect the actual SAMLResponse
Start an SP-initiated login from your app or use the Okta app tile. Capture the posted SAMLResponse, then decode it locally.
If you copied the Base64 value into saml.b64, run:
python3 - <<'PY'
import base64
from pathlib import Path
b64 = Path('saml.b64').read_text().strip()
xml = base64.b64decode(b64)
Path('saml.xml').write_bytes(xml)
print(xml.decode('utf-8')[:1200])
PY
Check NameID, Audience, and signatures:
xmlstarlet sel -N s="urn:oasis:names:tc:SAML:2.0:assertion" -t -m "//s:NameID" -v . -n saml.xml
xmlstarlet sel -N s="urn:oasis:names:tc:SAML:2.0:assertion" -t -m "//s:NameID" -v "@Format" -n saml.xml
xmlstarlet sel -N s="urn:oasis:names:tc:SAML:2.0:assertion" -t -m "//s:Audience" -v . -n saml.xml
xmlstarlet sel -N ds="http://www.w3.org/2000/09/xmldsig#" -t -v "count(//ds:Signature)" -n saml.xml
Expected output shape:
alice@example.com
urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
https://app.example.com/saml/metadata
2
A signature count of 2 usually means both response and assertion are signed. 1 means only one of them is signed.
What you should see when this step succeeds: the decoded XML contains the exact NameID value and format your SP expects, the audience exactly matches the SP entity ID, and at least the required signed element is present.
Step 7: If login still fails, match the SP’s error to the exact mismatch
Common SP-side errors map directly to one field:
Invalid audience -> Audience URI / Entity ID mismatch
NameID policy not satisfied -> wrong NameID format
No assertion signature found -> SP requires signed assertion but assertion is unsigned
Signature validation failed -> wrong cert, stale cert, or XML signature target mismatch
If your SP exposes HTTP logs, a failed ACS post often looks like this:
POST /saml/acs HTTP/1.1
Host: app.example.com
Content-Type: application/x-www-form-urlencoded
...
ERROR saml: audience mismatch: expected "https://app.example.com/saml/metadata" got "https://app.example.com/saml/metadata/"
What you should see when this step succeeds: the error points to one exact field, and changing that field in Okta or the SP resolves the next login attempt.
Verify it works
Run one complete login and verify all three conditions.
- In the browser, the SP login completes and lands on the authenticated page, for example
/dashboard. - In the decoded
saml.xml, these commands return the exact expected values:
xmlstarlet sel -N s="urn:oasis:names:tc:SAML:2.0:assertion" -t -m "//s:NameID" -v . -n saml.xml
xmlstarlet sel -N s="urn:oasis:names:tc:SAML:2.0:assertion" -t -m "//s:NameID" -v "@Format" -n saml.xml
xmlstarlet sel -N s="urn:oasis:names:tc:SAML:2.0:assertion" -t -m "//s:Audience" -v . -n saml.xml
Expected output example:
alice@example.com
urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
https://app.example.com/saml/metadata
- Your SP logs show a successful assertion consumer event rather than a validation error.
Common pitfalls
Audience URI differs by one slash or scheme
Mistake: using http:// in one system and https:// in the other, or adding a trailing slash to the entity ID in Okta.
Symptom: SP error like Invalid audience, Audience restriction validation failed, or expected https://app.example.com/saml/metadata got https://app.example.com/saml/metadata/.
Fix: copy the SP entity ID literally into Okta’s Audience URI (SP Entity ID).
NameID format and NameID value do not match
Mistake: sending EmailAddress format with a username like alice, or sending Persistent while the value changes.
Symptom: SP error like NameID policy not satisfied or user provisioning links to the wrong account.
Fix: set Application username and Name ID format as a pair that matches the SP requirement exactly.
SP requires signed assertions, but only the response is signed
Mistake: enabling signed response only and assuming that covers assertion validation.
Symptom: SP error like No assertion signature found or Assertion is not signed even though the XML contains one ds:Signature.
Fix: in Okta app SAML settings, set Assertion Signature: Signed and retest.
Wrong or stale Okta signing certificate in the SP
Mistake: pasting an old certificate after rotating the app signing cert, or truncating the PEM block.
Symptom: SP error like Signature validation failed, certificate parse error, or unable to verify signature.
Fix: recopy the full current X.509 certificate from the Okta app’s Sign On page into the SP.
Testing with an unassigned user
Mistake: the app is configured correctly, but the test user was never assigned.
Symptom: Okta blocks access before issuing SAML, or the app tile is missing for the user.
Fix: go to Applications → Applications → <your app> → Assignments and assign the test user explicitly.
This article was written by an AI system and published pending human review. Verify anything you intend to act on.
Have a project in mind?
Get an instant AI price estimate for it, or talk directly to our team.
One email a month on what we learn building with AI