Skip to content

fix: EdDSA to Ed25519 token migration#786

Draft
Stellatsuu wants to merge 6 commits intoDIRACGrid:mainfrom
Stellatsuu:Ed25519-token-migration
Draft

fix: EdDSA to Ed25519 token migration#786
Stellatsuu wants to merge 6 commits intoDIRACGrid:mainfrom
Stellatsuu:Ed25519-token-migration

Conversation

@Stellatsuu
Copy link
Contributor

@Stellatsuu Stellatsuu commented Feb 10, 2026

cc @aldbr
Closes: #718

Changes:

  • changed token creation algorithm to be Ed25519 instead of EdDSA
  • added Ed25519 to the authorized algorithm list. EdDSA is still listed as authorized algorithm so people have time to rotate keys before full migration

TODO:

  • create documentation about keys rotation

@Stellatsuu Stellatsuu changed the title fix: Ed25519 token migration fix: EdDSA to Ed25519 token migration Feb 10, 2026
Copy link
Contributor

@aldbr aldbr left a comment

Choose a reason for hiding this comment

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

That looks good, thank you 🙂
I just have a few minor comments around the tests.
Next step: making sure there is a documentation page to rotate the keys (and delete old ones if needed). If it does not exist, then we need to create one based on #499 (comment)

@read-the-docs-community
Copy link

Documentation build overview

📚 diracx | 🛠️ Build #31354735 | 📁 Comparing fb73096 against latest (60e5846)


🔍 Preview build

Show files changed (1 files in total): 📝 1 modified | ➕ 0 added | ➖ 0 deleted
File Status
admin/reference/env-variables/index.html 📝 modified

@Stellatsuu
Copy link
Contributor Author

TODOs:

Rotation:

  1. Launch demo
  2. kubectl get secrets (optional, check if diracx-jwks is here)
  3. kubectl get secret diracx-jwks -o yaml (retrieve old keys)
  4. python -m diracx.logic rotate-jwk --jwks-path jwks.json (rotate)
  5. kubectl create secret generic diracx-jwks \ --namespace=$namespace \ --from-file=jwks.json \ -> check if update exists (update new keys)

How to check rotation:

  1. dirac login -> rotate key -> submit job -> check if ok
  2. re-login -> /.cache/diracx/credentials.json -> decode -> check if alg = Ed25519

Also check https://jose.authlib.org/en/rfc/9864/#rfc9864

@Stellatsuu
Copy link
Contributor Author

Stellatsuu commented Feb 11, 2026

joserfc error breaking the demo when trying to login as diracAdmin:

  • HttpResponseError: Operation returned an invalid status 'Internal Server Error'
  • kubectl logs diracx-demo-...

Full Logs:

File "/diracx_source/diracx/diracx-logic/src/diracx/logic/auth/token.py", line 380, in create_token
   return jwt.encode(
          ^^^^^^^^^^^
 File "/opt/conda/lib/python3.11/site-packages/joserfc/jwt.py", line 80, in encode
   return serialize_compact(_header, payload, key, algorithms, registry)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "/opt/conda/lib/python3.11/site-packages/joserfc/jws.py", line 105, in serialize_compact
   alg: JWSAlgModel = registry.get_alg(protected["alg"])
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "/opt/conda/lib/python3.11/site-packages/joserfc/_rfc7515/registry.py", line 81, in get_alg
   raise UnsupportedAlgorithmError(f"Algorithm of '{name}' is not supported")
joserfc.errors.UnsupportedAlgorithmError: unsupported_algorithm: Algorithm of 'Ed25519' is not supported

See: https://jose.authlib.org/en/guide/errors/#unsupportedalgorithmerror

@Stellatsuu
Copy link
Contributor Author

Stellatsuu commented Feb 11, 2026

joserfc error breaking the demo when trying to login as diracAdmin:

* `HttpResponseError: Operation returned an invalid status 'Internal Server Error'`

* `kubectl logs diracx-demo-...`

Full Logs:

File "/diracx_source/diracx/diracx-logic/src/diracx/logic/auth/token.py", line 380, in create_token
   return jwt.encode(
          ^^^^^^^^^^^
 File "/opt/conda/lib/python3.11/site-packages/joserfc/jwt.py", line 80, in encode
   return serialize_compact(_header, payload, key, algorithms, registry)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "/opt/conda/lib/python3.11/site-packages/joserfc/jws.py", line 105, in serialize_compact
   alg: JWSAlgModel = registry.get_alg(protected["alg"])
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "/opt/conda/lib/python3.11/site-packages/joserfc/_rfc7515/registry.py", line 81, in get_alg
   raise UnsupportedAlgorithmError(f"Algorithm of '{name}' is not supported")
joserfc.errors.UnsupportedAlgorithmError: unsupported_algorithm: Algorithm of 'Ed25519' is not supported

See: https://jose.authlib.org/en/guide/errors/#unsupportedalgorithmerror

Tried to match their tests logic in our code:
https://github.com/authlib/joserfc/blob/9db7e44bd7936576ff3b6305d570b8b89ea342f4/tests/jws/test_eddsa.py#L21-L30

def create_token(payload: TokenPayload, settings: AuthSettings) -> str:
    """Create a JWT token with the given payload and settings."""
    signing_key = None
    for key in settings.token_keystore.jwks.keys:
        key_ops = key.get("key_ops")
        if key_ops and not isinstance(key_ops, list):
            key_ops = [key_ops]
        if key_ops and "sign" in key_ops:
            signing_key = key
            break

    if not signing_key:
        raise ValueError("No signing key found in JWKS")

    # test logic here
    algorithms = ["Ed25519"]
    encoded_jwt = jwt.encode({"alg": "Ed25519"}, {}, signing_key, algorithms=algorithms)
    jwt.decode(encoded_jwt, signing_key, algorithms=algorithms)

Still the same error.

What their signing_key looks like:

{
      "crv": "Ed25519", 
      "x": "t-nFRaxyM5DZcpg5lxiEeJcZpMRB8JgcKaQC0HRefXU", 
      "d": "gUF17HCe-pbN7Ej2rDSXl-e7uSj7rQW5u2dNu0KINP0", 
      "kty": "OKP", 
      "kid": "5V_IcL-iX5IbaNz9vg0CjXtWLZiJ94-ESnHI-HN1L2Y"
}

Our signing_key(s) looks like:

{
      "crv": "Ed25519",
      "x": "OgKojQ4lAhaRjU_KF1vNkv99dnu8GRuDwkhdiAdsSJc",
      "d": "skcpv4O3AR5GvITk2AA84H8AfhzXoth49TKTlE_dusM",
      "key_ops": [
        "sign",
        "verify"
      ],
      "alg": "Ed25519",
      "kid": "019c4d8717dc75b1afc74172281b3b75",
      "kty": "OKP"
    }

@Stellatsuu
Copy link
Contributor Author

Stellatsuu commented Feb 12, 2026

Tried to test the different keys

@pytest.mark.parametrize(
    "key", [
        # joserfc key
        ({"crv": "Ed25519", "x": "t-nFRaxyM5DZcpg5lxiEeJcZpMRB8JgcKaQC0HRefXU", "d": "gUF17HCe-pbN7Ej2rDSXl-e7uSj7rQW5u2dNu0KINP0", "kty": "OKP", "kid": "5V_IcL-iX5IbaNz9vg0CjXtWLZiJ94-ESnHI-HN1L2Y"}),
        # dirac key
        ({"crv": "Ed25519", "x": "OgKojQ4lAhaRjU_KF1vNkv99dnu8GRuDwkhdiAdsSJc", "d": "skcpv4O3AR5GvITk2AA84H8AfhzXoth49TKTlE_dusM", "key_ops": ["sign","verify"], "alg": "Ed25519", "kid": "019c4d8717dc75b1afc74172281b3b75", "kty": "OKP"})
    ]
)
def test_dummy(key):
    from joserfc.jwk import OKPKey
    from joserfc import jwt

    ed25519_key = OKPKey.import_key(key)
    algorithms = ["Ed25519"]
    encoded_jwt = jwt.encode({"alg": "Ed25519"}, {}, ed25519_key, algorithms=algorithms)
    jwt.decode(encoded_jwt, ed25519_key, algorithms=algorithms)

Both test passed.

Maybe ed25519_key = OKPKey.import_key(key) is needed in case of Ed25519 keys so they are registered as allowed algorithm?

Edit: import_key only takes dict objects, and we have BaseKey objects (OPKKey/RSAKey) in our settings.token_keystore.jwks.keys

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.

Migration needed: EdDSA algorithm identifier deprecated in favor of Ed25519 (RFC 9864)

2 participants