Generating JWT Tokens in Swift for the App Store Connect API
Using the App Store Connect API can significantly streamline workflows. For
example, I use it to onboard new TestFlight testers for my apps much faster than
through appstoreconnect.apple.com
. While Apple provides a Generating Tokens
for API Requests guide, this post shows how to create such tokens in Swift.
Generating JWT Tokens with JWTKit
The first step in using the API is to create JWT tokens. For this, I’ve used
vapor/jwt-kit
1.
Step 1: Define the Payload
Start by defining the payload structure expected by Apple:
/// A JWT payload structure for App Store Connect APIs.
public struct AppStoreConnectPayload: JWTPayload {
public let iss: IssuerClaim
public let iat: IssuedAtClaim
public let exp: ExpirationClaim
public let aud: AudienceClaim
public let scope: [String]?
/// Creates a new payload for App Store Connect.
public init(iss: String, iat: Date, exp: Date, aud: [String], scope: [String]? = nil) {
self.iss = IssuerClaim(value: iss)
self.iat = IssuedAtClaim(value: iat)
self.exp = ExpirationClaim(value: exp)
self.aud = AudienceClaim(value: aud)
self.scope = scope
}
/// Verifies that the payload is valid and not expired.
/// - Throws: An error if the payload is invalid or expired.
public func verify(using _: some JWTAlgorithm) throws {
try exp.verifyNotExpired()
}
}
Step 2: Generate the Token
Once the payload is ready, create the token. Below is an example using sample data (based on Apple’s guide) and a dummy private key. Be sure to replace these with your actual values:
let pem = """
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgqIuh45D/4x9KGZ9v
…
uJcVQDj14FbV2QLpTbrNtk82W6ZMJT+FAZaJsx5Xiu9u83rbeICgZy3G
-----END PRIVATE KEY-----
"""
let keys = JWTKeyCollection()
let kid = JWKIdentifier(string: "2X9R4HXF34")
let key = try ES256PrivateKey(pem: pem)
await keys.add(ecdsa: key, kid: kid)
let token = try await keys.sign(
payload,
kid: .init(string: keyIdentifier),
header: ["typ": "JWT"]
)
You now have a JWT token! 🎉
Using the Code as a Dependency
If you prefer not to write your own implementation, you can use the
dirtyhenry/swift-hoods
package I created. It provides a jwtFactory
dependency, compatible with pointfreeco/swift-dependencies
.
Here’s how to integrate it:
@Dependency(\.jwtFactory) var jwtFactory
And in your application’s single entry-point:
let jwtFactory = try LiveJWTFactory()
try await jwtFactory.addES256Key(pem: privateKey, keyIdentifier: keyIdentifier)
let newTransport = try withDependencies {
$0.jwtFactory = jwtFactory
} operation: {
return AppStoreConnectAPIAuthorizationTransport()
}
Additional Tips
- Managing Secrets. Storing private keys and secrets securely is crucial.
Check out the
GenericPasswordKeychainItem
class for handling keychain data on iOS and macOS. - Request Integration. To include the token with every API request, consider
using a dedicated
Transport
. This makes it easy to compose behaviors around aURLSession
instance. - Testing Tokens. Use the
date
dependency frompointfreeco/swift-dependencies
to simulate different expiration scenarios when testing token verification2.
With these steps, you can efficiently generate JWT tokens in Swift and streamline your use of the App Store Connect API. Happy coding!
-
One day, I would love to get rid of that dependency and use only
CryptoKit
to generate tokens. If you could help, let me know. ↩ -
Check out
JWTFactoryTests
indirtyhenry/swift-hoods
to see those tests. ↩