Establish secure account and managed AI backend
Provide the production foundation for Apple identity, immutable credits, referrals, integrity checks, managed providers, and hardened Docker deployment.
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
package com.osglab.account.features.auth
|
||||
|
||||
import com.nimbusds.jose.JWSAlgorithm
|
||||
import com.nimbusds.jose.crypto.ECDSAVerifier
|
||||
import com.nimbusds.jwt.SignedJWT
|
||||
import com.osglab.account.config.AppleConfig
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import java.security.KeyPairGenerator
|
||||
import java.security.interfaces.ECPublicKey
|
||||
import java.security.spec.ECGenParameterSpec
|
||||
import java.time.Clock
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
import java.util.Base64
|
||||
|
||||
class AppleClientSecretProviderTest : FunSpec({
|
||||
test("creates a verifiable short-lived ES256 Apple client secret") {
|
||||
val now = Instant.parse("2026-08-16T00:00:00Z")
|
||||
val keyPair = KeyPairGenerator.getInstance("EC").apply {
|
||||
initialize(ECGenParameterSpec("secp256r1"))
|
||||
}.generateKeyPair()
|
||||
val privateKeyPem = Base64.getMimeEncoder(64, "\n".toByteArray())
|
||||
.encodeToString(keyPair.private.encoded)
|
||||
.let { "-----BEGIN PRIVATE KEY-----\n$it\n-----END PRIVATE KEY-----" }
|
||||
val config = AppleConfig(
|
||||
teamId = "TEAM123",
|
||||
keyId = "KEY123",
|
||||
clientId = "com.example.ios",
|
||||
privateKeyPem = privateKeyPem,
|
||||
jwksUrl = "https://appleid.apple.com/auth/keys",
|
||||
tokenUrl = "https://appleid.apple.com/auth/token",
|
||||
revokeUrl = "https://appleid.apple.com/auth/revoke",
|
||||
)
|
||||
|
||||
val serialized = AppleClientSecretProvider(
|
||||
config,
|
||||
Clock.fixed(now, ZoneOffset.UTC),
|
||||
).create()
|
||||
val jwt = SignedJWT.parse(serialized)
|
||||
|
||||
jwt.header.algorithm shouldBe JWSAlgorithm.ES256
|
||||
jwt.header.keyID shouldBe "KEY123"
|
||||
jwt.verify(ECDSAVerifier(keyPair.public as ECPublicKey)) shouldBe true
|
||||
jwt.jwtClaimsSet.issuer shouldBe "TEAM123"
|
||||
jwt.jwtClaimsSet.subject shouldBe "com.example.ios"
|
||||
jwt.jwtClaimsSet.audience shouldBe listOf("https://appleid.apple.com")
|
||||
jwt.jwtClaimsSet.issueTime.toInstant() shouldBe now
|
||||
jwt.jwtClaimsSet.expirationTime.toInstant() shouldBe now.plusSeconds(300)
|
||||
}
|
||||
|
||||
test("rejects an EC key that is not Apple P-256") {
|
||||
val keyPair = KeyPairGenerator.getInstance("EC").apply {
|
||||
initialize(ECGenParameterSpec("secp384r1"))
|
||||
}.generateKeyPair()
|
||||
val privateKeyPem = Base64.getMimeEncoder(64, "\n".toByteArray())
|
||||
.encodeToString(keyPair.private.encoded)
|
||||
.let { "-----BEGIN PRIVATE KEY-----\n$it\n-----END PRIVATE KEY-----" }
|
||||
val config = AppleConfig(
|
||||
teamId = "TEAM123",
|
||||
keyId = "KEY123",
|
||||
clientId = "com.example.ios",
|
||||
privateKeyPem = privateKeyPem,
|
||||
jwksUrl = "https://appleid.apple.com/auth/keys",
|
||||
tokenUrl = "https://appleid.apple.com/auth/token",
|
||||
revokeUrl = "https://appleid.apple.com/auth/revoke",
|
||||
)
|
||||
|
||||
shouldThrow<AppleClientUnavailableException> {
|
||||
AppleClientSecretProvider(config).create()
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user