282

I have a login-screen in my iOS app. The username and password will be saved in the NSUserDefaults and be loaded into the login-screen again when you enter the app again (of course, NSUserDefaults are permanent).

Now, the user have the possibility to disable the username/password saving feature.

So the NSUserDefaults will be cleared then.

But In my app I need this username/password for database queries for the user. So: Where to store the data except NSUserDefaults? (This place can / should be deleted when the user quit the app or logout).

Rishil Patel
  • 1,977
  • 3
  • 14
  • 30
PassionateDeveloper
  • 14,558
  • 34
  • 107
  • 176
  • The user can only clear it by either resetting the device or removing the app. Am I missing something? –  Aug 07 '11 at 10:18
  • 3
    And by the way, if the data should be deleted when the user quits the app, why don't just keep it in RAM? –  Aug 07 '11 at 10:21
  • 11
    You should seriously consider using Keychain for storing usernames and passwords instead of NSUserDefaults. – Filip Radelic Aug 07 '11 at 10:36
  • 1
    You can get basic idea on swift3 implementation from [here](https://gist.github.com/anishparajuli555/dedfdd4aea836de49ac52d13a45a8f84) – LC 웃 Jan 04 '17 at 05:10
  • Please should I always use kSecValueData and kSecValueData as keys? Or can I use any string as a key? – Ne AS Mar 22 '17 at 09:56
  • I thought it was a bad idea to store username and password in apps....? Especially if we were interacting with an external API... – user805981 Oct 02 '17 at 13:07
  • Everybody here unfortunately recommends saving credentials in the KeyChain which can be easily dumped using many tools like KeyChain Dumper, which exposes all of your saved credentials, you should find another secure way to use for storing your credentials like requesting a token after each app launch and refresh this token after a period of time. – JAHelia Jan 22 '19 at 11:46

15 Answers15

427

You should always use Keychain to store usernames and passwords, and since it's stored securely and only accessible to your app, there is no need to delete it when app quits (if that was your concern).

Apple provides sample code that stores, reads and deletes keychain items and here is how to use the keychain wrapper class from that sample which greatly simplifies using Keychain.

Include Security.framework (in Xcode 3 right-click on frameworks folder and add existing framework. In Xcode 4 select your project, then select target, go to Build Phases tab and click + under Link Binary With Files) and KeychainItemWrapper .h & .m files into your project, #import the .h file wherever you need to use keychain and then create an instance of this class:

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];

(YourAppLogin can be anything you chose to call your Keychain item and you can have multiple items if required)

Then you can set the username and password using:

[keychainItem setObject:@"password you are saving" forKey:kSecValueData];
[keychainItem setObject:@"username you are saving" forKey:kSecAttrAccount];

Get them using:

NSString *password = [keychainItem objectForKey:kSecValueData];
NSString *username = [keychainItem objectForKey:kSecAttrAccount];

Or delete them using:

[keychainItem resetKeychainItem];
Filip Radelic
  • 26,607
  • 8
  • 71
  • 97
  • I saw this code before, but it's very hard for me to read it and understand it, because I'm new to iOS developing. Saving the data to NSUserDefaults was very simple (3-4 lines of code). Can you help me a little bit more, with a few line of code to save username and password easily?! – PassionateDeveloper Aug 07 '11 at 10:53
  • 5
    I have updated my answer with the code and description. It's not nearly as hard as you thought. – Filip Radelic Aug 07 '11 at 11:18
  • 45
    ***ATTANTION!*** Please add to your answer, that only "copy KeychainItemWrapper" isn't enough! I had the problem, that I can't build it afterwards! You must add the security.framework to your project that the KeychainItemWrapper will work! (HowTo: Select Project -> Select Target -> Select Tab "Build Phases" -> Select "Link Binary With Libaries" -> "+" -> add Security.Framework) – PassionateDeveloper Aug 07 '11 at 17:20
  • 1
    Indeed, I forgot about that detail because I was writing the answer off the top of my head without testing. Fixed that now, thank you. – Filip Radelic Aug 07 '11 at 17:23
  • 64
    When using ARC, the compiler will yell at you for using the constants `kSecValueData` and `kSecAttrAccount` in Objective-C code, so be sure to cast them using `(__bridge id)`, e.g., `[keychainItem setObject:obj forKey:(__bridge id)kSecValueData];` – Joe Hankin Apr 06 '13 at 23:13
  • @FilipRadelic just a beginer question. You mentioned save username key as kSecAttrAccount. Can I simply use 'username'? or kSecAttrAccount has any meaning? – aVC May 07 '13 at 03:28
  • @aVC that is a constant defined in Security.framework/SecItem.h to be used as a key for account/username. You can see the description of this and other keys in that header file or the docs. – Filip Radelic May 07 '13 at 07:08
  • Using same code : Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Couldn't add the Keychain Item :( – Mangesh May 31 '13 at 07:15
  • 7
    KeychainItemWrapper.m seem to have a memory leak at line 196. Changing the line to "self.keychainItemData = [[[NSMutableDictionary alloc] init] autorelease];" fixes it. – Olof Jun 17 '13 at 12:33
  • 2
    I tried this solution, but when I restart the app the string I get for kSecAttrAccount is empty. I have tried using the copy function of the string I need to save and also by adding a @"username", but it is not getting stored, is there something else that needs to be done? – marimaf Jun 18 '13 at 05:14
  • I have tried everything mentioned here,but still could not get values stored/retrieved into/from key chain... any suggestion ??? – Uniruddh Nov 14 '13 at 12:37
  • When I use this, the password seems to be returned as NSData and not NSString. Is it meant to do that? – Darren Mar 05 '14 at 11:47
  • @FilipRadelic the Keychain is associated with iCloud acc, it can be synced, and so it can be changed !!! – onmyway133 Jul 29 '14 at 03:45
  • @onmyway133 yeah, and? – Filip Radelic Jul 29 '14 at 08:03
  • 12
    When using ARC the updated code has been given [here](https://developer.apple.com/library/ios/documentation/security/conceptual/keychainServConcepts/iPhoneTasks/iPhoneTasks.html) by Apple. Look at Listing 2-1. Although the approach is a same. – Rick Aug 26 '14 at 12:35
  • if we reset the iphone then will it still alive? – sanginadham murali Mar 20 '15 at 05:12
  • I guess this official Apple's KeychainWrapper class might be of some help. Here is the download link : http://www.raywenderlich.com/wp-content/uploads/2014/12/KeychainWrapper.zip – Rizwan Ahmed Mar 09 '16 at 13:02
  • Apple updated the sample code as of september 2016 and now it only contains Swift, no objective-c. @RizwanAhmed linked to the old Objective C code, but it feels a bit wrong to grab security code from some 3rd party site. – mpoisot Mar 16 '17 at 17:57
  • Please from where can I get the KeychainItemWrapper class? – Ne AS Mar 21 '17 at 15:23
  • Should I always use kSecValueData and kSecValueData as keys? Or can I use any string as a key? – Ne AS Mar 22 '17 at 09:56
  • You can get KeychainItemWrapper .h & .m from here : https://stackoverflow.com/a/9851297/1517506 – Parthpatel1105 Jan 29 '18 at 02:45
  • See answer from @D_RBD below. You must use type NSData and (__bridge id) for password. – Libor B. Sep 10 '18 at 12:45
98

If you need an ARC version of the wrapper here is the link https://gist.github.com/1170641 Thanks to

Manuel Pintaldi
  • 1,065
  • 8
  • 8
48

A very easy solution via Keychains.

It's a simple wrapper for the system Keychain. Just add the SSKeychain.h, SSKeychain.m, SSKeychainQuery.h and SSKeychainQuery.m files to your project and add the Security.framework to your target.

To save a password:

[SSKeychain setPassword:@"AnyPassword" forService:@"AnyService" account:@"AnyUser"]

To retrieve a password:

NSString *password = [SSKeychain passwordForService:@"AnyService" account:@"AnyUser"];

Where setPassword is what value you want saved and forService is what variable you want it saved under and account is for what user/object the password and any other info is for.

Jagat Dave
  • 1,643
  • 3
  • 23
  • 30
sust86
  • 1,870
  • 2
  • 18
  • 25
  • Do you know how to use sskeychain to synchronise apps with the same username and password? – Joe Shamuraq Mar 27 '13 at 16:36
  • 4
    how do you store a username as well in addition to a password? How do you delete an entire account from the SSKeychain? Both are not mentioned the docs – user798719 Sep 09 '13 at 18:57
  • 2
    To get username do `NSString *username = [[SSKeychain accountsForService:@"AnyService"][0] valueForKey:@"acct"]`. This should work fine if you only use one account. As always, be sure to check the array length before trying to access index 0. – Jared Price Jun 12 '14 at 16:26
27

You can simply use NSURLCredential, it will save both username and password in the keychain in just two lines of code.

See my detailed answer.

Phil
  • 2,784
  • 2
  • 28
  • 26
16

I decided to answer how to use keychain in iOS 8 using Obj-C and ARC.

1)I used the keychainItemWrapper (ARCifief version) from GIST: https://gist.github.com/dhoerl/1170641/download - Add (+copy) the KeychainItemWrapper.h and .m to your project

2) Add the Security framework to your project (check in project > Build phases > Link binary with Libraries)

3) Add the security library (#import ) and KeychainItemWrapper (#import "KeychainItemWrapper.h") to the .h and .m file where you want to use keychain.

4) To save data to keychain:

NSString *emailAddress = self.txtEmail.text;
NSString *password = self.txtPasword.text;
//because keychain saves password as NSData object
NSData *pwdData = [password dataUsingEncoding:NSUTF8StringEncoding];

//Save item
self.keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];
[self.keychainItem setObject:emailAddress forKey:(__bridge id)(kSecAttrAccount)];
[self.keychainItem setObject:pwdData forKey:(__bridge id)(kSecValueData)];

