BerndWessels / flutter_aws_app

Example flutter app using AWS Cognito and AppSync GraphQL API
28 stars 6 forks source link

Question About Custom UI and Federated Identity #2

Open doc-rj-celltrak opened 4 years ago

doc-rj-celltrak commented 4 years ago

Hi,

First of all, thanks for providing this, it's very helpful!

My team needs to create a custom signup/signin UI in Flutter with AWS Cognito. We need to support Federated identity login via a SAML identity provider (Azure B2C), and also support authentication through our own user pool.

However, we do not want to use the AWS Cognito hosted UI. One other important piece is that we are all new to AWS Cognito, so we have a lot to learn. :)

In your blog post, you mentioned: "If you want your own fancy Sign Up/In UI including Federated Identities for a better user experience you are stuck with using the Cognito Identity Pool in combination with Cognito User Pool."

Do you have an example of how to provide a custom UI and use Cognito Identity Pool in combination with Cognito User Pool?

Your project here uses the stock hosted UI, but would it be difficult to replace this with a custom UI?

Thanks!

BerndWessels commented 4 years ago

@doc-rj-celltrak Hi There hasn't been any official progress from the AWS Amplify team in regards to Flutter support yet (keep pushing here) . But there is some more inofficial activity from the community. Your best bet at the moment is this package which should give you all the bits and pieces you need: https://pub.dev/packages/amazon_cognito_identity_dart_2 Hope that helps Cheers Bernd

doc-rj-celltrak commented 4 years ago

Thanks Bernd, that is very helpful. Regarding a fully custom UI (without using the AWS hosted UI), we were happy to find that it is indeed possible without using Cognito Identity Pools and using only User Pool.

It's not very well advertised and it's not exposed in their SDKs, but Amazon does encourage calling the /oauth2/authorize endpoint directly when needed, and that call goes through Cognito first and then gets redirected to the federated auth page/flow. This is the same call that happens when you click on the federated login button in their hosted UI.

So, it seems that the entire signup/signin UI can be customized using User Pool only, but you still need a webview for the federated auth page via the authorize API.

It's mentioned in step #5 of this post: https://aws.amazon.com/blogs/mobile/amazon-cognito-user-pools-supports-federation-with-saml/

Also, in the 2nd paragraph of this post: https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-user-pool-oauth-2-0-grants/

And, the API docs here: https://docs.aws.amazon.com/cognito/latest/developerguide/authorization-endpoint.html

All of the parameters for that call are defined in the Cognito User Pool config/setup.

Thanks again for your help, Ryan

BerndWessels commented 4 years ago

@doc-rj-celltrak Hi thanks for the info. Sounds interesting and I'll have a play with it. If it works we would only need to trigger the Google/Facebook authentication via Dart Libraries if available otherwise via WebView and their dedicated login sites and then use the tokens directly with the UserPool. Let's keep posting our research results on this topic here. Cheers Bernd

doc-rj-celltrak commented 4 years ago

Sure, that's a great idea. We found that there is an Android Cognito Auth SDK that makes it easy to take the redirect URI and create a session with it, and then use that session to feed tokens to the AppSyncClient. Something like the Android pseudocode below... but getting this to work in Flutter will probably require using platform channels (a new package).

private Auth auth;
...
void initCognito() {
      //  -- Create an instance of Auth --
      this.auth = new Auth.Builder().setAppClientId(getString(R.string.cognito_client_id))
                //.setAppClientSecret(getString(R.string.cognito_client_secret))
                .setAppCognitoWebDomain(getString(R.string.cognito_web_domain))
                .setApplicationContext(getApplicationContext())
                .setAuthHandler(new callback())
                .setSignInRedirect(getString(R.string.app_redirect))
                .setSignOutRedirect(getString(R.string.app_redirect))
                .build();
}

class callback implements AuthHandler {
        @Override
        public void onSuccess(AuthUserSession authUserSession) {
            // after successful auth
            doAppSync(authUserSession);
        }

        @Override
        public void onSignout() {
            // blah
        }

        @Override
        public void onFailure(Exception e) {
            Log.e(TAG, "Failed to auth", e);
        }
}

// after getting redirect URI from federated login, decode redirectUri to auth tokens
auth.getTokens(redirectUri);
...
private void doAppSync(final AuthUserSession authUserSession) {
        appSyncClient = AWSAppSyncClient.builder()
                .context(getApplicationContext())
                .awsConfiguration(new AWSConfiguration(getApplicationContext()))
                .cognitoUserPoolsAuthProvider(new CognitoUserPoolsAuthProvider() {
                    @Override
                    public String getLatestAuthToken() {
                        return authUserSession.getAccessToken().getJWTToken();
                    }
                })
                .build();

        appSyncClient.query(GetPatientsQuery.builder().build())
                .responseFetcher(AppSyncResponseFetchers.CACHE_AND_NETWORK)
                .enqueue(patientsCallback);
}
BerndWessels commented 4 years ago

Flutter has already a good amount of packages for that like https://pub.dev/packages/oauth2 https://pub.dev/packages/google_sign_in https://pub.dev/packages/flutter_facebook_login and many more. With the returned tokens we might be able to use the authentication via UserPool endpoint right away.

doc-rj-celltrak commented 4 years ago

Ok thanks... However, we found that there is a potentially better way to use user pools with fully custom UI and federation. It may not be necessary to call the authorize endpoint directly.

https://aws-amplify.github.io/docs/sdk/android/authentication#configuring-hosted-ui-to-launch-facebook-google-saml-sign-in-directly

These APIs are available for both Android and iOS through the AWSMobileClient singleton. This client also handles session (token) management behind the scenes, and works with AppSync, etc: https://aws.amazon.com/blogs/mobile/aws-amplify-simplifies-development-for-ios-and-android-developers/

So, we're hoping we can use these APIs through platform channels and create a smooth custom UI experience.

We still have to prove it out, and I'm a little worried that it may still require a Cognito Identity pool. However, if that happens, maybe we can call the authorize endpoint directly and then pass the resulting redirect URI into AWSMobileClient.default().handleAuthResponse(). https://aws-amplify.github.io/docs/sdk/android/authentication#setup-amazon-cognito-hosted-ui-in-android-app

Hopefully this API doesn't depend on a Cognito Identity pool. More investigation to be done! What a tangled web the AWS docs are. :/

doc-rj-celltrak commented 4 years ago

Update on the above. We found that this last solution works well with User Pool only and no Identity Pool. We're in the process of creating a plugin for it and may publish eventually.