-1

I have a password managing app. Currently, I store all of the user's accounts (i.e. Netflix, Spotify, etc) in an array that is then stored in User Defaults. Now, I want to make things more secure by storing each account in iOS keychain. I realized that even if I store each account in keychain, I will still need to keep track of them all in a data structure (for populating tableviews, etc) and that data structure will need to be stored somewhere. I am struggling to understand the best way to go about implementing this.

My current design approach is to have: • An array of custom Account objects stored in User Defaults • each Account in the array is stored in keychain

I am using Locksmith for working with keychain (Note: I'm not married to this framework). Locksmith requires me to implement several protocols in my Account class. This is then confusing me when it comes to trying to encode Account objects into the master array being stored in User Defaults. I am trying to make Account conform to Codable and am royally confused about what is going/needs to go on on this point.

class Account:  ReadableSecureStorable,
                CreateableSecureStorable,
                DeleteableSecureStorable,
                GenericPasswordSecureStorable,
                Codable {

    var username: String // can i make these let's
    var password: String

    // Required by GenericPasswordSecureStorable
    let service: String
    var account: String { return username }

    // Required by CreateableSecureStorable
    var data: [String: Any] {
        return ["password": password]
    }

    init(service: String, username: String, password: String) {
        self.username = username
        self.password = password
        self.service = service
    }

    private enum CodingKeys: String, CodingKey {
        case username
        case password = "secret"
        case service
    }
}

struct AccountDefaults {
    static private let accountsKey = "accountsKey"

    static var accounts: [Account] = {

        guard let data = UserDefaults.standard.data(forKey: accountsKey) else { return [] }
        return try! JSONDecoder().decode([Account].self, from: data)
        }() {
        didSet {
            guard let data = try? JSONEncoder().encode(accounts) else { return }
            UserDefaults.standard.set(data, forKey: accountsKey)
        }
    }
}

I was getting errors stating that Account does not conform to decodable before adding the codingKeys enum.

From here on, I can't figure out where to go.

When I add an account, I can store it with Locksmith. Will the default codingKeys password override the real password I'm trying to save into keychain?

I know I'm not supposed to include multiple questions here, but the thoughts going through my head are: What do I need to encode/decode? How does using Locksmith affect what I need to encode/decode? Will the password need to be encoded?

ncooke3
  • 548
  • 3
  • 10

1 Answers1

0

Codable is just an alias for Decodable & Encodable where Decodable and Encodable are protocols.

So, What do I need to encode/decode? If you mean JSON, For converting into JSON use default implementations. For simple structs just allow this protocol conformance.

How does using Locksmith affect what I need to encode/decode? Regarding this – no effect.

Will the password need to be encoded? your password is a string. Obviously, we be easily encoded into JSON. If you mean the password security, the Keychain with 'only unlocked' level is good.

Vyacheslav
  • 26,359
  • 19
  • 112
  • 194
  • Could you clarify about password security? I'm confused. If I store the account's password into keychain, and then encode the Account object, I shouldn't encode the password property because that would render storing it into keychain useless, right? – ncooke3 Jul 31 '19 at 00:17
  • @ncooke3 yes. There are several levels of the security. The more secure the better, but highly-level secured is not available at the time. – Vyacheslav Jul 31 '19 at 06:03