Skip to content

Instantly share code, notes, and snippets.

@hulk510
Last active June 28, 2023 02:37
Show Gist options
  • Save hulk510/7f13636d79d823adba609304b61bd040 to your computer and use it in GitHub Desktop.
Save hulk510/7f13636d79d823adba609304b61bd040 to your computer and use it in GitHub Desktop.
SwiftUI SignInWithApple
import SwiftUI
import Firebase
import FirebaseAuth
class AuthState: ObservableObject {
func signIn(credential: OAuthCredential) async throws {
_ = try await auth.signIn(with: credential)
}
func deleteUser(code: String) async throws {
try await auth.revokeToken(withAuthorizationCode: code)
...
}
}
import Foundation
import CryptoKit
struct CryptoUtils {
enum CryptoError: Error {
case nonceGenerationFailed
}
static func sha256(_ input: String) -> String {
let inputData = Data(input.utf8)
let hashedData = SHA256.hash(data: inputData)
let hashString = hashedData.compactMap {
String(format: "%02x", $0)
}.joined()
return hashString
}
static func randomNonceString(length: Int = 32) throws -> String {
precondition(length > 0)
let charset: [Character] =
Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
var result = ""
var remainingLength = length
while remainingLength > 0 {
let randoms: [UInt8] = try (0 ..< 16).map { _ in
var random: UInt8 = 0
let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random)
if errorCode != errSecSuccess {
throw CryptoError.nonceGenerationFailed
}
return random
}
randoms.forEach { random in
if remainingLength == 0 {
return
}
if random < charset.count {
result.append(charset[Int(random)])
remainingLength -= 1
}
}
}
return result
}
}
import SwiftUI
import FirebaseAuth
import AuthenticationServices
// ログインか退会かのenum
enum SignInWithAppleAction {
case signIn
case delete
}
struct AppleSignInButton: View {
var action: SignInWithAppleAction
@EnvironmentObject var auth: AuthState
@State var currentNonce: String?
var body: some View {
SignInWithAppleButton(onRequest: {
request in
request.requestedScopes = [.fullName, .email]
do {
let nonce = try CryptoUtils.randomNonceString()
currentNonce = nonce
request.nonce = CryptoUtils.sha256(nonce)
} catch {
print(error.localizedDescription)
}
}, onCompletion: {
result in
switch result {
case .success(let authResults):
switch authResults.credential {
case let appleIDCredential as ASAuthorizationAppleIDCredential:
if action == .delete {
deleteAccount(appleIDCredential)
} else {
signIn(appleIDCredential)
}
default:
break
}
default:
break
}
})
.frame(maxHeight: 40)
.padding(.horizontal, 30)
}
// 退会処理
func deleteAccount(_ appleIDCredential: ASAuthorizationAppleIDCredential) {
guard let _ = currentNonce else {
fatalError("Invalid state: A login callback was received, but no login request was sent.")
}
guard let appleAuthCode = appleIDCredential.authorizationCode else {
print("Unable to fetch authorization code")
return
}
guard let authCodeString = String(data: appleAuthCode, encoding: .utf8) else {
print("Unable to serialize auth code string from data: \(appleAuthCode.debugDescription)")
return
}
Task {
do {
try await auth.deleteUser(code: authCodeString)
} catch {
print(error.localizedDescription)
}
}
}
// サインイン時の処理
func signIn(_ appleIDCredential: ASAuthorizationAppleIDCredential) {
guard let nonce = currentNonce else {
fatalError("Invalid state: A login callback was received, but no login request was sent.")
}
guard let appleIDToken = appleIDCredential.identityToken else {
fatalError("Invalid state: A login callback was received, but no login request was sent.")
}
guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
return
}
let credential = OAuthProvider.credential(withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce)
Task {
do {
try await auth.signIn(credential: credential)
} catch {
print(error.localizedDescription)
}
}
}
}
struct AppleSignInButton_Previews: PreviewProvider {
static var previews: some View {
AppleSignInButton(action: .signIn)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment