Created
December 2, 2020 15:43
-
-
Save fredriccliver/a4863ac9b718c51de92787aa409f4f11 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Created by Fredric Cliver on 2020/10/13. | |
// | |
import UIKit | |
import Firebase | |
import GoogleSignIn | |
import CryptoKit | |
import AuthenticationServices | |
class LoginViewController: UIViewController, ASAuthorizationControllerPresentationContextProviding { | |
fileprivate var currentNonce: String? | |
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { | |
return self.view.window! | |
} | |
@IBAction func touchedSignInWithAppleButton(_ sender: MyAuthorizationAppleIDButton) { | |
startSignInWithAppleFlow() | |
} | |
var handle: AuthStateDidChangeListenerHandle? | |
@IBOutlet var signInWithGoogleButton: UIButton! | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
} | |
override func viewDidAppear(_ animated: Bool) { | |
GIDSignIn.sharedInstance()?.presentingViewController = self | |
// GIDSignIn.sharedInstance().signIn() | |
handle = Auth.auth().addStateDidChangeListener() { (auth, user) in | |
if user != nil { | |
print("logged in") | |
self.passUserToMainView() | |
} | |
} | |
} | |
func passUserToMainView() { | |
let storyboard = UIStoryboard(name: Constants.viewIDs.MainStoryboard.rawValue, bundle: nil) | |
let vc = storyboard.instantiateViewController(withIdentifier: Constants.viewIDs.MainView.rawValue) | |
vc.modalPresentationStyle = .fullScreen | |
self.present(vc, animated: true) | |
} | |
@IBAction func onClickSignInGoogle(_ sender: UIButton) { | |
GIDSignIn.sharedInstance().signIn() | |
} | |
override func viewWillAppear(_ animated: Bool) { | |
} | |
deinit { | |
if let handle = handle { | |
Auth.auth().removeStateDidChangeListener(handle) | |
} | |
} | |
} | |
// MARK: - extension for Apple signing in | |
extension LoginViewController: ASAuthorizationControllerDelegate { | |
@objc @available(iOS 13, *) | |
func startSignInWithAppleFlow() { | |
let nonce = randomNonceString() | |
currentNonce = nonce | |
let appleIDProvider = ASAuthorizationAppleIDProvider() | |
let request = appleIDProvider.createRequest() | |
request.requestedScopes = [.fullName, .email] | |
request.nonce = sha256(nonce) | |
let authorizationController = ASAuthorizationController(authorizationRequests: [request]) | |
authorizationController.delegate = self | |
authorizationController.presentationContextProvider = self | |
authorizationController.performRequests() | |
} | |
//MARK: - succeeded on signing in with Apple | |
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) { | |
if let appleIDCredential = authorization.credential as? 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 { | |
print("Unable to fetch identity token") | |
return | |
} | |
guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else { | |
print("Unable to serialize token string from data: \(appleIDToken.debugDescription)") | |
return | |
} | |
// Initialize a Firebase credential. | |
let credential = OAuthProvider.credential(withProviderID: "apple.com", idToken: idTokenString, rawNonce: nonce) | |
// Sign in with Firebase. | |
Auth.auth().signIn(with: credential) { (authResult, error) in | |
if (error != nil) { | |
// Error. If error.code == .MissingOrInvalidNonce, make sure | |
// you're sending the SHA256-hashed nonce as a hex string with | |
// your request to Apple. | |
// print(error!.localizedDescription) | |
ToastManager().showToast(message: error!.localizedDescription) | |
return | |
} | |
// User is signed in to Firebase with Apple. | |
self.passUserToMainView() | |
} | |
} | |
} | |
//MARK: - failed on signing in with Apple | |
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { | |
// Handle error. | |
print("Sign in with Apple errored: \(error)") | |
} | |
} | |
// MARK: - Encrypt Functions | |
extension LoginViewController { | |
// Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce | |
private func randomNonceString(length: Int = 32) -> String { | |
precondition(length > 0) | |
let charset: Array<Character> = | |
Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._") | |
var result = "" | |
var remainingLength = length | |
while remainingLength > 0 { | |
let randoms: [UInt8] = (0 ..< 16).map { _ in | |
var random: UInt8 = 0 | |
let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random) | |
if errorCode != errSecSuccess { | |
fatalError("Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)") | |
} | |
return random | |
} | |
randoms.forEach { random in | |
if remainingLength == 0 { | |
return | |
} | |
if random < charset.count { | |
result.append(charset[Int(random)]) | |
remainingLength -= 1 | |
} | |
} | |
} | |
return result | |
} | |
@available(iOS 13, *) | |
private func sha256(_ input: String) -> String { | |
let inputData = Data(input.utf8) | |
let hashedData = SHA256.hash(data: inputData) | |
let hashString = hashedData.compactMap { | |
return String(format: "%02x", $0) | |
}.joined() | |
return hashString | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment