Added generic password support.
This commit is contained in:
parent
24045a74dd
commit
75446cb734
33
README.md
33
README.md
|
@ -1,7 +1,7 @@
|
|||
# react-native-keychain
|
||||
Keychain Access for React Native
|
||||
|
||||
Currently functionality is limited to just storing internet passwords. More to come...
|
||||
Currently functionality is limited to just storing internet and generic passwords. Wider exposure of the underlying API coming.
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -17,25 +17,46 @@ See `KeychainExample` for fully working project example.
|
|||
```js
|
||||
var Keychain = require('Keychain');
|
||||
|
||||
var server = 'http://facebook.com';
|
||||
var username = 'zuck';
|
||||
var password = 'poniesRgr8';
|
||||
|
||||
// Generic Password, service argument optional
|
||||
Keychain
|
||||
.setGenericPassword(username, password)
|
||||
.then(function() {
|
||||
console.log('Credentials saved successfully!');
|
||||
});
|
||||
|
||||
Keychain
|
||||
.getGenericPassword()
|
||||
.then(function(credentials) {
|
||||
console.log('Credentials successfully loaded for user ' + credentials.username);
|
||||
});
|
||||
|
||||
Keychain
|
||||
.resetGenericPassword()
|
||||
.then(function() {
|
||||
console.log('Credentials successfully deleted');
|
||||
});
|
||||
|
||||
// Internet Password, server argument required
|
||||
var server = 'http://facebook.com';
|
||||
Keychain
|
||||
.setInternetCredentials(server, username, password)
|
||||
.then(function() {
|
||||
console.log('Credentials saved successfully!')
|
||||
console.log('Credentials saved successfully!');
|
||||
});
|
||||
|
||||
Keychain
|
||||
.getInternetCredentials(server)
|
||||
.then(function(credentials) {
|
||||
console.log('Credentials successfully loaded', credentials)
|
||||
console.log('Credentials successfully loaded for user ' + credentials.username);
|
||||
});
|
||||
|
||||
Keychain
|
||||
.resetInternetCredentials(server)
|
||||
.then(function(credentials) {
|
||||
console.log('Credentials successfully deleted')
|
||||
.then(function() {
|
||||
console.log('Credentials successfully deleted');
|
||||
});
|
||||
|
||||
```
|
||||
|
|
|
@ -71,17 +71,21 @@ NSDictionary * makeError(NSError *error)
|
|||
}
|
||||
|
||||
|
||||
RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString*)server withUsername:(NSString*)username withPassword:(NSString*)password callback:(RCTResponseSenderBlock)callback){
|
||||
RCT_EXPORT_METHOD(setGenericPasswordForService:(NSString*)service withUsername:(NSString*)username withPassword:(NSString*)password callback:(RCTResponseSenderBlock)callback){
|
||||
if(service == nil) {
|
||||
service = [[NSBundle mainBundle] bundleIdentifier];
|
||||
}
|
||||
|
||||
// Create dictionary of search parameters
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassInternetPassword), kSecClass, server, kSecAttrServer, kCFBooleanTrue, kSecReturnAttributes, nil];
|
||||
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassGenericPassword), kSecClass, service, kSecAttrService, kCFBooleanTrue, kSecReturnAttributes, nil];
|
||||
|
||||
// Remove any old values from the keychain
|
||||
OSStatus osStatus = SecItemDelete((__bridge CFDictionaryRef) dict);
|
||||
|
||||
|
||||
// Create dictionary of parameters to add
|
||||
NSData* passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
|
||||
dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassInternetPassword), kSecClass, server, kSecAttrServer, passwordData, kSecValueData, username, kSecAttrAccount, nil];
|
||||
|
||||
dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassGenericPassword), kSecClass, service, kSecAttrService, passwordData, kSecValueData, username, kSecAttrAccount, nil];
|
||||
|
||||
// Try to save to keychain
|
||||
osStatus = SecItemAdd((__bridge CFDictionaryRef) dict, NULL);
|
||||
|
||||
|
@ -89,16 +93,19 @@ RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString*)server withUsername
|
|||
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
|
||||
return callback(@[makeError(error)]);
|
||||
}
|
||||
|
||||
|
||||
callback(@[[NSNull null]]);
|
||||
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getInternetCredentialsForServer:(NSString*)server callback:(RCTResponseSenderBlock)callback){
|
||||
|
||||
RCT_EXPORT_METHOD(getGenericPasswordForService:(NSString*)service callback:(RCTResponseSenderBlock)callback){
|
||||
if(service == nil) {
|
||||
service = [[NSBundle mainBundle] bundleIdentifier];
|
||||
}
|
||||
|
||||
// Create dictionary of search parameters
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassInternetPassword), kSecClass, server, kSecAttrServer, kCFBooleanTrue, kSecReturnAttributes, kCFBooleanTrue, kSecReturnData, nil];
|
||||
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassGenericPassword), kSecClass, service, kSecAttrService, kCFBooleanTrue, kSecReturnAttributes, kCFBooleanTrue, kSecReturnData, nil];
|
||||
|
||||
// Look up server in the keychain
|
||||
NSDictionary* found = nil;
|
||||
CFTypeRef foundTypeRef = NULL;
|
||||
|
@ -119,7 +126,77 @@ RCT_EXPORT_METHOD(getInternetCredentialsForServer:(NSString*)server callback:(RC
|
|||
NSString* password = [[NSString alloc] initWithData:[found objectForKey:(__bridge id)(kSecValueData)] encoding:NSUTF8StringEncoding];
|
||||
|
||||
callback(@[[NSNull null], username, password]);
|
||||
|
||||
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(resetGenericPasswordForService:(NSString*)service callback:(RCTResponseSenderBlock)callback){
|
||||
if(service == nil) {
|
||||
service = [[NSBundle mainBundle] bundleIdentifier];
|
||||
}
|
||||
|
||||
// Create dictionary of search parameters
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassGenericPassword), kSecClass, service, kSecAttrService, kCFBooleanTrue, kSecReturnAttributes, kCFBooleanTrue, kSecReturnData, nil];
|
||||
|
||||
// Remove any old values from the keychain
|
||||
OSStatus osStatus = SecItemDelete((__bridge CFDictionaryRef) dict);
|
||||
if (osStatus != noErr && osStatus != errSecItemNotFound) {
|
||||
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
|
||||
return callback(@[makeError(error)]);
|
||||
}
|
||||
|
||||
callback(@[[NSNull null]]);
|
||||
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setInternetCredentialsForServer:(NSString*)server withUsername:(NSString*)username withPassword:(NSString*)password callback:(RCTResponseSenderBlock)callback){
|
||||
// Create dictionary of search parameters
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassInternetPassword), kSecClass, server, kSecAttrServer, kCFBooleanTrue, kSecReturnAttributes, nil];
|
||||
|
||||
// Remove any old values from the keychain
|
||||
OSStatus osStatus = SecItemDelete((__bridge CFDictionaryRef) dict);
|
||||
|
||||
// Create dictionary of parameters to add
|
||||
NSData* passwordData = [password dataUsingEncoding:NSUTF8StringEncoding];
|
||||
dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassInternetPassword), kSecClass, server, kSecAttrServer, passwordData, kSecValueData, username, kSecAttrAccount, nil];
|
||||
|
||||
// Try to save to keychain
|
||||
osStatus = SecItemAdd((__bridge CFDictionaryRef) dict, NULL);
|
||||
|
||||
if (osStatus != noErr && osStatus != errSecItemNotFound) {
|
||||
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
|
||||
return callback(@[makeError(error)]);
|
||||
}
|
||||
|
||||
callback(@[[NSNull null]]);
|
||||
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(getInternetCredentialsForServer:(NSString*)server callback:(RCTResponseSenderBlock)callback){
|
||||
|
||||
// Create dictionary of search parameters
|
||||
NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)(kSecClassInternetPassword), kSecClass, server, kSecAttrServer, kCFBooleanTrue, kSecReturnAttributes, kCFBooleanTrue, kSecReturnData, nil];
|
||||
|
||||
// Look up server in the keychain
|
||||
NSDictionary* found = nil;
|
||||
CFTypeRef foundTypeRef = NULL;
|
||||
OSStatus osStatus = SecItemCopyMatching((__bridge CFDictionaryRef) dict, (CFTypeRef*)&foundTypeRef);
|
||||
|
||||
if (osStatus != noErr && osStatus != errSecItemNotFound) {
|
||||
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
|
||||
return callback(@[makeError(error)]);
|
||||
}
|
||||
|
||||
found = (__bridge NSDictionary*)(foundTypeRef);
|
||||
if (!found) {
|
||||
return callback(@[[NSNull null]]);
|
||||
}
|
||||
|
||||
// Found
|
||||
NSString* username = (NSString*) [found objectForKey:(__bridge id)(kSecAttrAccount)];
|
||||
NSString* password = [[NSString alloc] initWithData:[found objectForKey:(__bridge id)(kSecValueData)] encoding:NSUTF8StringEncoding];
|
||||
|
||||
callback(@[[NSNull null], username, password]);
|
||||
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(resetInternetCredentialsForServer:(NSString*)server callback:(RCTResponseSenderBlock)callback){
|
||||
|
@ -133,7 +210,7 @@ RCT_EXPORT_METHOD(resetInternetCredentialsForServer:(NSString*)server callback:(
|
|||
NSError *error = [NSError errorWithDomain:NSOSStatusErrorDomain code:osStatus userInfo:nil];
|
||||
return callback(@[makeError(error)]);
|
||||
}
|
||||
|
||||
|
||||
callback(@[[NSNull null]]);
|
||||
|
||||
}
|
||||
|
|
73
index.ios.js
73
index.ios.js
|
@ -56,7 +56,8 @@ var Keychain = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Deletes all keychain entries for `server` and calls `callback` with an `Error` if there is any.
|
||||
* Deletes all internet password keychain entries for `server` and calls `callback` with an
|
||||
* `Error` if there is any.
|
||||
* Returns a `Promise` object.
|
||||
*/
|
||||
resetInternetCredentials: function(
|
||||
|
@ -75,6 +76,76 @@ var Keychain = {
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Saves the `username` and `password` combination for `service` (defaults to `bundleId`)
|
||||
* and calls `callback` with an `Error` if there is any.
|
||||
* Returns a `Promise` object.
|
||||
*/
|
||||
setGenericPassword: function(
|
||||
username: string,
|
||||
password: string,
|
||||
service?: string,
|
||||
callback?: ?(error: ?Error) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RNKeychainManager.setGenericPasswordForService(service, username, password, function(err) {
|
||||
callback && callback((err && convertError(err)) || null);
|
||||
if (err) {
|
||||
reject(convertError(err));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches login combination for `service` (defaults to `bundleId`) as an object with the format
|
||||
* `{ username, password }` and passes the result to `callback`, along with an `Error` if
|
||||
* there is any.
|
||||
* Returns a `Promise` object.
|
||||
*/
|
||||
getGenericPassword: function(
|
||||
service?: string,
|
||||
callback?: ?(error: ?Error, result: ?string) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RNKeychainManager.getGenericPasswordForService(service, function(err, username, password) {
|
||||
err = convertError(err);
|
||||
if(!err && arguments.length === 1) {
|
||||
err = new Error('No keychain entry found' + (service ? ' for service "' + service + '"' : ''));
|
||||
}
|
||||
callback && callback((err && convertError(err)) || null, username, password);
|
||||
if (err) {
|
||||
reject(convertError(err));
|
||||
} else {
|
||||
resolve({ username, password });
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Deletes all generic password keychain entries for `service` (defaults to `bundleId`) and calls
|
||||
* `callback` with an `Error` if there is any.
|
||||
* Returns a `Promise` object.
|
||||
*/
|
||||
resetGenericPassword: function(
|
||||
service?: string,
|
||||
callback?: ?(error: ?Error) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RNKeychainManager.resetGenericPasswordForService(service, function(err) {
|
||||
callback && callback((err && convertError(err)) || null);
|
||||
if (err) {
|
||||
reject(convertError(err));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
function convertError(err) {
|
||||
|
|
Loading…
Reference in New Issue