amazon-archives / aws-sdk-react-native

AWS SDK for React Native (developer preview)
Apache License 2.0
631 stars 68 forks source link

Integration of basic authentication to User Pool #27

Open mmailhos opened 7 years ago

mmailhos commented 7 years ago

Hello,

How can I do a basic authentication (e.g. get access token) to a user pool on Cognito ?

All I was able to see in the repo was connections to Identity Providers (such as Facebook...).

Thanks!

mnichols commented 7 years ago

It isn't supported in this. One user posted some code here that gave me a jump no connecting using user pools though.

~It isn't clear what the roadmap is for proper AWS support of Cognito yet in react-native so for now the best hope imo is to cobble together bits from the ios and androis SDK's into react bridge code.~

EDIT: I misspoke I think...actually you can use the user pool as a cognito provider I think so the missing bit it authenticating against the pool using creds and fetching the idToken. The code linked above has example of that. I am still trying to pull the pieces together myself but I think that is the right direction.

mnichols commented 7 years ago

Ok I was able to use AWSRNCognitoCredentials with the CognitoUserPool backing. A few things I needed to do for understanding this non-documented thing:

JS bug

The AWSCognitoCredentials.js has a bug at line 48 which needs to be changed to:

      listener.addListener("LoginsRequestedEvent", async ({callbackId}) => {

You might get some odd JSON errors from RCTConvert if you don't make this change. Basically the event raised from the native code is LoginRequestEvent and that will send a payload of a NSDictionary that has something like { callbackId: <uuid> }.

Workflow

First, you need to authenticate against the user pool and pull the idToken out of the response. that is the missing thing in this sdk. The code I linked earlier can get a head start on that.

Once you have that, you need to use that token for using the federated identity flow. When you call initWithOptions here you can call setLogins but only android needs that. What you need to be sure you provide is a getLogins callback func that returns a JS map (object) where the key is the following format:

var logins = {}
logins[`cognito-idp.${REGION}.amazonaws.com/${USER_POOL_ID}`] = idTokenFromUserPool;

Checking it out

On IOS I made these calls and they worked once I figured out all that other junk:

                const fetchedId = await AWSCognitoCredentials.getCredentialsAsync()
                console.log('called getCredentialsAsync', fetchedId)
                //this doesnt
                console.log('calling getIdentityIDAsync')
                const fetchedId2 = await AWSCognitoCredentials.getIdentityIDAsync()
                console.log('called getIdentityIDAsync', fetchedId2)

I confirmed that my getLogins callback was invoked as expected. The getCredentialsAsync got a response like:

{
   "AccessKey": <hash>,
   "Expiration": <datetime>,
   "SecretKey": <hash>,
   "SessionKey": <hash>
}

And the getIdentityIDAsync got a response like: { identityId: "<region>:<uuid>" }

Deprecated usage of sendAppEventWithName

The SDK needs to be updated to subclass the RCTEventEmitter. See here for details on an approach for that. It's unlikely this will be done here though so that looks like the next thing to solve...

HTH

lielran commented 7 years ago

I find this use case very basic. The first thing every developer will try to implement is basic authentication(login/logout/sign in...)

mnichols commented 7 years ago

@lielran the first drop of this SDK isnt concerned with the UserPool portion, but rather the Cognito Identity Provider side of the story which isnt concerned about login/logout. I had to roll my own user pool stuff but once you have the id token you can use this sdk. Took me a while to recognize they are unrelated concerns (federation versus persona).

mmailhos commented 7 years ago

Hello, I started to work back again on this recently. It seems that the easiest thing to do would be to get the id/access token using Native Bridge (I probably won't need the federation for now anyway). As you said, @mnichols , this link is a great start. I added the jars from the Android SDK and created the CognitoPackage implements ReactPackage class as well. I was finally able to compile the project.

Unfortunately, at the very end, I could not load the module, the third does not recognize './Cognito'.

import { NativeModules } from 'react-native';
module.exports = NativeModules.ToastAndroid;
import Cognito from './Cognito';

I will post as soon as I am able to get the token; In the meantime, if anyone was able to make the native bridge working, I'd be glad to hear how to did it :)

mnichols commented 7 years ago

@MathieuMailhos there is a missing bit in the Java stuff from that download. Follow the ReactNative docs for creating Native bridges for Android. Specifically make sure your MainActivity and MainApplication files have properly registered the module. I've gotten pretty far along getting the android and ios sdks bound to ReactNative for user pool support but not quite ready. Their programming model isn't very friendly so reconsidering the approach.

mmailhos commented 7 years ago

Yes!

So I started to add the missing jars from the SDK:

$ ls android/app/libs/
aws-android-sdk-cognito-2.3.8.jar                 
aws-android-sdk-core-2.3.8.jar
aws-android-sdk-cognitoidentityprovider-2.3.8.jar

I updated MainApplication.java with:

import com.rctcognito.RCTCognitoPackage;
// and later in the List<ReactPackage>
new RCTCognitoPackage(),

I created the folder in src/main/java/com/rctcognito which includes: AppHelper.java Cognito.java (both from the AWS topic) and RCTCognitoPackage.java, which is the Reat module:

package com.rctcognito;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class RCTCognitoPackage implements ReactPackage {

        @Override
        public List<Class<? extends JavaScriptModule>> createJSModules() {
                return Collections.emptyList();
        }

        @Override
        public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
                return Collections.emptyList();
        }

        @Override
        public List<NativeModule> createNativeModules( ReactApplicationContext reactContext) {
                List<NativeModule> modules = new ArrayList<>();
                modules.add(new Cognito(reactContext));
                return modules;
        }
}

After all of that, I had a nicely compiling project (which means that package is well-loaded). I Just had a problem to import it when doing:

import { NativeModules } from 'react-native';
module.exports = NativeModules.Cognito
import Cognito from './Cognito';

I have a big doubt about ./Cognito syntax

mnichols commented 7 years ago

Ah..that's wrong. The module is found at NativeModules.Cognito. So you can const { Cognito } = NativeModules and that should werk

On Sat, Jan 7, 2017 at 3:38 PM, Mathieu notifications@github.com wrote:

Yes!

So I started to add the missing jars from the SDK:

$ ls android/app/libs/ aws-android-sdk-cognito-2.3.8.jar aws-android-sdk-core-2.3.8.jar aws-android-sdk-cognitoidentityprovider-2.3.8.jar

I updated MainApplication.java with:

import com.rctcognito.RCTCognitoPackage; // and later in the List new RCTCognitoPackage(),

I created the folder in src/main/java/com/rctcognito which includes: AppHelper.java Cognito.java (both from the AWS topic) and RCTCognitoPackage.java, which is the Reat module:

package com.rctcognito;

import com.facebook.react.ReactPackage; import com.facebook.react.bridge.JavaScriptModule; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList; import java.util.Collections; import java.util.List;

public class RCTCognitoPackage implements ReactPackage {

    @Override
    public List<Class<? extends JavaScriptModule>> createJSModules() {
            return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
            return Collections.emptyList();
    }

    @Override
    public List<NativeModule> createNativeModules( ReactApplicationContext reactContext) {
            List<NativeModule> modules = new ArrayList<>();
            modules.add(new Cognito(reactContext));
            return modules;
    }

}

After all of that, I had a nicely compiling project (which means that package is well-loaded). I Just had a problem to import it when doing:

import { NativeModules } from 'react-native'; module.exports = NativeModules.ToastAndroid; import Cognito from './Cognito';

I have a big doubt about ./Cognito syntax

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/awslabs/aws-sdk-react-native/issues/27#issuecomment-271115111, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAsC3I1tM18B7Om2J5U8QDd0sn5EY4Nks5rQBPvgaJpZM4LJrt4 .

lielran commented 7 years ago

@mnichols - I found it strange that the basic part is not supported(get a token) while the advanced is shown in the examples.

also, the solution above is valid for Android,right?

what about wrapping all aws apis via some v4 wrapper(e.g. https://github.com/leimd/react-native-aws-signature )

mnichols commented 7 years ago

The idToken you get from Cognito's identity provider is what you use to connect to AWS services. The way you obtain that idToken depends on how you have integrated your Cognito Identity Pool with your identity provider (Cognito User Pool, Facebook, whatever...). As far as refresh tokens with the REST api and so on...I don't know because I am using their SDK directly so haven't really needed to use the raw api, but I am sure it is possible.

On Mon, Feb 6, 2017 at 3:41 PM, Mathieu notifications@github.com wrote:

@mnichols https://github.com/mnichols I was able to succesfuly logon with Facebook on my App and to get:

{ "AccessKey": , "Expiration": , "SecretKey": , "SessionKey": } { identityId: ":" }

However, I still have a question. How do I validate, on a remote REST API, that those credentials are valid? I do not see any Credentials.validate() in any SDK. It seems that I need to use this credential object and use it against an AWS service pretty much anything right? Thank you very much

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/awslabs/aws-sdk-react-native/issues/27#issuecomment-277838178, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAsC48dZ5xtZQhMfPFCZvHRlzBBCEM_ks5rZ6GEgaJpZM4LJrt4 .

nihp commented 6 years ago

@mnichols I am trying to get the token from the user pool. I don't know where i have lost. I can't able to create a user in the pool(Users and groups). Kindly provide any sample code to get the token. I am following this link https://medium.com/@thexap/user-authentication-with-react-native-and-amazon-cognito-982cca085916