Uploaded image for project: 'MusicBrainz Server'
  1. MusicBrainz Server
  2. MBS-11313

OAuth PKCE S256 verification implementation is not RFC compliant

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Normal
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 2021-01-11
    • Component/s: Accounts, Web service
    • Labels:
      None

      Description

      Whilst testing OAuth authentication with MB in a Rails app I encountered an issue with PKCE when the verification method is set to "S256". I believe this is due to an implementation detail in the MusicBrainz server which means it is not compliant with the RFC.

      The RFC states the the client should derive a code challenge from the code verifier as follows:

      code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
      

      Later on in the process the server should verify the code_verifier using the following logic:

      BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) == code_challenge
      

      However the MB server code performs the verification as follows (operands reordered for clarity):

      encode_base64url(sha256(decode_base64url($code_verifier)))) eq $code_challenge
      

      Note the call to decode_base64. I believe this may have been added due to a misunderstanding of the following recommendation from the RFC:

      The client SHOULD create a "code_verifier" with a minimum of 256 bits
       of entropy. This can be done by having a suitable random number
       generator create a 32-octet sequence. The octet sequence can then be
       base64url-encoded to produce a 43-octet URL safe string to use as a
       "code_challenge" that has the required entropy.

      The base64 encoding is recommended here purely as a means of generating a 43 octet URL safe string with sufficient entropy, not so that the underlying value can be subsequently decoded during the PKCE verification.

      The following is example of S256 PKCE verification code from another OAuth implementation that I believe is RFC compliant:

      grant.code_challenge == generate_code_challenge(code_verifier)
      def generate_code_challenge(code_verifier) 
        Base64.urlsafe_encode64(Digest::SHA256.digest(code_verifier), padding: false)
      end 

      I'd be happy to submit a simple PR to fix this if confirmed.

        Attachments

          Activity

            People

            Assignee:
            bitmap Michael Wiencek
            Reporter:
            atj atj
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved:

                Packages

                Version Package
                2021-01-11