Apple Notary API client for Kotlin.
Notarize your macOS application from Kotlin code without dependency on Xcode nor macOS.
This library is available on Space Packages, you will need to add this Maven repository to your Gradle configuration:
import java.net.URI
repositories {
maven {
url = URI("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies")
}
}
Then add the dependency
implementation("org.jetbrains:apple-notary-api-kotlin-client:$version")
You will also need to provide a Ktor engine implementation, for example if you want to use the CIO engine:
implementation("io.ktor:ktor-client-cio:$ktorVersion")
You will first need to create an API Key for App Store Connect API, see Apple's Documentation . While doing this process make sure to save:
- your issuer ID from the API Keys page in App Store Connect (e.g.
57246542-96fe-1a63-e053-0824d011072a
) - your private key ID from App Store Connect (e.g.
2X9R4HXF34
) - your private key
.p8
file you can download from the API Keys page in App Store Connect
Once you have these information, you can setup a credential object:
import com.jetbrains.notary.NotaryClientV2
import com.jetbrains.notary.auth.AppStoreConnectAPIKey
val credentials = AppStoreConnectAPIKey(
issuerId = "your-issuer-id",
keyId = "your-private-key-id",
privateKey = "your-private-key-file-content",
)
val client = NotaryClientV2(credentials)
// ... now you can use your client!
// For example retrieving your previous submissions:
val submissions = client.getPreviousSubmissions()
println(submissions)
apple-notary-api-kotlin-client
provides some extension functions that cover basic use cases of
Apple Notary API.
notarize
(or its blocking equivalent notarizeBlocking
) will cover the basic use case of "I want to notarize a file,
wait for the notarization to complete, get status and logs of the submission result when completed".
Under the hood, it will do the following:
- Issue a notarization submission for the specified file
- Upload the specified file to the location asked by Apple Notary API
- Poll Apple Notary API until the submission completes (or timeout)
- Note: with configuration such as
ignoreServerError
andignoreTimeoutExceptions
set totrue
(set by default), polling will be hardened not to fail on usual false negative situations
- Note: with configuration such as
- Fetch logs of the completed submission (whether it is a success or a failure)
- Return status and logs of the completed submission
import com.jetbrains.notary.NotaryClientV2
import com.jetbrains.notary.auth.AppStoreConnectAPIKey
import kotlin.time.Duration.Companion.minutes
val credentials = AppStoreConnectAPIKey(
issuerId = "your-issuer-id",
keyId = "your-private-key-id",
privateKey = "your-private-key-file-content",
)
val notaryApiClient = NotaryClientV2(credentials)
val notarizationResult = notaryApiClient.notarizeBlocking(file, StatusPollingConfiguration(
timeout = 30.minutes,
pollingPeriod = 1.minutes,
ignoreServerError = true,
ignoreTimeoutExceptions = true,
))
val json = Json { prettyPrint = true }
println("Notarization logs:\n${json.encodeToString(notarizationResult.logs)}")
when (notarizationResult.status) {
SubmissionResponse.Status.ACCEPTED -> println("Notarization of $file successful")
SubmissionResponse.Status.IN_PROGRESS, null -> error("Timed out polling status of $file notarization submission")
SubmissionResponse.Status.INVALID, SubmissionResponse.Status.REJECTED -> error("Notarization of $file failed, see logs above")
}
You are free to use your own ktor
client implementation and thus configuration.
We provide NotaryClientV2.defaultHttpClient
to allow your client configuration to extend the default one.
import com.jetbrains.notary.NotaryClientV2
import com.jetbrains.notary.auth.AppStoreConnectAPIKey
import kotlin.time.Duration.Companion.minutes
val credentials = AppStoreConnectAPIKey(
issuerId = "your-issuer-id",
keyId = "your-private-key-id",
privateKey = "your-private-key-file-content",
)
val myOwnClient = NotaryClientV2.defaultHttpClient.config { // keep all the default configuration
install(HttpRequestRetry) {
retryOnExceptionOrServerErrors(maxRetries = 10) // but increase the number of retry of the default configuration
constantDelay(millis = 1.minutes.inWholeMilliseconds) // but change the default exponential retry by a constant one
}
}
val notaryApiClient = NotaryClientV2(credentials, httpClient = myOwnClient)
This project is distributed under the Apache 2.0 license.
Special thanks to the people that built Rust library apple-codesign that inspired this work.