macOS Authorization Plugin: Keychain Error -25308 When Storing Password

Hi everyone,

I'm working on a macOS authorization plugin (NameAndPassword) to enable users to log into their system using only MFA, effectively making it passwordless. To achieve this, I'm attempting to store the user's password securely in the Keychain so it can be used when necessary without user input.

However, when I attempt to store the password, I encounter error code -25308. Below is the code I'm using to save the password to the Keychain:

objc code

  • (void)storePasswordInKeychain:(NSString *)password forAccount:(NSString *)accountName { NSData *passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];

    NSDictionary *query = @{ (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrService: @"com.miniOrange.nameandpassword", (__bridge id)kSecAttrAccount: accountName, (__bridge id)kSecValueData: passwordData, (__bridge id)kSecAttrAccessible: (__bridge id)kSecAttrAccessibleAfterFirstUnlock };

    // Delete any existing password for the account OSStatus deleteStatus = SecItemDelete((__bridge CFDictionaryRef)query); if (deleteStatus == errSecSuccess || deleteStatus == errSecItemNotFound) { [Logger debug:@"Old password entry deleted or not found."]; } else { [Logger error:@"Failed to delete existing password: %d", (int)deleteStatus]; }

    // Add the new password OSStatus addStatus = SecItemAdd((__bridge CFDictionaryRef)query, NULL); if (addStatus == errSecSuccess) { [Logger debug:@"Password successfully saved to the Keychain."]; } else { [Logger error:@"Failed to save password: %d", (int)addStatus]; }

}

Any insights or suggestions would be greatly appreciated!

Answered by DTS Engineer in 814876022

Isn’t this a chicken’n’egg problem? Ignoring the store side for the moment, let’s think about the load side. The user’s password is used to unlock the keychain, so you won’t be able to load the user’s password from the keychain until you’ve unlocked the keychain, which requires the user’s password.

ps I’m all in favour of posting code snippets, but please use a code block. See Quinn’s Top Ten DevForums Tips for advice on how to do that.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Isn’t this a chicken’n’egg problem? Ignoring the store side for the moment, let’s think about the load side. The user’s password is used to unlock the keychain, so you won’t be able to load the user’s password from the keychain until you’ve unlocked the keychain, which requires the user’s password.

ps I’m all in favour of posting code snippets, but please use a code block. See Quinn’s Top Ten DevForums Tips for advice on how to do that.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks for the response!

Apologies for the formatting issues earlier; this is my first post.

I wanted to ask for more insights into achieving the passwordless feature for my MFA module. Specifically, my approach is to use the Keychain to securely store the user's password and fetch it later for authentication.

I'm using kSecAttrAccessibleAfterFirstUnlock because I assumed that once the user unlocks the device after a restart (using their password), I would then be able to access the Keychain to retrieve the stored password.

Am I misunderstanding how kSecAttrAccessibleAfterFirstUnlock works in the context of an authorization plugin? Or is there a better way to securely store and retrieve the password for this use case?

Looking forward to your suggestions!

If you’re using keychain on the Mac, you need to read TN3137 On Mac keychain APIs and implementations. Without that context, nothing I say will make sense.

I'm using kSecAttrAccessibleAfterFirstUnlock

That constant is associated with the data protection keychain, which isn’t something you can use in an authorisation plug-in. There are two reasons for this:

  • Your authorisation plug-in runs outside of the user’s context, and the data protection keychain is only available within the user’s context. See the discussion of launchd daemons in TN3137 [1].

  • Regardless, neither the data protection keychain nor the file-based login keychain are available because the system unlocks them using the user’s password at the end of the login process, and your authorisation plug-in runs as part of the login process.

I assumed that once the user unlocks the device after a restart

Yeah, sorry, you’re very much off in the weeds here. The data protection keychain originated on iOS, which has a much simpler keychain model. That’s because iOS only supports a single user [2]. OTOH, macOS supports multiple users, each with their own keychain. In the context of macOS, the Unlock in kSecAttrAccessibleAfterFirstUnlock means “after the user has logged in”.

The boot UI that you’re referring to is for FileVault, that is, it unlocks the boot volume, not the keychain. If you have auto-login enabled then the system transfers that credential to the authorisation context so that the login process can use it to authenticate the user login and, as a consequence of that, unlock the user’s keychain.

IMPORTANT There’s no supported way for an authorisation plug-in to access that credential. I’ve seen various folks try to reverse engineer the implementation. We don’t support that because these implementation details can change at any time. For example, I recently looked into a case where a developer’s product broke on Apple silicon for exactly this reason (s. 6569402).

The only keychain that’s available to an authorisation plug-in is the system keychain, a file-based keychain. And even that has limitations. An authorisation plug-in can operate in privileged or non-privileged mode. It’ll only be able to modify the system keychain in privileged mode.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Your plug-in isn’t running in a daemon context, but it is running outside of the user context, which is what matters here.

[2] Don’t ask me about Shared iPad or I’ll start to whimper.

macOS Authorization Plugin: Keychain Error -25308 When Storing Password
 
 
Q