Reputation: 297
I have an app that autofills the password on iOS 12. Now I want to implement this function:
- (void)provideCredentialWithoutUserInteractionForIdentity:(ASPasswordCredentialIdentity *)credentialIdentity;
But I cant get it to work like it should.
AutoFill is enabled in settings and also enabled for my app.
I've read the documentation but this doens't help me. https://developer.apple.com/documentation/authenticationservices/ascredentialproviderviewcontroller/2977554-providecredentialwithoutuserinte?language=objc
I've tried calling this function from viewDidLoad
, prepareCredentialListForServiceIdentifiers
,.. but this is stupid and definitely won't work.
- (void)provideCredentialWithoutUserInteractionForIdentity:(ASPasswordCredentialIdentity *)credentialIdentity {
ASPasswordCredential *credential = [[ASPasswordCredential alloc] initWithUser:@"theUsername" password:@"thePassword"];
[self.extensionContext completeRequestWithSelectedCredential:credential completionHandler:nil];
}
The function should show the credentials above the keyboard, but this doesn't happen. It just shows the default "Passwords" button.
Upvotes: 2
Views: 410
Reputation: 1
Let me add to this topic in case this is still relevant:
First of all, make sure you have the capabilities enabled to provide passwords:
Go to your autofill extension target:
Info.plist
NSExtension
NSExtensionAttributes
ASCredentialProviderExtensionCapabilities
ProvidesPasswords: Boolean -> YES
Now, in your app, as mentioned above, you should make sure that you have stored a list of ASPasswordCredentialIdentities
into your ASCredentialIdentityStore
.
Let me provide you a some sample code:
func saveCredential(username: String, password: String) {
let serviceIdentifier = ASCredentialServiceIdentifier(identifier: "netflix.com", type: .domain)
let credential = ASPasswordCredentialIdentity(serviceIdentifier: serviceIdentifier, user: username, recordIdentifier: "record_id_in_your_database")
ASCredentialIdentityStore.shared.saveCredentialIdentities([credential]) { success, error in ... }
}
This will prompt the keyboard to display the text that lets you select that credential, and then calls provideCredentialWithoutUserInteractionForIdentity
, where you will be able find the password for that domain in your database using "record_id_in_your_database"
Alternatively, you can create multiple multiple ASPasswordCredentialIdentities
and store them and keep them up to date using the method ASCredentialIdentityStore.shared.replaceCredentialIdentities(yourListOfIdentities)
Upvotes: 0
Reputation: 646
ASPasswordCredentialIdentity
s for your domain/url in ASCredentialIdentityStore
. (These are records with some unique recordIdentifier, that doesn't hold password but hold data that can help you decide what password you should take from some secure storage of your choice.)ASCredentialIdentityStore
, and shows a big button for quick login if there's a matching record. If you hit the button - this is only when provideCredentialWithoutUserInteraction
callback is executed. Your task is to work with ASPasswordCredentialIdentity
passed as an argument (it has recordIdentifier
field) and find matching password for it (in your database/keychain/etc.) When you have password - you create ASPasswordCredential
and pass it to self.extensionContext.completeRequest
. Also make sure to call extensionContext.cancelRequest in case of any errors.here's my example
override func provideCredentialWithoutUserInteraction(for credentialIdentity: ASPasswordCredentialIdentity) {
let databaseIsUnlocked = false
if (databaseIsUnlocked) {
// this function queries my custom keychain records by recordIdentifier (Generic Passwords) to find matching password
getItemForRecordId(identifier: credentialIdentity.recordIdentifier) { password, err in
guard password != nil else {
print("password was nil")
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code:ASExtensionError.userInteractionRequired.rawValue))
return;
}
let passwordCredential = ASPasswordCredential(user: credentialIdentity.user, password: password as! String);
self.extensionContext.completeRequest(withSelectedCredential: passwordCredential, completionHandler: nil);
};
} else {
self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, code:ASExtensionError.userInteractionRequired.rawValue))
}
}
Upvotes: 0