Skip to content

Conversation

@shimastripe
Copy link
Contributor

WHAT

The conversion between base64URL and standard base64 had incorrect character mappings. Updated both directions:

  • base64URLToBase64: now correctly maps -+ and _/
  • base64ToBase64URL: now correctly maps +- and /_

Added comprehensive unit tests to verify the conversion logic including padding and character replacement.

ref

   The conversion between base64URL and standard base64 had incorrect character mappings. Updated both directions:
   - base64URLToBase64: now correctly maps - → + and _ → /
   - base64ToBase64URL: now correctly maps + → - and / → _

   Added comprehensive unit tests to verify the conversion logic including padding and character replacement.
@shimastripe
Copy link
Contributor Author

This issue is already present in the JWT returned by the production IAP API, and with the current SDK version it results in INVALID_JWT_FORMAT. I would appreciate it if you could look into this.

guard let headerData = Data(base64Encoded: base64URLToBase64(bodySegments[0])), let bodyData = Data(base64Encoded: base64URLToBase64(bodySegments[1])) else {
return VerificationResult.invalid(VerificationError.INVALID_JWT_FORMAT)
}

import XCTest
@testable import AppStoreServerLibrary

final class Base64URLToBase64Tests: XCTestCase {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this not already tested by the ChainVerifier tests now that TestingUtility is corrected? Could we stick with those tests please, if so

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The TestingUtility method is only triggered when the Base64 generated from a JSON file contains one of -, +, /, or _. Right now the issue doesn’t occur simply by chance, and even if we prepare test data that hits this case, any future changes to the JSON could remove those characters from the Base64, making the test difficult to maintain.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

    public func testEscapedJWT() async {
        /// {"czP": "ml>"}
        /// base64: eyJjelAiOiAibWw+In0=
        struct SignedString: DecodedSignedData, Codable, Hashable {
            let czP: String
            var signedDateOptional: Date? = nil
        }

        let chainVerifier = TestingUtility.getChainVerifier()
        let result = await chainVerifier.verify(
            signedData: "eyJhbGciOiJFUzI1NiJ9.eyJjelAiOiAibWw+In0=.stM6DaOF2ihXJHhU9vqcxxrtYctS7qc6IioF8co_00KiqvaLlSdpog11Yc8TbmSj099babIeSm_Ei53-ZioWFg",
            type: SignedString.self,
            onlineVerification: false,
            environment: .localTesting
        )
        guard case .valid = result else {
            XCTFail("Expected .valid, got \(result)")
            return
        }
    }

I tried a few approaches, and it seems we could at least prepare tests like the ones I’ve put together. What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@shimastripe Could we please remove this test now and break this out into a separate PR for separate consideration so we can go ahead and get this fix merged?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Collaborator

@alexanderjordanbaker alexanderjordanbaker left a comment

Choose a reason for hiding this comment

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

Thank you!

@alexanderjordanbaker alexanderjordanbaker merged commit 5da964e into apple:main Dec 2, 2025
1 check passed
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