I am trying to generate access token accroding to https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens for apple sign in and revoke it after. However always getting "{"error":"invalid_client"}" what I am doing wrong?
` func validateTokens(completion: (([String: Any]?, Error?) -> Void)? = nil) {
guard let code = keychain["code"],
let userId = Bundle.main.bundleIdentifier
else {
log("Failed getting token")
return
}
let paramString: [String: Any]
do {
let key = try privateKey()
let token = try jwtSignedToken(ecSECp256rPrivateKey: key)
paramString = [
"client_id": userId, //"com.MyCompany.Name",
"client_secret": token,
"grant_type": "authorization_code",
]
} catch {
return
}
let url = URL(string: "https://appleid.apple.com/auth/token")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: paramString, options: .prettyPrinted)
} catch let error {
log("Account apple delete %@", error.localizedDescription)
completion?(nil, error)
}
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.dataTask(with: request as URLRequest) { (data, response, error) in
guard
let response = response as? HTTPURLResponse,
error == nil
else {
print("error", error ?? URLError(.badServerResponse))
return
}
guard (200...299) ~= response.statusCode else {
log("statusCode should be 2xx, but is %@", "\(response.statusCode)")
log("response = %@", "\(response)")
return
}
if let error = error {
log("%@", error.localizedDescription)
} else {
log("token just has generated")
}
}
task.resume()
}
}
extension Data {
init?(base64URLEncodedString: String) {
let unpadded =
base64URLEncodedString
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
let padCount: Int
switch unpadded.count % 4 {
case 0: padCount = 0
case 1: return nil
case 2: padCount = 2
case 3: padCount = 1
default: fatalError()
}
self.init(base64Encoded: String(unpadded + String(repeating: "=", count: padCount)))
}
var base64URLEncodedString: String {
let base64 = self.base64EncodedString()
return String(base64.split(separator: "=").first!)
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
}
}
func jwtHeader(kid: String) -> String {
struct Header: Codable {
var alg: String
var kid: String
}
let header = Header(alg: "ES256", kid: kid)
return (try? JSONEncoder().encode(header).base64URLEncodedString) ?? ""
}
func jwtPayload(iss: String, exp: Date) -> String {
// We round the expiry date up because it’s not clear how servers are going
// to cope with fractional values.
let exp = Date(timeIntervalSinceReferenceDate: exp.timeIntervalSinceReferenceDate.rounded(.up))
struct Payload: Codable {
var iss: String
var exp: Date
var iat: Int
var aud: String
var sub: String
}
let payload = Payload(iss: iss, exp: exp, iat: 1437179036, aud: "https://appleid.apple.com", sub: Keys.appBundleId)
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .secondsSince1970
return (try? encoder.encode(payload).base64URLEncodedString) ?? ""
}
func jwtSignedToken(
kid: String = "keyId", iss: String = "teamId", exp: Date = Date().addingTimeInterval(120),
ecSECp256rKeyK keyK: Data
) throws -> String {
let header = jwtHeader(kid: kid)
let payload = jwtPayload(iss: iss, exp: exp)
let signingInput = "\(header).\(payload)"
let privateKey = try P256.Signing.PrivateKey(rawRepresentation: keyK)
let sig = try privateKey.signature(for: Data(signingInput.utf8)).rawRepresentation
return "\(signingInput).\(sig.base64URLEncodedString)"
}
func privateKey() throws -> SecKey {
let privateKeyRawBytes = [UInt8]([
///generate like in https://developer.apple.com/forums/thread/681267
])
var error: Unmanaged<CFError>?
let privateSecKey = SecKeyCreateWithData(
Data(privateKeyRawBytes) as CFData,
[
kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
] as NSDictionary, &error)
return privateSecKey!
}
func jwtSignedToken(
kid: String = "keyId", iss: String = teamId", exp: Date = Date().addingTimeInterval(120),
ecSECp256rPrivateKey privateKey: SecKey
) throws -> String {
var errorCF: Unmanaged<CFError>?
guard let keyData = SecKeyCopyExternalRepresentation(privateKey, &errorCF) as Data? else {
throw errorCF!.takeRetainedValue()
}
let keyK = keyData.suffix(0x20)
return try jwtSignedToken(kid: kid, iss: iss, exp: exp, ecSECp256rKeyK: keyK)
}