How do I structure a swift POST request to satisfy the Sign In With Apple token revocation requirements?
I am not sure what form-data, client_id, client_secret, token, or token_type_hint are supposed to be. I was able to implement Sign in With Apple to create a user, but very lost on the revocation part of this.
I am looking to perform this client-side with Swift, as that would be the most convenient. Firebase may be developing a solution built into their SDK, but not sure if that is a one-size fits all solution for developers using Firebase.
https://developer.apple.com/documentation/sign_in_with_apple/revoke_tokens#url
Edit: source of requirements https://developer.apple.com/support/offering-account-deletion-in-your-app
The following functions live in the same class (ViewModel). The first does my login/registration flow. Some of the code is related to Firebase flows and can be largely ignored, but you can see I grab the token string and nonce for client_secret. The second function resembles the POST request for token revocation (which gets called from a delete account function not shown). Has anyone had success with this approach/boilerplate?
Testing the token revocation method below with a button tap in my app returns status code 400. I cannot revoke tokens with this method, and I am not sure what else to do.
public func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
// Sign in using Firebase Auth
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
guard let nonce = currentNonce else {
print("Invalid state: A login callback was received, but no login request was sent.")
return
}
// JWT
guard let appleIDToken = appleIDCredential.identityToken else {
print("Unable to fetch identity token")
return
}
guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
print("Unable to serialize token string from data")
return
}
let credential = OAuthProvider.credential(withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce)
Auth.auth().signIn(with: credential) { result, error in
if error != nil {
print(error!.localizedDescription)
return
}
else { // successful auth, we can now check if its a login or a registration
guard let user = Auth.auth().currentUser else {
print("No user was found.")
return
}
let db = Firestore.firestore()
let docRef = db.collection("Users").document(("\(user.uid)"))
docRef.getDocument{ (document, error) in
if let document = document, document.exists {
// User is just logging in, their db store exists
print("Successful Apple login.")
// Token revocation requirements
self.clientSecret = nonce
self.appleToken = idTokenString
if (self.isDeletingAccount == true) {
print("Is deleting account.")
self.isReauthenticated = true
}
self.isLogged = true
}
else { // document does not exist! we are registering a new user
db.collection("Users").document("\(user.uid)").setData([
"name": "\(appleIDCredential.fullName?.givenName ?? "")"
])
print("Successful Apple registration.")
self.clientSecret = nonce
self.appleToken = idTokenString
self.isLogged = true
}
}
}
}
}
}
// POST request to revoke user's Apple token
func appleAuthTokenRevoke(completion: (([String: Any]?, Error?) -> Void)? = nil) {
let paramString: [String : Any] = [
"client_id": Bundle.main.bundleIdentifier!, //"com.MyCompany.Name",
"client_secret": self.clientSecret,
"token": self.appleToken,
"token_type_hint": "access_token"
]
let url = URL(string: "https://appleid.apple.com/auth/revoke")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: paramString, options: .prettyPrinted)
}
catch let error {
print(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 {
print("statusCode should be 2xx, but is \(response.statusCode)")
print("response = \(response)")
return
}
if let error = error {
print(error)
}
else {
print("deleted accont")
}
}
task.resume()
}