5) Read data (probably login screen on loading > viewDidLoad):

self.keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];

self.txtEmail.text = [self.keychainItem objectForKey:(__bridge id)(kSecAttrAccount)];

//because label uses NSString and password is NSData object, conversion necessary
NSData *pwdData = [self.keychainItem objectForKey:(__bridge id)(kSecValueData)];
NSString *password = [[NSString alloc] initWithData:pwdData encoding:NSUTF8StringEncoding];
self.txtPassword.text = password;

Enjoy!

David
  • 3,285
  • 1
  • 37
  • 54
D_RBD
  • 341
  • 2
  • 7
11

If you are having an issue retrieving the password using the keychain wrapper, use this code:

NSData *pass =[keychain objectForKey:(__bridge id)(kSecValueData)];
NSString *passworddecoded = [[NSString alloc] initWithData:pass
                                           encoding:NSUTF8StringEncoding];
Robby891
  • 111
  • 1
  • 2
3

checkout this sample code i tried first the apple's wrapper from the sample code but this is much simpler for me

NANNAV
  • 4,875
  • 4
  • 32
  • 50
BSevo
  • 743
  • 5
  • 13
2

try this one:

 KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];
[keychainItem setObject:@"password you are saving" forKey:kSecValueData]; 
[keychainItem setObject:@"username you are saving" forKey:kSecAttrAccount];

may it will help.

Dhaval Bhadania
  • 3,090
  • 1
  • 20
  • 35
Vara
  • 989
  • 1
  • 7
  • 16
2

I looked at using KeychainItemWrapper (the ARC version) but I didn't find its Objective C wrapper as wholesome as desired.

I used this solution by Kishikawa Katsumi, which meant I wrote less code and didn't have to use casts to store NSString values.

Two examples of storing:

[UICKeyChainStore setString:@"kishikawakatsumi" forKey:@"username"];
[UICKeyChainStore setString:@"P455_w0rd$1$G$Z$" forKey:@"password"];

Two examples of retrieving

UICKeyChainStore *store = [UICKeyChainStore keyChainStore];
    // or
UICKeyChainStore *store = [UICKeyChainStore keyChainStoreWithService:@"YOUR_SERVICE"];

NSString *username = [store stringForKey:@"username"];
NSString *password = [store stringForKey:@"password"];
David
  • 3,285
  • 1
  • 37
  • 54
Carl
  • 2,896
  • 2
  • 32
  • 50
2

There is a small bug in the above code (by the way Dave it was very helpful your post thank you)

In the part where we save the credentials it also needs the following code in order to work properly.

[self.keychainItem setObject:@"myCredentials" forKey:(__bridge id)(kSecAttrService)];

most probably is because the second time we try to (re-)sign in with the same credentials it finds them already assigned in the keychain items and the app crashes. with the above code it works like a charm.

ReaLity
  • 83
  • 1
  • 8
2

To update this question:

For those using Swift checkout this drag and drop swift implementation by Mihai Costea supporting access groups:

https://github.com/macostea/KeychainItemWrapper.swift/blob/master/KeychainItemWrapper.swift

Before using the keychain: consider twice before storing passwords. In many cases storing an authentication token (such as a persistence session id) and the email or account name might be enough. You can easily invalidate authentication tokens to block unauthorized access, requiring the user to login again on the compromised device but not requiring reset password and having to login again on all devices (we are not only using Apple are we?).

pizzamonster
  • 1,141
  • 10
  • 9
1

But, now you can go for NURLCredential instead of keychain wrapper. It does what we need to do.

Sumitiscreative
  • 647
  • 3
  • 10
  • 24
1

For swift you can use this library:

https://github.com/jrendel/SwiftKeychainWrapper

It supports all versions of swift.

Ghulam Rasool
  • 3,996
  • 2
  • 27
  • 40
0

The following should work just fine:

KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:@"YourAppLogin" accessGroup:nil];
[keychainItem setObject:@"password you are saving" forKey:kSecValueData]; 
[keychainItem setObject:@"username you are saving" forKey:kSecAttrAccount];
pkamb
  • 33,281
  • 23
  • 160
  • 191
0

If your app has an associated domain it's better to use Managing Shared Credentials. It will provide the best experience for the user and it's managed by the system itself.

Step 1:

Set up your Associated Domains with webcredentials key

Step 2

Mark your text fields

userIdTextField.textContentType = .username
passwordTextField.textContentType = .password

Step 3

Use the following code to save the details whenever the user successfully login. iOS will show a confirmation action sheet for the user to save the password. And next time the user tries to login keyboard will suggest the user credentials for your app.

SecAddSharedWebCredential("www.example.com" as CFString, "user_id" as CFString, "password" as CFString) { error in
    if error != nil {
      // Handle error
      return
    }

    // The credentials have been successfully saved.
}

Now you're ready to go. Read more...?!

Sreekuttan
  • 1,579
  • 13
  • 19