Skip to content

fix: okta sibling case - #151

Merged
samiksharana-okta merged 5 commits into
auth0:masterfrom
elbuo8:okta-sibling-case
Oct 16, 2026
Merged

samiksharana-okta merged 5 commits into
auth0:masterfrom
elbuo8:okta-sibling-case

Conversation

@elbuo8

@elbuo8 elbuo8 commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

By submitting a PR to this repository, you agree to the terms within the Auth0 Code of Conduct. Please see the contributing guidelines for how to create and submit a high-quality PR for this repo.

Description

decryptKeyInfo now resolves a RetrievalMethod reference to an EncryptedKey that sits next to EncryptedData. This is the layout Okta uses, where the EncryptedKey is a sibling of EncryptedData under EncryptedAssertion. It is valid XML Encryption and SAML 2.0. Previously the lookup only searched inside the EncryptedData element, so callers that pass that element alone could not find the key.

Changes

  • Key lookup scope: a RetrievalMethod #id is now resolved within the parent of EncryptedData (the enclosing EncryptedAssertion) instead of only the EncryptedData subtree. The same-document #id requirement and the duplicate-Id rejection are unchanged.
  • Single EncryptedData: decrypt and decryptKeyInfo resolve the first EncryptedData once. The key, algorithm, and ciphertext are all read from that block, so they can no longer come from different EncryptedData or EncryptedKey elements in the same document.
  • Direct-child lookups: KeyInfo, EncryptedKey, RetrievalMethod, EncryptionMethod, and CipherData/CipherValue are read as direct children of the resolved element. An EncryptedKey nested deeper than a direct child of KeyInfo is no longer found.

Testing

  • Unit tests cover the sibling-key layout, a key located outside the parent (must still fail), and a document with two EncryptedData blocks.
  • A sanitized Okta-shaped fixture (test/test-okta-sibling-key.xml) checks that the key is found. It uses dummy identifiers and ciphertext.
  • An integration test decrypts the existing Okta fixture with the real key, passing only the EncryptedData element.

Checklist

  • I have added documentation for new/changed functionality in this PR or in auth0.com/docs
  • All active GitHub checks for tests, formatting, and security are passing
  • The correct base branch is being used, if not the default branch

elbuo8 and others added 3 commits July 31, 2026 16:11
decryptKeyInfo located elements with XPath expressions that were built by
string concatenation and scoped by a leading "//". Both properties made the
lookups behave differently from how they read.

"//" is descendant-or-self over the whole document regardless of the context
node passed to select(), so lookups that appear scoped were document-wide:

  - the wrapped key was taken from the first CipherValue anywhere in the
    document rather than from the EncryptedKey being processed
  - DigestMethod was resolved by an absolute path, so with several
    EncryptedKey elements it could return a digest declared by one key while
    the ciphertext came from another
  - the absolute DigestMethod path also matched nothing for documents using
    EncryptedData/KeyInfo/RetrievalMethod, where EncryptedKey sits outside
    KeyInfo, silently defaulting oaepHash to sha1

Predicates were also assembled by concatenating the RetrievalMethod URI into
the expression text, so an Id containing a quote changed what the expression
meant instead of being compared as a value.

lib/dom-select.js walks the DOM explicitly. Document values are compared,
never concatenated into anything that gets parsed, and scope is a node
enforced by the walk itself. EncryptionMethod and CipherValue are now read
from one resolved EncryptedKey, so they always describe the same key.

Two input-validation changes come with this:

  - a RetrievalMethod URI must be a same-document fragment reference.
    substring(1) removed the first character whatever it was, so "/ek1" and
    "Xek1" both resolved to Id "ek1".
  - a duplicated Id is reported rather than resolved to its first match.
    Duplicate Ids are invalid XML and resolving them makes the result depend
    on document order.

xpath moves to devDependencies; the runtime no longer parses expressions.
Verified with a packed tarball installed via --omit=dev, where
require('xpath') throws MODULE_NOT_FOUND and both fixtures still decrypt.

65 passing (44 before). Each property is pinned by a test that also asserts
the previous XPath behaviour for contrast, and 11 mutations of the traversal
and the guards were each confirmed to fail a named test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@elbuo8
elbuo8 requested a review from a team as a code owner October 9, 2026 17:29

@adamjmcgrath adamjmcgrath left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tested with okta and samlmock - lgtm

@elbuo8 elbuo8 changed the title Okta sibling case fix: okta sibling case Oct 16, 2026
@samiksharana-okta
samiksharana-okta merged commit da5b083 into auth0:master Oct 16, 2026
4 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 6.0.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants