Version 4 to Version 5 Migration Deprecations#221
Conversation
| /// JWTKeyCollection was introduced in v5 and replaces ``JWTSigners``. | ||
| /// | ||
| /// - Note: Please migrate over to ``JWTKeyCollection`` before updating to v5, though if you plan on remaining on v4, ``JWTSigners`` can continue to be used. | ||
| public actor JWTKeyCollection { | ||
| var signers = JWTSigners() |
There was a problem hiding this comment.
This is a lightweight implementation of the new JWTKeyCollection API falling back entirely on the now deprecated JWTSigners API.
| /// A transitionary protocol with sync and async requirements. | ||
| /// | ||
| /// This protocol should be dropped once you are finished migrating to v5, as it'll have been renamed back to ``JWTPayload``, but with a single async requirement. In order to support both versions v4 and v5 in a library, do not implement the requirements of ``JWTPayload`` as ``JWTSigner`` is no longer available in v5. | ||
| public protocol AsyncJWTPayload: Codable { | ||
| func verify<Algorithm: JWTAlgorithm>(using signer: Algorithm) throws | ||
|
|
||
| /// Verifies that the payload's claims are correct or throws an error. | ||
| func verify<Algorithm: JWTAlgorithm>(using algorithm: Algorithm) async throws | ||
| } |
There was a problem hiding this comment.
Because we can't mark protocol methods as deprecated, we define a new one that will be transitionary and support both sync and asynchronous modalities, ensuring compatibility with existing code paths, but also newer signing methods no matter which would be used.
There was a problem hiding this comment.
I think you can mark protocol methods as deprecated can't you?
There was a problem hiding this comment.
You can mark them, but you don't get any warnings telling you they are deprecated as a conformer (which makes sense since the method itself may be used by another protocol or just on its own by whoever is conforming to the protocol). I believe you do see them as a caller though, which is not as useful in this situation.
| @available(*, deprecated, message: "Please make sure Payload conforms to AsyncJWTPayload instead of JWTPayload before updating to v5.") | ||
| public func sign<Payload: JWTPayload>( | ||
| _ payload: Payload, | ||
| kid: JWKIdentifier? = nil | ||
| ) async throws -> String { | ||
| try signers.sign(payload, kid: kid) | ||
| } |
There was a problem hiding this comment.
This variant is marked as deprecated despite appearing on the new JWTKeyCollection to help push users to adopt AsyncJWTPayload wherever they use another payload type.
| /// A transitionary protocol with sync and async requirements. | ||
| /// | ||
| /// This protocol should be dropped once you are finished migrating to v5, as it'll have been renamed back to ``JWTPayload``, but with a single async requirement. In order to support both versions v4 and v5 in a library, do not implement the requirements of ``JWTPayload`` as ``JWTSigner`` is no longer available in v5. | ||
| public protocol AsyncJWTPayload: Codable { |
There was a problem hiding this comment.
As for the name, I'm open to alternatives. JWTPayloadV4 could work well since this variant of it only really exists in the v4 version of the codebase.
There was a problem hiding this comment.
Nah AsyncJWTPayload is fine, it matches with the rest of Vapor
| func payload<Payload>(as payload: Payload.Type, jsonDecoder: any JWTJSONDecoder) throws -> Payload | ||
| where Payload: AsyncJWTPayload | ||
| { | ||
| try jsonDecoder | ||
| .decode(Payload.self, from: .init(self.encodedPayload.base64URLDecodedBytes())) | ||
| } |
There was a problem hiding this comment.
This is a duplicate of the above variant to support the new protocol.
| jsonEncoder: any JWTJSONEncoder | ||
| ) throws -> String | ||
| where Payload: JWTPayload | ||
| where Payload: Encodable |
There was a problem hiding this comment.
Similarly, rather than duplicate this internal method, I loosened the protocol requirements to it works for both.
| public enum ECDSA: Sendable { | ||
| /// ECDSA.PublicKey was introduced in v5 and replaces ``ECDSAKey``. | ||
| /// | ||
| /// - Note: Please migrate over to ``ECDSA/PublicKey`` before updating to v5, though if you plan on remaining on v4, ``ECDSAKey`` can continue to be used. | ||
| public struct PublicKey<Curve: ECDSACurveType> { | ||
| let key: ECDSAKey | ||
| init(key: ECDSAKey) { self.key = key } |
There was a problem hiding this comment.
These are wrappers on the existing keys to support the new types a user would be forced into, wile not adding much new functionality. I added bare minimum support for all ECDSA public and private keys.
| class JWTKitMigrationTests: XCTestCase { | ||
| func testVerifyingCryptoKey() async throws { |
There was a problem hiding this comment.
An end to end test to validate everything is hooked up properly. Also tested the migrations in the app-store-server-library repo along with my own codebase.
| public typealias ES256PublicKey = ECDSA.PublicKey<P256> | ||
| public typealias ES256PrivateKey = ECDSA.PrivateKey<P256> | ||
|
|
||
| extension P384: ECDSACurveType, @unchecked @retroactive Sendable { |
There was a problem hiding this comment.
These will need versions for Swift 5 and Swift 6
There was a problem hiding this comment.
I thought it wouldn’t, because this version of the library would always be compiled with the Swift 5 language mode. Once v5 is used, then none of this is included.
There was a problem hiding this comment.
Ah, nvm, forgot retroactive didn’t exist pre swift 6 compiler, will update
There was a problem hiding this comment.
There was a problem hiding this comment.
Though I'm now wondering if its worth adding Sendable conformances at all, since the rest of the library didn't support it yet at all…
There was a problem hiding this comment.
I don't mind not having them tbh, anyone wanting a full sendable supported library can bump to v5
…version 4 to version 5
fba14c1 to
83afce0
Compare
|
Added a couple of brief comments. There's also a failing test we need to investigate |
0xTim
left a comment
There was a problem hiding this comment.
This looks good to me, I don't know if anyone wants to comment but this is probably good to merge soon
|
Still need to fix the tests, but let me get PRs for the other pieces in place before we merge so they can go in all at once |
This PR adds migration deprecations for many common APIs between version 4 and version 5 that should help most users (particularly those using ECDSA keys and standard JWTs) migrate from version 4 to version 5 by following warnings. Crucially, it allows libraries that rely on the same requirements (for instance, https://github.com/apple/app-store-server-library-swift and https://github.com/m-barthelemy/AcmeSwift) to allow for a range of versions such as
"4.14.0" ..< "6.0.0".If we agree with the proposed updates here, a few more things should be done:
4.14.0, so users can opt out of the warnings should they wish to.AsyncJWTPayloadtypealias back toJWTPayloadthat is itself deprecated, to fully support libraries that wish to be compatible with v4 and v5.jwtfor vapor to move folks away from signers and ontokeys(otherwise they are still forced into an all or nothing update since they will still be stuck withJWTSigners).Note that I didn't make compatibility APIs for every type — if someone feels they are necessary due to a library requiring them, I figured we could add more support in a future PR.