4

I have been working in iOS autofill credential extension since long days. I have checked so many iOS articles and videos. But I am not able to show credential on quick type bar, reset things are successfully integrated. Can any give me quick help?

Using this video and url which was shared from apple: https://developer.apple.com/videos/play/wwdc2018/721 https://developer.apple.com/documentation/authenticationservices
I am using below code to save credential to Keychain for particular domain.

    let keychain = Keychain(server: "instagram.com", protocolType: .https, authenticationType: .htmlForm)
    keychain["emailAddress"] = "Password"

And use this code for save domain:

func savedomain(domain: String, account: String, password: String, completion: ((Bool, SharedWebCredentialsManagerError?) -> Void)? = nil) {
    SecAddSharedWebCredential(domain as CFString, account as CFString, password as CFString?) { error in
        guard let error = error else {
            completion?(true, nil)
            return
        }
        let errorDescription = CFErrorCopyDescription(error) as String
        let saveFailedError = SharedWebCredentialsManagerError.saveFailed(errorDescription)
        completion?(false, saveFailedError)
    }
}

I have created autofill extension and getting saved credentials, but not able to display credential on quick type bar in safari for instagram.com

Parthpatel1105
  • 541
  • 6
  • 17

3 Answers3

2

I have implemented autofill extension for all social sites, Sharing my source code to save emailID-Password with domain.

class func save(key: String, data: Data) -> OSStatus {
    let query = [
        kSecClass as String       : kSecClassGenericPassword as String,
        kSecAttrAccount as String : key,
        kSecValueData as String   : data ] as [String : Any]

    SecItemDelete(query as CFDictionary)

    return SecItemAdd(query as CFDictionary, nil)
}

Above "Save" function i have written in my custom KeyChainManager class, Also i added below code in KeyChainManager class which is as below.

extension Data {

init<T>(from value: T) {
    var value = value
    self.init(buffer: UnsafeBufferPointer(start: &value, count: 1))
}

func to<T>(type: T.Type) -> T {
    return self.withUnsafeBytes { $0.load(as: T.self) }
}

}

I am saving my data from VC by calling our KeyChainManager class like below:

let email = (txtEmail?.text ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
        let password = (txtPassword?.text ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
        let domain = (txtDomain?.text ?? "").lowercased().trimmingCharacters(in: .whitespacesAndNewlines)
        
        let user = User(email: email, password: password, key: self.key, domain: domain, identifier: self.keyIdentifier)
        let data = (try? JSONEncoder().encode(user)) ?? Data()
        let _ = KeyChain.save(key: "\(self.keyIdentifier)", data: data)

This all stuff is for saving our credentials, Now major point is how to list all saved credentials in our extension's CredentialProviderViewController.swift.

For that i added below method in KeyChainManager class :

class func load(key: String) -> Data? {
    
    let query = [
        kSecClass as String       : kSecClassGenericPassword,
        kSecAttrAccount as String : key,
        kSecReturnData as String  : kCFBooleanTrue!,
        kSecMatchLimit as String  : kSecMatchLimitOne ] as [String : Any]

    var dataTypeRef: AnyObject? = nil

    let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef)

    if status == noErr {
        return dataTypeRef as! Data?
    } else {
        return nil
    }
}

And calling this function from extension's CredentialProviderViewController.swift like this:

users.removeAll()
    for i in 0..<1000000 {
        if let encodedData = KeyChain.load(key: "\(i)")  {
            let user = try! JSONDecoder().decode(User.self, from: encodedData)
            if user.key == key && ((serviceIdentifier.contains(user.domain ?? "")) ) {
                users.append(user)
            }
        }else{
            break
        }
    }

I hope this content helps you as i spent many days to create just one demo :) :) Comment below, If its helps you :) :)

Thanks, Anjali.

Anjali jariwala
  • 410
  • 5
  • 15
0

You need to populate ASCredentialIdentityStore in order for the quicktype bar to work. See the description of ASCredentialProviderViewController:

Optionally add ASPasswordCredentialIdentity instances to the shared ASCredentialIdentityStore to make identities available directly in the QuickType bar.

This is also described in the WWDC presentation you reference.

MichaelR
  • 1,681
  • 15
  • 28
  • Any idea how to populate ASCredentialIdentityStore? I tried: let store = ASCredentialIdentityStore.shared let passwordIdentity = ASPasswordCredentialIdentity(serviceIdentifier: ASCredentialServiceIdentifier(identifier: "chase.com", type: .URL), user: "jbrantl8", recordIdentifier: nil) store.saveCredentialIdentities(identities) { (success, error) in – metamonkey Jul 10 '21 at 03:58
  • error: Editor placeholder in source file and error: Cannot convert value of type 'ASPasswordCredentialIdentity' to expected argument type '[ASPasswordCredentialIdentity]' – metamonkey Jul 10 '21 at 03:59
0

How I implemented population of ASCredentialIdentityStore:

        var firstItem = ASPasswordCredentialIdentity(serviceIdentifier: ASCredentialServiceIdentifier(identifier: "https://online.somebank.com/auth/login", type: .URL), user: "login@bank.com", recordIdentifier: nil)
    ASCredentialIdentityStore.shared.replaceCredentialIdentities(with: [firstItem]) { result, error in
        if result {
            print("saved")
        }
    }

In my case everything works perfect. So I fetch all passwords from remote and then populate ASCredentialIdentityStore with available passwords.

Melany
  • 466
  • 7
  • 20