-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18 from occamLab/ayush/firebase-integration
Integrate Firebase SSO and User Profiles
- Loading branch information
Showing
335 changed files
with
50,542 additions
and
2,539 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// | ||
// AuthManager.swift | ||
// MusicalCaneGame | ||
// | ||
// Created by occamlab on 11/12/24. | ||
// Copyright © 2024 occamlab. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
import FirebaseCore | ||
import FirebaseAuth | ||
import CryptoKit | ||
import AuthenticationServices | ||
import SwiftUI | ||
|
||
class AuthManager: NSObject, ObservableObject, ASAuthorizationControllerDelegate { | ||
public static var shared = AuthManager() | ||
private var currentNonce: String? | ||
|
||
@Published var currentUID: String? | ||
@Published var currentEmail: String? | ||
|
||
private let firebaseAuth = Auth.auth() | ||
|
||
private override init() { | ||
currentUID = firebaseAuth.currentUser?.uid | ||
currentEmail = firebaseAuth.currentUser?.email | ||
super.init() | ||
createAuthListener() | ||
} | ||
|
||
private func createAuthListener() { | ||
firebaseAuth.addStateDidChangeListener() { (auth, user) in | ||
self.currentUID = user?.uid | ||
print("currentUID \(self.currentUID ?? "nil")") | ||
print("currentEmail \(user?.email)") | ||
print("name \(user?.displayName)") | ||
|
||
// Ensure that a document exists for the current user within | ||
// firebase | ||
if self.currentUID != nil { | ||
FirebaseManager.shared.checkOrAppendInstructorUID(instructorUID: self.currentUID!) | ||
} | ||
} | ||
} | ||
|
||
private func randomNonceString(length: Int = 32) -> String { | ||
precondition(length > 0) | ||
var randomBytes = [UInt8](repeating: 0, count: length) | ||
let errorCode = SecRandomCopyBytes(kSecRandomDefault, randomBytes.count, &randomBytes) | ||
if errorCode != errSecSuccess { | ||
fatalError( | ||
"Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)" | ||
) | ||
} | ||
|
||
let charset: [Character] = | ||
Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._") | ||
|
||
let nonce = randomBytes.map { byte in | ||
// Pick a random character from the set, wrapping around if needed. | ||
charset[Int(byte) % charset.count] | ||
} | ||
|
||
return String(nonce) | ||
} | ||
|
||
private 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 | ||
} | ||
|
||
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.performRequests() | ||
} | ||
|
||
/// A callback function that indicates authentication was successful. | ||
/// - Parameters: | ||
/// - controller: the controller that was used for authentication | ||
/// - authorization: the authorization generated by the Apple authentication procedure | ||
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, including the user's full name. | ||
let credential = OAuthProvider.appleCredential(withIDToken: idTokenString, | ||
rawNonce: nonce, | ||
fullName: appleIDCredential.fullName) | ||
// Sign in with Firebase. | ||
Auth.auth().signIn(with: credential) { (authResult, error) in | ||
if let error = error { | ||
// 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) | ||
return | ||
} | ||
// User is signed in to Firebase with Apple. | ||
// ... | ||
} | ||
} | ||
} | ||
|
||
/// A callback function that is called when the authentication request failed | ||
/// - Parameters: | ||
/// - controller: the controller that requested the authentication with Apple | ||
/// - error: the error that occurred | ||
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { | ||
// Handle error. | ||
print("Sign in with Apple errored: \(error)") | ||
} | ||
} | ||
|
||
/// A wrapper for the "Sign in with Apple" button for SwiftUI | ||
struct SignInWithApple: UIViewRepresentable { | ||
/// Creates the UIView as part of the `UIViewRepresentable` protocol | ||
/// - Parameter context: the context for creating the UIView | ||
/// - Returns: the UIView | ||
func makeUIView(context: Context) -> ASAuthorizationAppleIDButton { | ||
return ASAuthorizationAppleIDButton() | ||
} | ||
|
||
/// | ||
/// - Parameters: | ||
/// - uiView: a handle to the ASAuthorizationAppleIDButton | ||
/// - context: the context in which the update is occurring. | ||
func updateUIView(_ uiView: ASAuthorizationAppleIDButton, context: Context) { | ||
// no updates | ||
} | ||
} |
Oops, something went wrong.