Problem/Motivation

Been struggling all arvo with JWT authentication issues and I finally found the problem; a malformed key on our deployment environments. However the logic in the /user/login injection and /jwt/token led me up the garden path -- it specifically checks if \Drupal::service('jwt.authentication.jwt')->generateToken() returns FALSE. However, with the malformed key in play, this method was returning NULL.

The JwtAuth::generateToken method is documented to return string|bool, but it's not PHP typed to return anything. Let's fix that.

Steps to reproduce

  1. Set up JWT with JWT Auth Issuer
  2. Break your key somehow. Say, put a typo in it or leave off the -----BEGIN PRIVATE KEY----- prefix/suffix
  3. Try getting a JWT through /user/login, /jwt/token, or \Drupal::service('jwt.authentication.jwt')->generateToken()

Proposed resolution

Ensure that JwtAuth::generateToken returns a strongly typed string|bool.

Remaining tasks

  • Implement change
  • Test?

User interface changes

None.

API changes

Ideally there shouldn't be any, but anything that relies on the return of JwtAuth::generateToken to not be a string or bool will break.

Data model changes

None.

Issue fork jwt-3557317

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

geoffreyr created an issue. See original summary.

geoffreyr’s picture

Assigned: geoffreyr » Unassigned
Status: Active » Needs review
geoffreyr’s picture

Issue summary: View changes
pwolanin’s picture

Since we are working on a new major version here, I wonder if it would be clearer/simple to return NULL on failure so the type hint can be ?string. This would be the simplest and would match the return value type hint on the underlying transcoder.

Or would it even be better to throw an exception?

pwolanin’s picture

Status: Needs review » Needs work

Switch to ?string and simpler check for empty return. Needs a test

pwolanin’s picture

Used claude to generate a Functional test:

added the functional test at web/modules/drupal/jwt/tests/src/Functional/JwtAuthIssuerControllerTest.php.   
                                                                                                                                     
  Test coverage — four cases, each exercising a code path that the 3.x→branch diff changed:                                                                                  
                                                                                                                                                                             
  1. testTokenResponseValidKey — logs in via cookie, GETs jwt/token, asserts 200 + token key + token decodes to the user's UID.                                              
  2. testTokenResponseInvalidKey — calls setShortHmacKey() to overwrite jwt_test_hmac with a <32-byte value (so JwtTranscoder::setSecret() rejects it and generateToken()    
  returns NULL), then asserts 500 + error message containing "set a key in the JWT admin page".                                                                              
  3. testLoginResponseIncludesJwtWithValidKey — POSTs to user.login.http (_format=json), asserts access_token is present.
  4. testLoginResponseHasNoJwtWithInvalidKey — same login POST after setShortHmacKey(), asserts 200, no access_token, but current_user still present (the new if             
  (!empty($decoded['access_token'])) guard preserves the underlying login payload).                                                                                          
                                                                                                                                                                             
  A note on the "invalid key" approach: pointing jwt.config:key_id at a non-existent key id was tempting but would leave JwtTranscoder::$algorithmType (typed string,        
  non-nullable) uninitialized and throw a TypeError from getKey() rather than reach the controller's null-token branch. Overwriting the existing key with a too-short value
  reaches the intended path because setKey() still calls setAlgorithm() before setSecret() rejects the value. 
pwolanin’s picture

Status: Needs work » Needs review

  • pwolanin committed f8f941b2 on 3.x authored by geoffreyr
    fix: #3557317 Incorrect typing on JwtAuth::generateToken, hard to...
pwolanin’s picture

Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.