Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions DevLog/Infra/Service/AuthService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import FirebaseFirestore

final class AuthService {
private let store = Firestore.firestore()
private let logger = Logger(category: "AuthService")

var uid: String? {
Auth.auth().currentUser?.uid
Expand All @@ -20,13 +21,25 @@ final class AuthService {
}

func getProviderID() async throws -> String? {
guard let uid = uid else { return nil }
logger.info("Fetching current provider ID")

guard let uid = uid else {
logger.warning("No user ID available")
return nil
}

let document = try await store
.collection("users/\(uid)/userData")
.document("info")
.getDocument()
do {
let document = try await store
.collection("users/\(uid)/userData")
.document("info")
.getDocument()

return document.data()?["currentProvider"] as? String
let providerID = document.data()?["currentProvider"] as? String
logger.info("Successfully fetched provider ID: \(providerID ?? "nil")")
return providerID
} catch {
logger.error("Failed to fetch provider ID", error: error)
throw error
}
}
}
48 changes: 35 additions & 13 deletions DevLog/Infra/Service/PushNotificationService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,32 @@ import FirebaseFirestore

final class PushNotificationService {
private let store = Firestore.firestore()
private let logger = Logger(category: "PushNotificationService")

/// 푸시 알림 On/Off 설정
func fetchPushNotificationEnabled() async throws -> Bool {
logger.info("Fetching push notification enabled status")

guard let uid = Auth.auth().currentUser?.uid else {
logger.error("User not authenticated")
throw AuthError.notAuthenticated
}

let settingsRef = store.document("users/\(uid)/userData/settings")
let doc = try await settingsRef.getDocument()
do {
let settingsRef = store.document("users/\(uid)/userData/settings")
let doc = try await settingsRef.getDocument()

if let allowPush = doc.data()?["allowPushNotification"] as? Bool { return allowPush }
if let allowPush = doc.data()?["allowPushNotification"] as? Bool {
logger.info("Push notification enabled: \(allowPush)")
return allowPush
}

throw FirestoreError.dataNotFound("allowPushNotification")
logger.error("Push notification setting not found")
throw FirestoreError.dataNotFound("allowPushNotification")
} catch {
logger.error("Failed to fetch push notification status", error: error)
throw error
}
}

/// 푸시 알림 시간 설정
Expand All @@ -47,23 +60,32 @@ final class PushNotificationService {

/// 푸시 알림 설정 업데이트
func updatePushNotificationSettings(isEnabled: Bool, components: DateComponents) async throws {
logger.info("Updating push notification settings - enabled: \(isEnabled)")

guard let uid = Auth.auth().currentUser?.uid else {
logger.error("User not authenticated")
throw AuthError.notAuthenticated
}

let settingsRef = store.document("users/\(uid)/userData/settings")
do {
let settingsRef = store.document("users/\(uid)/userData/settings")

var dict: [String: Any] = ["allowPushNotification": isEnabled]
var dict: [String: Any] = ["allowPushNotification": isEnabled]

if let hour = components.hour {
dict["pushNotificationHour"] = hour
}
if let hour = components.hour {
dict["pushNotificationHour"] = hour
}

if let minute = components.minute {
dict["pushNotificationMinute"] = minute
}
if let minute = components.minute {
dict["pushNotificationMinute"] = minute
}

try await settingsRef.setData(dict, merge: true)
try await settingsRef.setData(dict, merge: true)
logger.info("Successfully updated push notification settings")
} catch {
logger.error("Failed to update push notification settings", error: error)
throw error
}
}

/// 푸시 알림 기록 요청
Expand Down
43 changes: 27 additions & 16 deletions DevLog/Infra/Service/SocialLogin/AppleAuthenticationService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,29 @@ final class AppleAuthenticationService: AuthenticationService {
private let messaging = Messaging.messaging()
private var user: User? { Auth.auth().currentUser }
private let providerID = AuthProviderID.apple
private let logger = Logger(category: "AppleAuthService")

func signIn() async throws -> AuthenticationDataResponse {
let response = try await authenticateWithAppleAsync()
logger.info("Starting Apple sign in")

let nonce = response.nonce
let credential = response.credential
let authorizationCode = response.authorizationCode
let idTokenString = response.idTokenString

// Firebase Function을 통해 customToken 요청
let customToken = try await requestAppleCustomToken(
idToken: idTokenString,
authorizationCode: authorizationCode
)

// customToken으로 Firebase 로그인
let result = try await Auth.auth().signIn(withCustomToken: customToken)
do {
let response = try await authenticateWithAppleAsync()

let nonce = response.nonce
let credential = response.credential
let authorizationCode = response.authorizationCode
let idTokenString = response.idTokenString

// Firebase Function을 통해 customToken 요청
logger.debug("Requesting custom token from Firebase Function")
let customToken = try await requestAppleCustomToken(
idToken: idTokenString,
authorizationCode: authorizationCode
)

// customToken으로 Firebase 로그인
logger.debug("Signing in with custom token")
let result = try await Auth.auth().signIn(withCustomToken: customToken)

let changeRequest = result.user.createProfileChangeRequest()
var displayName: String?
Expand Down Expand Up @@ -72,9 +78,14 @@ final class AppleAuthenticationService: AuthenticationService {
try await result.user.link(with: appleCredential)
}

let fcmToken = try await messaging.token()
let fcmToken = try await messaging.token()

return result.user.toData(providerID: .apple, fcmToken: fcmToken)
logger.info("Successfully signed in with Apple")
return result.user.toData(providerID: .apple, fcmToken: fcmToken)
} catch {
logger.error("Failed to sign in with Apple", error: error)
throw error
}
}

func signOut(_ uid: String) async throws {
Expand Down
98 changes: 65 additions & 33 deletions DevLog/Infra/Service/SocialLogin/GithubAuthenticationService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,23 @@ final class GithubAuthenticationService: NSObject, AuthenticationService {
private let messaging = Messaging.messaging()
private var user: User? { Auth.auth().currentUser }
private let providerID = AuthProviderID.gitHub
private let logger = Logger(category: "GithubAuthService")

func signIn() async throws -> AuthenticationDataResponse {
// 1. GitHub OAuth 로그인 요청
let authorizationCode = try await requestAuthorizationCode()

// 2. Firebase Functions를 통해 customToken 발급 요청
let (accessToken, customToken) = try await requestTokens(authorizationCode: authorizationCode)
logger.info("Starting GitHub sign in")

// 3. Firebase 로그인
let result = try await Auth.auth().signIn(withCustomToken: customToken)
do {
// 1. GitHub OAuth 로그인 요청
logger.debug("Requesting authorization code")
let authorizationCode = try await requestAuthorizationCode()

// 2. Firebase Functions를 통해 customToken 발급 요청
logger.debug("Requesting tokens from Firebase Function")
let (accessToken, customToken) = try await requestTokens(authorizationCode: authorizationCode)

// 3. Firebase 로그인
logger.debug("Signing in with custom token")
let result = try await Auth.auth().signIn(withCustomToken: customToken)

// 4. Firebase Auth 사용자 프로필 업데이트
let githubUser = try await requestUserProfile(accessToken: accessToken)
Expand All @@ -45,13 +52,18 @@ final class GithubAuthenticationService: NSObject, AuthenticationService {
try await result.user.link(with: credential)
}

let fcmToken = try await messaging.token()

return result.user.toData(
providerID: .gitHub,
fcmToken: fcmToken,
accessToken: accessToken
)
let fcmToken = try await messaging.token()

logger.info("Successfully signed in with GitHub")
return result.user.toData(
providerID: .gitHub,
fcmToken: fcmToken,
accessToken: accessToken
)
} catch {
logger.error("Failed to sign in with GitHub", error: error)
throw error
}
}

func signOut(_ uid: String) async throws {
Expand Down Expand Up @@ -79,36 +91,56 @@ final class GithubAuthenticationService: NSObject, AuthenticationService {
}

func link(uid: String, email: String) async throws {
let tokensRef = store.document("users/\(uid)/userData/tokens")
let authorizationCode = try await requestAuthorizationCode()
let (accessToken, _) = try await requestTokens(authorizationCode: authorizationCode)
logger.info("Linking GitHub account for user: \(uid)")

do {
let tokensRef = store.document("users/\(uid)/userData/tokens")
let authorizationCode = try await requestAuthorizationCode()
let (accessToken, _) = try await requestTokens(authorizationCode: authorizationCode)

let githubUser = try await requestUserProfile(accessToken: accessToken)
let githubUser = try await requestUserProfile(accessToken: accessToken)

guard let githubEmail = githubUser.email else {
try await revokeAccessToken(accessToken: accessToken)
throw EmailFetchError.emailNotFound
}
guard let githubEmail = githubUser.email else {
logger.error("GitHub email not found")
try await revokeAccessToken(accessToken: accessToken)
throw EmailFetchError.emailNotFound
}

if githubEmail != email {
try await revokeAccessToken(accessToken: accessToken)
throw EmailFetchError.emailMismatch
}
if githubEmail != email {
logger.error("Email mismatch - Expected: \(email), Got: \(githubEmail)")
try await revokeAccessToken(accessToken: accessToken)
throw EmailFetchError.emailMismatch
}

try await tokensRef.setData(["githubAccessToken": accessToken], merge: true)
try await tokensRef.setData(["githubAccessToken": accessToken], merge: true)

let credential = OAuthProvider.credential(providerID: AuthProviderID.gitHub, accessToken: accessToken)
try await user?.link(with: credential)
let credential = OAuthProvider.credential(providerID: AuthProviderID.gitHub, accessToken: accessToken)
try await user?.link(with: credential)

logger.info("Successfully linked GitHub account")
} catch {
logger.error("Failed to link GitHub account", error: error)
throw error
}
}

func unlink(_ uid: String) async throws {
try await revokeAccessToken()
logger.info("Unlinking GitHub account for user: \(uid)")

do {
try await revokeAccessToken()

let tokensRef = store.document("users/\(uid)/userData/tokens")
let tokensRef = store.document("users/\(uid)/userData/tokens")

try await tokensRef.updateData(["githubAccessToken": FieldValue.delete()])
try await tokensRef.updateData(["githubAccessToken": FieldValue.delete()])

_ = try await user?.unlink(fromProvider: providerID.rawValue)
_ = try await user?.unlink(fromProvider: providerID.rawValue)

logger.info("Successfully unlinked GitHub account")
} catch {
logger.error("Failed to unlink GitHub account", error: error)
throw error
}
}

func requestAuthorizationCode() async throws -> String {
Expand Down
54 changes: 33 additions & 21 deletions DevLog/Infra/Service/SocialLogin/GoogleAuthenticationService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,47 @@ final class GoogleAuthenticationService: AuthenticationService {
private let messaging = Messaging.messaging()
private var user: User? { Auth.auth().currentUser }
private let provider = TopViewControllerProvider()
private let logger = Logger(category: "GoogleAuthService")

@MainActor
func signIn() async throws -> AuthenticationDataResponse {
logger.info("Starting Google sign in")

guard let topViewController = provider.topViewController() else {
logger.error("Top view controller not found")
throw UIError.notFoundTopViewController
}

let signIn = try await GIDSignIn.sharedInstance.signIn(withPresenting: topViewController)

guard let idToken = signIn.user.idToken?.tokenString else {
throw URLError(.badServerResponse)
}

let accessToken = signIn.user.accessToken.tokenString
let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)

let result = try await Auth.auth().signIn(with: credential)

if let photoURL = signIn.user.profile?.imageURL(withDimension: 200) {
let changeRequest = result.user.createProfileChangeRequest()
changeRequest.photoURL = photoURL
changeRequest.displayName = signIn.user.profile?.name

try await changeRequest.commitChanges()
do {
let signIn = try await GIDSignIn.sharedInstance.signIn(withPresenting: topViewController)

guard let idToken = signIn.user.idToken?.tokenString else {
logger.error("ID token not found")
throw URLError(.badServerResponse)
}

let accessToken = signIn.user.accessToken.tokenString
let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)

logger.debug("Signing in with Google credential")
let result = try await Auth.auth().signIn(with: credential)

if let photoURL = signIn.user.profile?.imageURL(withDimension: 200) {
let changeRequest = result.user.createProfileChangeRequest()
changeRequest.photoURL = photoURL
changeRequest.displayName = signIn.user.profile?.name

try await changeRequest.commitChanges()
}

let fcmToken = try await messaging.token()

logger.info("Successfully signed in with Google")
return result.user.toData(providerID: .google, fcmToken: fcmToken)
} catch {
logger.error("Failed to sign in with Google", error: error)
throw error
}

let fcmToken = try await messaging.token()

return result.user.toData(providerID: .google, fcmToken: fcmToken)
}

func signOut(_ uid: String) async throws {
Expand Down
Loading