I am 100% new to AWS and to the AWS iOS SDK.
I am using The Standard AWS Code Example. to initialize the Cognito credentials provider. I of course am changing #"COGNITO_IDENTITY_POOL"].
I have noticed that when I do this the first time for a device that NSString *cognitoId = credentialsProvider.identityId; is nil. Is this to be expected? if so at what time is the property available?
Also is my understanding correct that the SDK saves this Cognito ID in the devices key chain? Thus this identity will not change for the device while ever the user backs up his operating system. If for example he wipes his device and freshly installs iOS then a new ID (same app same Congnito pool) will be generated? If however he just deletes my App but later installs it again (no change to iOS), then if I use the same Cognito pool he will be recognized as an existing member with the same ID?
"credentialsProvider.identityId" is null the first time you use the SDK, until it gets refreshed. Once you get an identity from the service, it will get stored in the keychain and the device will always reuse the same one (unless, as you said, the user clears the keychain or wipes the device).
Albert
Related
I am writing an iOS app and have a dilemma. The app will be used to test the BLE protocol of devices coming off our production line. It needs to be very simple, It is a one button app that automatically connects to devices and tests commands in our protocol and gives a pass or fail result. If it passes the device id is sent to our API to be entered in our database.
My problem is I don't want the user to have to login, but I need to send a username and password to the API to log in. This means the username and password has to be included in the app. I am trying to find out the most secure way to do this. I initially thought I would include a plist in the app with the credentials, enter them in keychain, then delete the plist. However I don't believe you can delete a file included in the app bundle (I get a 513 permission error).
I have 2 questions. Is it secure to include credentials in code? Is there a better way to do this?
Thanks!
No.
Use API to retrieve credentials and store to keychain. Simple call to firebase could solve this. Or use your own backend with your custom “security”.
But even obtaining via API don’t give you 100% security. Everything depends. API solution gives you flexibility and ability to support different versions with different credentials.
I'm working with hyperledger fabric in the stable version.
In the CA, the users with their password are stored in clear-text on the membersrvc.yaml file in $GOPATH/src/github.com/hyperledger/fabric/membersrvc.
Is it normal ? Is there an other way to create the users when I launch the CA without having the crendentials in clear text ?
The userid/password combination present in the membersrvc.yaml can be used only once. Think about it as an OTP. If you want to reuse it again, you have to bring down the membership service instance and you have to clean up the user data store in the /var/hyperledger/production/db directory (And as of now, as per my information, there are no plans to include a logout API which might have enabled the usage of the credentials again without clean up).
If you want to add a new user to the Fabric, then this can be done programatically with the Member.register method - while using Node.js SDK. But the user would exist only during the life cycle of the Fabric ( ie, till a clean up is performed).
On AWS, I wish to create an application that allows a user to sign in via mobile, web or both.
I created a system using API Gateway, Lambdas and DynamoDB for the back end. I have sign in working for web using JavaScript but was having issues finding a Swift example for iOS of the same code (objective C only available). So I created a mobile hub application, imported my existing API and have a working iOS sign in.
The issue is that the iOS side uses the Mobile Hub so I now have 2 different User Pools so you can't sign up on web and log in on mobile (or vice versa).
I tried to change the settings in the iOS app to point to my web app Cognito User Pool settings and remove the secret but it errors as it can not be null or empty.
Why does the mobile hub require a client secret? The JavaScript documentation suggested was bad practice since code can be de-compiled and the secret extracted.
There doesn't seem to be any consistent documentation that explains, what I would guess, as the most common use case of a mobile and web app!
The other issue is that I can download my API client SDK for my API Gateway for use with web app and iOS app. But, the generated mobile hub app includes a REST based call? Am I going crazy here or does the official web approach not link to the official mobile approach?
So the key questions are:
Can or should I change the mobile apps to point at the original
Cognito?
if so should I remove the client secret?
can I effectively ignore the Mobile Hub after set up and use it purely for code generation? Assuming it was then working can I just use the generated client SDK for my API Gateway?
Is there a better way of setting up an iOS (later Android too) and web app?
I've spent considerable time and effort, and tried many approaches.
The "Mobile Hub" nicely sets up the user pool, identity pool, IAM roles etc.
The keys etc are mostly provided in the Info.plist file, although ( unwisely ) the developers of the user pool AWSSignInProvider made it have hardcoded keys in a configuration file.
SO:
If you don't intend to use the "Mobile Hub" console application for making changes to your mobile app configuration, then you won't need any more downloads. In that case, you don't have to worry about changes to Info.plist or the configuration file, and you can edit what you want.
It is unclear if you are going to use the mobile hub created identity pool and just want to insert your user pool, or if you want to change both pools. Obviously if you are using the same identity pool then some of the changes below will not be needed (they will be obvious because you will be changing them to be exactly the same).
So all you have to do is change the ID's to get everything hooked up correctly.
In general you need to fix all the downloaded keys and ID's in Info.plist and the configuration file, and then you need to update the server configuration. In more detail, here are the places you need to change it:
in the app:
update all the keys in Info.plist to be the keys you want. (specifically credentials provider and identity manager keys for google) But if you are using other mobile hub services, check the keys there too.
If you are using s3, and some of the other services the directory names/database names are also stored in the code... I leave it as an activity to find them.
in the file MySampleApp->AmazonAWS->AWSConfiguration.swift edit the keys provided by Mobile hub to match your user pool (do this while quietly swearing under your breath because they are not in Info.plist)
in the console:
put your app name in your user pool APP's list if it is not there, and record the user pool id, app id, and app secret.
click on federated identities and the identity pool created by mobile hub and update the authentication provider to use your cognito user pool id and app id.
if you are changing the identity pool too, then you will need to look at the IAM Roles for your auth and unauth users and specifically edit the policy that is named: .....yourapp....signin_MOBILEHUB_xxxxxxx, and change the identity pool id in that policy to be the one you want to use. Do this for both auth and unauth.
( you can change the id if you only use one pool, or add another to a list of id's like this if you will have multiple identity pools (for test...etc))
"Resource": [
"arn:aws:cognito-identity:*:*:identityPool/us-east-1:8s8df8f8-sd9fosd9f0sdf-999sd99fd",
"arn:aws:cognito-identity:*:*:identityPool/us-east-1:dfsf9099-sd9fosd9f0sdf-sd9f0sdf09f9s"
]
similarly, in the trust relationship associated with the roles, you need to fix the id's, (or handle multiple ID's if you want the role to serve multiple identity pools). Here is how to specify multiple id's there.
"Condition": {
"ForAllValues:StringLike": {
"cognito-identity.amazonaws.com:aud": [
"us-east-1:8s8df8f8-sd9fosd9f0sdf-999sd99fsdfdd",
"us-east-1:dfsf9099-sd9fosd9f0sdf-sd9f0sdf09f9s"
]
},
if you are using google too... you need to make sure that you have an identityProvider in the IAM configuration for google (mobile hub did that for you) and if you are using your own identity pool , in your federated identity pool authorization providers configuration you will need to select google open id provider (and put google in the authorized providers too (but I don't think this part is strictly needed))
facebook doesnt use OpenID Connect, it has a proprietary way of configuring into the authentication providers section, so enter those keys if needed in the identity pool authentication providers section.
And that should be enough to make it work.
And no you are not going crazy... the documentation does not match the current IOS SDK. Mobile hub uses the aws-mobilehub-helper-ios (github) which is built on TOP of the sdk, so the documentation does not apply to that either! Mobile Hub Helper has a nice design, so I recommend you use it, rather than the raw SDK.
(and lastly... and I am out of my depth here because I don't use API gateway, but my understanding was that the API Gateway is a way to get credentials to use AWS Services, and with the mobile hub app you will be using Cognito to get those credentials, so I am not sure you will need to bring the API Gateway into it...at all)
UPDATE
You may want to use no client secret for users of your javascript app, and use a IOS Mobile App on the same pool too. This can be done in two ways:
1) The better way is that you create two different client's in the user pool. For one you will generate a client secret, for the other you will UNCHECK the "generate client secret" box.
Then in your Federated Identity Pool you go to Authentication Providers, and click on Cognito, and specify TWO DIFFERENT PROVIDERS USING THE SAME USER POOL ID. (This is not really two different providers, but that is how the console makes you specify it). And you put the two different client ids in those providers.
Now both the IOS app and the Javascript app can access the pool and get authentication and credentials from the identityProvider and credentialsProvider.
2) A not so good way. The reason this way is worse is because I don't know the impact (if any) it has on the security of your mobile app. And at AWS there is nobody to ask the question to without buying a support contract. But the other way exists.
What you do is use the same client id in both apps, and you don't generate a client secret. To do this you put "nil" in the clientSecret. This works fine with some caveats.
First, the AWS Mobile Hub has a bug in it's AWSCognitoUserPoolsSignInProvider. That class requires that the clientSecret is non-null. But in the SDK, the only way to tell the SDK that you want no client secret is to pass nil! However there are workarounds.
(What I did was use the AWSCUPIdPSignInProvider.swift (that I wrote), which will work fine and I have a version that will take a nil for the secret. I did that because it was faster for me to test this out. you can find that signin provider on github if you want to use it)
But a better (more future proof) solution is probably to use the AWSCognitoUserPoolsSignInProvider that the mobile hub delivers, but change the code in AWSMobileClient to configure and register your own pool rather than letting AWSCognitoUserPoolsSignInProvider do it for you.
I haven't bothered to try this, (because we only have to do it because AWS has not gotten around to updating the github aws-mobilehub-helper-ios yet). But basically in AWSMobileClient instead of this code:
func setupUserPool() {
// register your user pool configuration
AWSCognitoUserPoolsSignInProvider.setupUserPoolWithId(AWSCognitoUserPoolId, cognitoIdentityUserPoolAppClientId: AWSCognitoUserPoolAppClientId, cognitoIdentityUserPoolAppClientSecret: AWSCognitoUserPoolClientSecret, region: AWSCognitoUserPoolRegion)
AWSSignInProviderFactory.sharedInstance().registerAWSSignInProvider(AWSCognitoUserPoolsSignInProvider.sharedInstance(), forKey:AWSCognitoUserPoolsSignInProviderKey)
}
you would have something like this code
func setupUserPool() {
// register your user pool configuration
// find the service configuration (we don't know if they set it as default)
let credentialProvider = AWSCognitoCredentialsProvider(regionType: .USEast1 (or your region), identityPoolId: "YourIdentityPoolId")
let configuration = AWSServiceConfiguration(region: .USWest2 (or your region), credentialsProvider: credentialProvider)
// configure and put your own user pool in the service configuration
let userPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: AWSCognitoUserPoolAppClientId, clientSecret: nil, poolId:AWSCognitoUserPoolId)
// now we register that pool with the service configuration using the key they use
AWSCognitoIdentityUserPool.register(with: configuration, userPoolConfiguration: userPoolConfiguration, forKey: AWSCognitoUserPoolsSignInProviderKey)
AWSSignInProviderFactory.sharedInstance().registerAWSSignInProvider(AWSCognitoUserPoolsSignInProvider.sharedInstance(), forKey:AWSCognitoUserPoolsSignInProviderKey)
}
But as I said above, solution 1, use two different clients and specify two different providers is preferred.
Context
I use booth Cognito User Pools and Cognito Identity Pools to sign-in user.
I want to achieve the simple task to sign-in a user on a iOS app, a Swift app.
My app is based on custom version of Mobile Hub Help to allow the use of Cognito User Pools. See A Swift Sample Sign-In App (using aws-mobile hub-helper) with Cognito User Pools
There is two steps in app: first one is only launch and second one is Sign-In after user touch a Sign-In button: user credentials are then hardcoded for sake of minimality.
During the first step, user have to received an unauthorized identityId. Then after second step, user received an auth identityId.
Behavior
Before press signin, step 1, there is 2 requests to AWS:
AWSCognitoIdentityService.GetId
AWSCognitoIdentityService.GetCredentialsForIdentity
After press signing there are 3 more requests:
AWSCognitoIdentityProviderService.InitiateAuth
AWSCognitoIdentityProviderService.RespondToAuthChallenge
AWSCognitoIdentityService.GetId
Before request 5 — AWSCognitoIdentityService.GetId — user is signed-in, as indicated by observer on AWSIdentityManagerDidSignInNotification.
After request 5, an IdentityId is retrieved. But immediately after user is signed-out according to observer on notification AWSIdentityManagerDidSignOutNotification.
What may happen?
It would help to have a look at your logs. But what you need to realize is that cognito sdk REMEMBERS stuff (session id's, identity id's, etc) using keychain and NSUserDefaults. So when you run your hard coded test for a second time, the behavior of your test will probably be different. If you want to start clean, go to the simulator menu and choose reset content and settings (this will clear out the keychain - which stores session stuff, and will clear out the NSUserDefaults which stores sessions that are active).
I just pushed a new version of the repo on the branch AddAllMobileHubDemos. The latest version of the repository has 3 targets defined. SignIn, MySampleApp and Dom. SignIn is a version of my old signin app modified to work with mobile-hub-helper. MySampleApp is a full downloaded MobileHub app with Cognito User Pools built in. There is also Dom, a single page hard coded signin app.
Also check out (in that same repository) the cognito-diagram cognito-diagram.pdf from the latest push, which has some of the possible errors listed. That version has the features I described above.
If you post the logs that might help me be more specific. There are cases where the AWSIdentityManager will log you out (for instance if you try to merge two identities... which you might very well if you are using a hard coded usrname and password and then changed them without ever logging out.)
Bruce
Problem solved!
#Bruce0 led me to answer!
First, on a real device running everything worked as expected.
Second, there were a lot of errors on log:
OSStatus error: [-34018] Security error has occurred.
The answer was to set the "Keychain Sharing" capability to "ON" in "Target->Capabilities" on Xcode.
Does anyone have any tips migrating from the old iOS FB SDK (the one hosted on their GitHub account) to their .framework (package-based installer) one? I'm having issues getting the existing auth token and expiration date objects to validate against the new FBSession object.
Here's the situation:
In the old SDK/technique, the Facebook iOS SDK required you to save things like the auth. token and expiration date manually through your own storage mechanisms. In the new framework based SDK they now handle this for you, but in order to migrate users (i.e. not have them re-login) I need to provide this information to the new SDK.
It winds up being that Facebook is storing the K-Vs in NSUserDefaults and they even tell you the root key name as well as all the keys for the nested K-Vs of the dictionary they use to store this information.
Their token class FBSessionTokenCachingStrategy even has a class method to verify if an NSDictionary validates to a proper dictionary that establishes a usable Facebook session
+ (BOOL)isValidTokenInformation:(NSDictionary*)tokenInformation;
So I've taken my preexisting auth. token and expiration, put them in a new dictionary, stored it in the proper key'ed location and synchronized NSUserDefaults.
So far so good right? Well once I initialize a FBSession object via
- (id)initWithAppID:(NSString*)appID
permissions:(NSArray*)permissions
urlSchemeSuffix:(NSString*)urlSchemeSuffix
tokenCacheStrategy:(FBSessionTokenCachingStrategy*)tokenCachingStrategy;
The whole key is removed from NSUserDefaults and the session object's state property is in the state of FBSessionStateCreated (i.e. no preexisting state) vs what it should be FBSessionStateCreatedTokenLoaded (i.e. it knows it has the locally saved properties and is ready to be checked online).
Why is it being removed? It validated against the class method.
Thanks
It turns out that the local storage object for newer Facebook SDK also preserves the permissions list related to the token. This wasn't required before so while I didn't have it saved locally, my permissions list did not change. So what I did was also save the permissions list into the new storage object (totaling 3 keys: auth. token, expiration date, and the permissions list).
Once I had done this, creating/initializing the FBSession object did not remove my locally stored object and upon invoking openWithCompletionHandler: it established the expected session state to FBSessionStateOpen.