Add username/password login and keychain manager

Fix #224
This commit is contained in:
Arkadiusz Fal
2022-08-26 01:36:46 +02:00
parent 08ed810b9e
commit 2f2fd67860
19 changed files with 280 additions and 107 deletions

View File

@@ -5,13 +5,12 @@ struct Account: Defaults.Serializable, Hashable, Identifiable {
static var bridge = AccountsBridge()
let id: String
let app: VideosApp
var app: VideosApp?
let instanceID: String?
var name: String?
let url: String
let username: String
let password: String?
var token: String?
var username: String
var password: String?
let anonymous: Bool
let country: String?
let region: String?
@@ -24,7 +23,6 @@ struct Account: Defaults.Serializable, Hashable, Identifiable {
url: String? = nil,
username: String? = nil,
password: String? = nil,
token: String? = nil,
anonymous: Bool = false,
country: String? = nil,
region: String? = nil
@@ -32,19 +30,26 @@ struct Account: Defaults.Serializable, Hashable, Identifiable {
self.anonymous = anonymous
self.id = id ?? (anonymous ? "anonymous-\(instanceID ?? url ?? UUID().uuidString)" : UUID().uuidString)
self.app = app ?? .invidious
self.instanceID = instanceID
self.name = name
self.url = url ?? ""
self.username = username ?? ""
self.token = token
self.password = password ?? ""
self.country = country
self.region = region
self.app = app ?? instance.app
}
var token: String? {
KeychainModel.shared.getAccountKey(self, "token")
}
var credentials: (String?, String?) {
AccountsModel.getCredentials(self)
}
var instance: Instance! {
Defaults[.instances].first { $0.id == instanceID } ?? Instance(app: app, name: url, apiURL: url)
Defaults[.instances].first { $0.id == instanceID } ?? Instance(app: app ?? .invidious, name: url, apiURL: url)
}
var isPublic: Bool {
@@ -52,8 +57,12 @@ struct Account: Defaults.Serializable, Hashable, Identifiable {
}
var shortUsername: String {
guard username.count > 10 else {
return username
let (username, _) = credentials
guard let username = username,
username.count > 10
else {
return username ?? ""
}
let index = username.index(username.startIndex, offsetBy: 11)
@@ -61,7 +70,11 @@ struct Account: Defaults.Serializable, Hashable, Identifiable {
}
var description: String {
(name != nil && name!.isEmpty) ? shortUsername : name!
guard let name = name, !name.isEmpty else {
return shortUsername
}
return name
}
func hash(into hasher: inout Hasher) {

View File

@@ -43,14 +43,6 @@ final class AccountValidator: Service {
$0.pipeline[.parsing].add(SwiftyJSONTransformer, contentTypes: ["*/json"])
}
configure("/api/v1/auth/feed", requestMethods: [.get]) {
guard self.account != nil else {
return
}
$0.headers["Cookie"] = self.invidiousCookieHeader
}
configure("/login", requestMethods: [.post]) {
$0.headers["Content-Type"] = "application/json"
}
@@ -167,7 +159,8 @@ final class AccountValidator: Service {
var accountRequest: Request? {
switch app.wrappedValue {
case .invidious:
return feed.load()
guard let password = account.password else { return nil }
return login.request(.post, urlEncoded: ["email": account.username, "password": password])
case .piped:
return login.request(.post, json: ["username": account.username, "password": account.password])
default:
@@ -184,18 +177,10 @@ final class AccountValidator: Service {
error?.wrappedValue = nil
}
var invidiousCookieHeader: String {
"SID=\(account.username)"
}
var login: Resource {
resource("/login")
}
var feed: Resource {
resource("/api/v1/auth/feed")
}
var videoResourceBasePath: String {
app.wrappedValue == .invidious ? "/api/v1/videos" : "/streams"
}

View File

@@ -93,22 +93,47 @@ final class AccountsModel: ObservableObject {
Defaults[.accounts].first { $0.id == id }
}
static func add(instance: Instance, name: String, username: String, password: String? = nil) -> Account {
let account = Account(
instanceID: instance.id,
name: name,
url: instance.apiURL,
username: username,
password: password
)
static func add(instance: Instance, name: String, username: String, password: String) -> Account {
let account = Account(instanceID: instance.id, name: name, url: instance.apiURL)
Defaults[.accounts].append(account)
setCredentials(account, username: username, password: password)
return account
}
static func remove(_ account: Account) {
if let accountIndex = Defaults[.accounts].firstIndex(where: { $0.id == account.id }) {
let account = Defaults[.accounts][accountIndex]
KeychainModel.shared.removeAccountKeys(account)
Defaults[.accounts].remove(at: accountIndex)
}
}
static func setToken(_ account: Account, _ token: String) {
KeychainModel.shared.updateAccountKey(account, "token", token)
}
static func setCredentials(_ account: Account, username: String, password: String) {
KeychainModel.shared.updateAccountKey(account, "username", username)
KeychainModel.shared.updateAccountKey(account, "password", password)
}
static func getCredentials(_ account: Account) -> (String?, String?) {
(
KeychainModel.shared.getAccountKey(account, "username"),
KeychainModel.shared.getAccountKey(account, "password")
)
}
static func removeDefaultsCredentials(_ account: Account) {
if let accountIndex = Defaults[.accounts].firstIndex(where: { $0.id == account.id }) {
var account = Defaults[.accounts][accountIndex]
account.name = ""
account.username = ""
account.password = nil
Defaults[.accounts][accountIndex] = account
}
}
}