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()
}
For any one who wants to implement account deletion with flutter , swift or react .
Before you continue reading , take a moment to read the api requirement of two urls you will need to complete the revoking process.
The link below shows how to do this with iOS(swift) and firebase using firebase functions .And by reading it you can implement a solution with the other SDKs i.e flutter , react-native etc.
Implementing account deletion with apple and firebase
But in an attempt to summarise :-
2.This you will use to create a Jwt using popular libraries of your own choosing .which then becomes the client_secret parameter for apple's api end point .
4.Finally you call the revoke endpoint with the parameters from the above processes .
NB: I believe the solution above is so far the best way assuming that you secure your key on the server side. and unless you have a way of scrambling the key you should not attempt to store this in your project(or app).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With