Skip to content

Fix at_hash verification for string access tokens - #339

Draft
mehdifanai wants to merge 1 commit into
NabuCasa:masterfrom
mehdifanai:agent/fix-at-hash-string-handling
Draft

mehdifanai wants to merge 1 commit into
NabuCasa:masterfrom
mehdifanai:agent/fix-at-hash-string-handling

Conversation

@mehdifanai

@mehdifanai mehdifanai commented Aug 6, 2026 •

Copy link
Copy Markdown

Summary

  • Encode string access tokens before passing them to PyJWT's hash implementation.
  • Decode the Base64URL digest before comparing it with the JWT at_hash string claim.
  • Add real RSA-signed token coverage for both matching and mismatched at_hash values.

Root cause

Cognito.verify_token passes self.access_token directly to
compute_hash_digest. Cognito access tokens are strings, while current PyJWT
hash implementations require bytes. This raises TypeError before an ID token
with an at_hash claim can be verified. After hashing, the Base64URL result is
also bytes and must be decoded before it is compared with the string claim.

The conversion is deliberately conditional so callers that already provide a
bytes-like access token retain their existing hashing path.

This failure was observed through Emporia Vue token authentication on Home
Assistant 2026.7/Python 3.14: magico13/ha-emporia-vue#439

Validation

  • Full test suite on Python 3.9, 3.12, and 3.13: 18 passed on each version
  • Repository lint suite: flake8 passed, pylint 10.00/10, black clean
  • Real RSA-signed valid and invalid at_hash cases under Home Assistant's
    Python 3.14 runtime

trooperthorn added a commit to trooperthorn/ha_int_emporia that referenced this pull request Sep 26, 2026
pycognito 2024.5.1 hashes the Cognito access token as a str when
verifying an ID token's OIDC at_hash claim, which raises
TypeError: Cannot convert str to buffer on Python 3.14. This breaks
token-based setup (pasted Google/Apple Cognito tokens) whenever the
ID token carries an at_hash claim, which Hosted-UI tokens do.

Add pycognito_compat.py, a scoped shim that rebinds only
pyemvue.auth.Cognito (not pycognito.Cognito globally) to a subclass
whose verify_token fixes the at_hash computation per the still-open
NabuCasa/pycognito#339, gated on a runtime probe so it becomes a
no-op automatically once pycognito ships that fix. Hook it in at the
two places PyEmVue's Cognito client gets constructed: VueHub in
config_flow.py and async_login_vue in __init__.py.

Reproduced the bug and verified the fix does not weaken verification
in tests/test_pycognito_compat.py using a locally generated RS256
JWT/JWKS pair (no network). Documented the decision and flagged the
migration plan's future direct-pycognito helper to carry the same
fix.

Co-authored-by: Sean T. Martinez <Sean@net.secretsquirrel.club>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
@mattheworiordan

Copy link
Copy Markdown

I hit this same issue with Apple SSO in the Home Assistant Emporia Vue integration: token validation failed with TypeError: Strings must be encoded before hashing when the ID token contained at_hash.

Applying the same string/bytes handling as this PR in a local compatibility adapter resolved it. Fresh Emporia tokens now connect successfully, with all 100+ sensor entities reporting across three Vue monitors. This was tested on Home Assistant 2026.9.4 with pycognito 2024.5.1 and PyJWT 2.13.0.

I also prepared six additional regression tests against this PR's branch: valid hashes and decoded claims, mismatched access tokens, bytes access tokens, invalid signatures, ID tokens without at_hash, and verification after token refresh. All 24 tests pass on Python 3.12, along with flake8, pylint and Black. The fixtures use synthetic tokens and a generated RSA key; no account credentials are included.

@mehdifanai, would you like to include these tests in this PR? I can provide the patch or open a tests-only PR against your fix branch.

This PR is still in draft too,what's holding it up. Can I help?

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants