-
-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Version 4 to Version 5 Migration Deprecations #221
base: v4
Are you sure you want to change the base?
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a duplicate of the above variant to support the new protocol.
where Payload: JWTPayload | ||
where Payload: Encodable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly, rather than duplicate this internal method, I loosened the protocol requirements to it works for both.
Sources/JWTKit/ECDSA/ECDSAKey.swift
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These will need versions for Swift 5 and Swift 6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, nvm, forgot retroactive didn’t exist pre swift 6 compiler, will update
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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…
…version 4 to version 5
fba14c1
to
83afce0
Compare
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.AsyncJWTPayload
typealias back toJWTPayload
that is itself deprecated, to fully support libraries that wish to be compatible with v4 and v5.jwt
for 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.