188 lines
7.2 KiB
Swift
188 lines
7.2 KiB
Swift
//
|
|
/*
|
|
* SwiftFin is subject to the terms of the Mozilla Public
|
|
* License, v2.0. If a copy of the MPL was not distributed with this
|
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* Copyright 2021 Aiden Vigue & Jellyfin Contributors
|
|
*/
|
|
|
|
import Combine
|
|
import CoreData
|
|
import Foundation
|
|
import JellyfinAPI
|
|
import KeychainSwift
|
|
import UIKit
|
|
|
|
#if os(tvOS)
|
|
import TVServices
|
|
#endif
|
|
|
|
final class SessionManager {
|
|
static let current = SessionManager()
|
|
fileprivate(set) var user: SignedInUser!
|
|
fileprivate(set) var deviceID: String = ""
|
|
fileprivate(set) var accessToken: String = ""
|
|
|
|
#if os(tvOS)
|
|
let tvUserManager = TVUserManager()
|
|
#endif
|
|
let userDefaults = UserDefaults()
|
|
|
|
init() {
|
|
let savedUserRequest: NSFetchRequest<SignedInUser> = SignedInUser.fetchRequest()
|
|
let lastUsedUserID = userDefaults.string(forKey: "lastUsedUserID")
|
|
let savedUsers = try? PersistenceController.shared.container.viewContext.fetch(savedUserRequest)
|
|
|
|
#if os(tvOS)
|
|
savedUsers?.forEach { savedUser in
|
|
if savedUser.appletv_id == tvUserManager.currentUserIdentifier ?? "" {
|
|
self.user = savedUser
|
|
}
|
|
}
|
|
#else
|
|
if lastUsedUserID != nil {
|
|
savedUsers?.forEach { savedUser in
|
|
if savedUser.user_id ?? "" == lastUsedUserID! {
|
|
user = savedUser
|
|
}
|
|
}
|
|
} else {
|
|
user = savedUsers?.first
|
|
}
|
|
#endif
|
|
|
|
if user != nil {
|
|
let authToken = getAuthToken(userID: user.user_id!)
|
|
generateAuthHeader(with: authToken, deviceID: user.device_uuid)
|
|
}
|
|
}
|
|
|
|
fileprivate func generateAuthHeader(with authToken: String?, deviceID devID: String?) {
|
|
let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
|
|
var deviceName = UIDevice.current.name
|
|
deviceName = deviceName.folding(options: .diacriticInsensitive, locale: .current)
|
|
deviceName = String(deviceName.unicodeScalars.filter {CharacterSet.urlQueryAllowed.contains($0) })
|
|
|
|
var header = "MediaBrowser "
|
|
#if os(tvOS)
|
|
header.append("Client=\"Jellyfin tvOS\", ")
|
|
#else
|
|
header.append("Client=\"SwiftFin iOS\", ")
|
|
#endif
|
|
|
|
header.append("Device=\"\(deviceName)\", ")
|
|
|
|
if devID == nil {
|
|
LogManager.shared.log.info("Generating device ID...")
|
|
#if os(tvOS)
|
|
header.append("DeviceId=\"tvOS_\(UIDevice.current.identifierForVendor!.uuidString)_\(String(Date().timeIntervalSince1970))\", ")
|
|
deviceID = "tvOS_\(UIDevice.current.identifierForVendor!.uuidString)_\(String(Date().timeIntervalSince1970))"
|
|
#else
|
|
header.append("DeviceId=\"iOS_\(UIDevice.current.identifierForVendor!.uuidString)_\(String(Date().timeIntervalSince1970))\", ")
|
|
deviceID = "iOS_\(UIDevice.current.identifierForVendor!.uuidString)_\(String(Date().timeIntervalSince1970))"
|
|
#endif
|
|
} else {
|
|
LogManager.shared.log.info("Using stored device ID...")
|
|
header.append("DeviceId=\"\(devID!)\", ")
|
|
deviceID = devID!
|
|
}
|
|
|
|
header.append("Version=\"\(appVersion ?? "0.0.1")\", ")
|
|
|
|
if authToken != nil {
|
|
header.append("Token=\"\(authToken!)\"")
|
|
accessToken = authToken!
|
|
}
|
|
|
|
JellyfinAPI.customHeaders["X-Emby-Authorization"] = header
|
|
}
|
|
|
|
fileprivate func getAuthToken(userID: String) -> String? {
|
|
let keychain = KeychainSwift()
|
|
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
|
|
return keychain.get("AccessToken_\(userID)")
|
|
}
|
|
|
|
func doesUserHaveSavedSession(userID: String) -> Bool {
|
|
let savedUserRequest: NSFetchRequest<SignedInUser> = SignedInUser.fetchRequest()
|
|
savedUserRequest.predicate = NSPredicate(format: "user_id == %@", userID)
|
|
let savedUsers = try? PersistenceController.shared.container.viewContext.fetch(savedUserRequest)
|
|
|
|
if savedUsers!.isEmpty {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func getSavedSession(userID: String) -> SignedInUser {
|
|
let savedUserRequest: NSFetchRequest<SignedInUser> = SignedInUser.fetchRequest()
|
|
savedUserRequest.predicate = NSPredicate(format: "user_id == %@", userID)
|
|
let savedUsers = try? PersistenceController.shared.container.viewContext.fetch(savedUserRequest)
|
|
return savedUsers!.first!
|
|
}
|
|
|
|
func loginWithSavedSession(user: SignedInUser) {
|
|
let accessToken = getAuthToken(userID: user.user_id!)
|
|
userDefaults.set(user.user_id!, forKey: "lastUsedUserID")
|
|
self.user = user
|
|
generateAuthHeader(with: accessToken, deviceID: user.device_uuid)
|
|
print(JellyfinAPI.customHeaders)
|
|
let nc = NotificationCenter.default
|
|
nc.post(name: Notification.Name("didSignIn"), object: nil)
|
|
}
|
|
|
|
func login(username: String, password: String) -> AnyPublisher<SignedInUser, Error> {
|
|
generateAuthHeader(with: nil, deviceID: nil)
|
|
|
|
return UserAPI.authenticateUserByName(authenticateUserByName: AuthenticateUserByName(username: username, pw: password))
|
|
.map { response -> (SignedInUser, String?) in
|
|
let user = SignedInUser(context: PersistenceController.shared.container.viewContext)
|
|
user.username = response.user?.name
|
|
user.user_id = response.user?.id
|
|
user.device_uuid = self.deviceID
|
|
|
|
#if os(tvOS)
|
|
let descriptor: TVAppProfileDescriptor = TVAppProfileDescriptor(name: user.username!)
|
|
self.tvUserManager.shouldStorePreferenceForCurrentUser(to: descriptor) { should in
|
|
if should {
|
|
user.appletv_id = self.tvUserManager.currentUserIdentifier ?? ""
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return (user, response.accessToken)
|
|
}
|
|
.handleEvents(receiveOutput: { [unowned self] response, accessToken in
|
|
user = response
|
|
_ = try? PersistenceController.shared.container.viewContext.save()
|
|
|
|
let keychain = KeychainSwift()
|
|
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
|
|
keychain.set(accessToken!, forKey: "AccessToken_\(user.user_id!)")
|
|
|
|
generateAuthHeader(with: accessToken, deviceID: user.device_uuid)
|
|
|
|
let nc = NotificationCenter.default
|
|
nc.post(name: Notification.Name("didSignIn"), object: nil)
|
|
})
|
|
.map(\.0)
|
|
.eraseToAnyPublisher()
|
|
}
|
|
|
|
func logout() {
|
|
let nc = NotificationCenter.default
|
|
nc.post(name: Notification.Name("didSignOut"), object: nil)
|
|
let keychain = KeychainSwift()
|
|
keychain.accessGroup = "9R8RREG67J.me.vigue.jellyfin.sharedKeychain"
|
|
keychain.delete("AccessToken_\(user?.user_id ?? "")")
|
|
generateAuthHeader(with: nil, deviceID: nil)
|
|
if user != nil {
|
|
let deleteRequest = NSBatchDeleteRequest(objectIDs: [user.objectID])
|
|
user = nil
|
|
_ = try? PersistenceController.shared.container.viewContext.execute(deleteRequest)
|
|
}
|
|
}
|
|
}
|