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-kit1.

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 a URLSession instance.
  • Testing Tokens. Use the date dependency from pointfreeco/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!

  1. 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.

  2. Check out JWTFactoryTests in dirtyhenry/swift-hoods to see those tests.