blog icon indicating copy to clipboard operation
blog copied to clipboard

How to generate JWT token for App Store Connect API in Swift

Open onmyway133 opened this issue 3 years ago • 1 comments

Use JWTKit and code from AppStoreConnect library

import JWTKit

public struct Credential {
    let issuerId: String
    let privateKeyId: String
    let privateKey: String

    public init(
        issuerId: String,
        privateKeyId: String,
        privateKey: String
    ) {
        self.issuerId = issuerId
        self.privateKeyId = privateKeyId
        self.privateKey = privateKey
    }

    func generateJWT() throws -> String {
        guard let signer = try? JWTSigner.es256(
            key: ECDSAKey.private(pem: privateKey))
        else {
            throw AppStoreConnectError.invalidJWT
        }

        let payload = Payload(
            issueID: IssuerClaim(value: issuerId),
            expiration: ExpirationClaim(
                value: Date(
                    timeInterval: 2 * 60,
                    since: Date()
                )
            ),
            audience: AudienceClaim(
                value: "appstoreconnect-v1"
            )
        )

        guard let jwt = try? signer.sign(
            payload,
            kid: JWKIdentifier(string: privateKeyId)
        ) else {
            throw AppStoreConnectError.invalidJWT
        }

        return jwt
    }
}

struct Payload: JWTPayload {
    private enum CodingKeys: String, CodingKey {
        case issueID = "iss"
        case expiration = "exp"
        case audience = "aud"
    }

    var issueID: IssuerClaim
    var expiration: ExpirationClaim
    var audience: AudienceClaim

    func verify(using signer: JWTSigner) throws {
        try expiration.verifyNotExpired()
    }
}

onmyway133 avatar Feb 16 '22 13:02 onmyway133

Thanks! This saved me a lot of time! Only one change I had to make was to add the bundleID or "bid" to the Payload struct, which is required by Apple.

struct Payload: JWTPayload {
    private enum CodingKeys: String, CodingKey {
        case issueID = "iss"
        case expiration = "exp"
        case audience = "aud"
        case bundleId = "bid"
    }
    
    var issueID: IssuerClaim
    var expiration: ExpirationClaim
    var audience: AudienceClaim
    var bundleId: String
    
    func verify(using signer: JWTSigner) throws {
        try expiration.verifyNotExpired()
    }
}

dominiquemiller avatar Jan 26 '23 02:01 dominiquemiller