aws-amplify / amplify-flutter

A declarative library with an easy-to-use interface for building Flutter applications on AWS.
https://docs.amplify.aws
Apache License 2.0
1.33k stars 247 forks source link

Unauthorized exception when trying to subscribe to DDB streams while using DataStore authorization #2527

Closed skim037 closed 1 year ago

skim037 commented 1 year ago

Description

My usecase is to subscribe to a model and process the data when Lambda Function creates a new record. When I apply DataStore authorization, I don't get notification for the records I'm listening to.

? Select a setting to edit Authorization modes
? Choose the default authorization type for the API Amazon Cognito User Pool
Use a Cognito user pool configured as a part of this project.
? Configure additional auth types? Yes
? Choose the additional authorization types you want to configure for the API API key
API key configuration
✔ Enter a description for the API key: · Public API
✔ After how many days from now the API key should expire (1-365): · 365

Model

type User @model @auth(rules: [
  { allow: private, provider: iam, operations: [read]},
  { allow: owner }
]) {
  id: ID!
  user_id: String! @index(name: "byUserId")
  owner: String @auth(rules: [{ allow: owner, operations: [read, delete] }])
}

Subscription

When the app starts, it subscribes to listen to DDB changes.

  observeUser({required String userId}) {
    final itemStream = ddbRepo.observeUser(userId);
      final subscription = itemStream.listen((event) async {
        // logic
      });
    }
  } 

Repository

  Stream<QuerySnapshot<Delete>> observeDelete(String userId) {
    return Amplify.DataStore.observeQuery(User.classType, where: User.USER_ID.eq(userId));
  }

Lambda Function

I'm creating new User record from Lambda function following this guide. https://docs.amplify.aws/cli/function/#iam-authorization


After reading https://docs.amplify.aws/cli/graphql/authorization-rules/#field-level-authorization-rules, I tried something like this.

type User @model @auth(rules: [
  { allow: private, provider: iam, operations: [read]},
  { allow: owner }
]) {
  id: ID! @auth(rules: [{ allow: public, operations: [read] }]) 
  user_id: String! @auth(rules: [{ allow: public, operations: [read] }]) @index(name: "byUserId")
  owner: String @auth(rules: [{ allow: owner, operations: [read, delete] }])
}

But it gives me following error.

When using field-level authorization rules you need to add rules to all of the model's required fields with at least read permissions. Found model "User" with required fields ["id","user_id"] missing field-level authorization rules.

When the app starts, I do see lots of unauthorized errors.

[IncomingAsyncSubscriptionEventToAnyModelMapper] Received subscription: PassthroughSubject
ConnectionProviderError.jsonParse; identifier=F948F50D-ECB7-49CB-B022-A6885CEEB31D; additionalInfo=Optional(["errors": AppSyncRealTimeClient.AppSyncJSONValue.array([AppSyncRealTimeClient.AppSyncJSONValue.object(["message": AppSyncRealTimeClient.AppSyncJSONValue.string("Not Authorized to access onDeleteMessage on type Subscription"), "errorType": AppSyncRealTimeClient.AppSyncJSONValue.string("Unauthorized")])])])

Categories

Steps to Reproduce

Test Scenario

  1. When the app starts, it calls observeUser with userId 12345.
  2. Trigger Lambda function to create a new User record with userId 12345.

Expected behavior

User model allows read operation for authenticated users. So my expectation is that when Lambda function creates a record with userId the app is listening to, the app should be notified.

Actual behavior

The app doesn't get notified when Lambda creates a record with userId the app is listening to.

Screenshots

No response

Platforms

Android Device/Emulator API Level

No response

Environment

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.3.10, on macOS 13.0 22A380 darwin-arm, locale en-US)
[✓] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1)
[✓] Xcode - develop for iOS and macOS (Xcode 14.2)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.3)
[✓] VS Code (version 1.74.0)
[✓] Connected device (3 available)
[✓] HTTP Host Availability

Dependencies

Dart SDK 2.18.6
Flutter SDK 3.3.10
chummy 1.0.0+1

dependencies:
- amplify_analytics_pinpoint 0.6.10 [amplify_analytics_pinpoint_android amplify_analytics_pinpoint_ios amplify_core aws_common flutter meta]
- amplify_api 0.6.10 [amplify_api_android amplify_api_ios amplify_core amplify_flutter aws_common collection flutter meta plugin_platform_interface]
- amplify_auth_cognito 0.6.10 [amplify_auth_cognito_android amplify_auth_cognito_ios amplify_core aws_common collection flutter meta plugin_platform_interface]
- amplify_authenticator 0.2.4 [amplify_auth_cognito amplify_core amplify_flutter async aws_common collection flutter flutter_localizations intl stream_transform]
- amplify_datastore 0.6.10 [flutter amplify_datastore_plugin_interface amplify_core plugin_platform_interface meta collection async]
- amplify_flutter 0.6.10 [amplify_core amplify_datastore_plugin_interface amplify_flutter_android amplify_flutter_ios aws_common collection flutter meta plugin_platform_interface]
- amplify_storage_s3 0.6.10 [amplify_storage_s3_android amplify_storage_s3_ios amplify_core aws_common flutter meta plugin_platform_interface path_provider path]
- cached_network_image 3.2.3 [flutter flutter_cache_manager octo_image cached_network_image_platform_interface cached_network_image_web]
- connectivity_plus 3.0.2 [flutter flutter_web_plugins connectivity_plus_platform_interface js meta nm]
- cupertino_icons 1.0.5
- dotted_border 2.0.0+2 [flutter path_drawing]
- dropdown_button2 1.8.5 [flutter]
- email_validator 2.1.17
- firebase_core 2.3.0 [firebase_core_platform_interface firebase_core_web flutter meta]
- firebase_messaging 14.1.1 [firebase_core firebase_core_platform_interface firebase_messaging_platform_interface firebase_messaging_web flutter meta]
- flutter 0.0.0 [characters collection material_color_utilities meta vector_math sky_engine]
- flutter_app_badger 1.5.0 [flutter]
- flutter_bloc 8.1.1 [flutter bloc provider]
- flutter_cache_manager 3.3.0 [clock collection file flutter http path path_provider pedantic rxdart sqflite uuid]
- flutter_image_compress 1.1.3 [flutter]
- flutter_local_notifications 12.0.4 [clock flutter flutter_local_notifications_linux flutter_local_notifications_platform_interface timezone]
- flutter_native_splash 2.2.15 [args flutter flutter_web_plugins js html image meta path universal_io xml yaml]
- flutter_slidable 2.0.0 [flutter]
- geocoding 2.0.5 [flutter geocoding_platform_interface]
- geolocator 9.0.2 [flutter geolocator_platform_interface geolocator_android geolocator_apple geolocator_web geolocator_windows]
- google_maps_flutter 2.2.1 [flutter google_maps_flutter_android google_maps_flutter_ios google_maps_flutter_platform_interface]
- google_mobile_ads 2.3.0 [meta flutter visibility_detector]
- google_place 0.4.7 [http]
- image_picker 0.8.6 [flutter image_picker_android image_picker_for_web image_picker_ios image_picker_platform_interface]
- intl 0.17.0 [clock path]
- material_design_icons_flutter 6.0.7096 [flutter]
- path_provider 2.0.11 [flutter path_provider_android path_provider_ios path_provider_linux path_provider_macos path_provider_platform_interface path_provider_windows]
- percent_indicator 4.2.2 [flutter]
- permission_handler 10.2.0 [flutter meta permission_handler_android permission_handler_apple permission_handler_windows permission_handler_platform_interface]
- quiver 3.1.0 [matcher]
- social_login_buttons 1.0.7 [flutter]
- url_launcher 6.1.7 [flutter url_launcher_android url_launcher_ios url_launcher_linux url_launcher_macos url_launcher_platform_interface url_launcher_web url_launcher_windows]

transitive dependencies:
- _flutterfire_internals 1.0.9 [cloud_firestore_platform_interface cloud_firestore_web collection firebase_core firebase_core_platform_interface flutter meta]
- amplify_analytics_pinpoint_android 0.6.10 [flutter]
- amplify_analytics_pinpoint_ios 0.6.10 [flutter]
- amplify_api_android 0.6.10 [flutter]
- amplify_api_ios 0.6.10 [amplify_core flutter]
- amplify_auth_cognito_android 0.6.10 [flutter]
- amplify_auth_cognito_ios 0.6.10 [amplify_core flutter]
- amplify_core 0.6.10 [aws_common collection flutter intl json_annotation meta plugin_platform_interface uuid]
- amplify_datastore_plugin_interface 0.6.10 [amplify_core collection flutter meta]
- amplify_flutter_android 0.6.10 [flutter]
- amplify_flutter_ios 0.6.10 [amplify_core flutter]
- amplify_storage_s3_android 0.6.10 [flutter]
- amplify_storage_s3_ios 0.6.10 [flutter]
- archive 3.3.4 [crypto path pointycastle]
- args 2.3.1
- async 2.9.0 [collection meta]
- aws_common 0.1.1 [async collection http meta stream_transform uuid]
- bloc 8.1.0 [meta]
- boolean_selector 2.1.0 [source_span string_scanner]
- cached_network_image_platform_interface 2.0.0 [flutter flutter_cache_manager]
- cached_network_image_web 1.0.2 [flutter flutter_cache_manager cached_network_image_platform_interface]
- characters 1.2.1
- clock 1.1.1
- cloud_firestore_platform_interface 5.9.0 [_flutterfire_internals collection firebase_core flutter meta plugin_platform_interface]
- cloud_firestore_web 3.1.0 [_flutterfire_internals cloud_firestore_platform_interface collection firebase_core firebase_core_web flutter flutter_web_plugins js]
- collection 1.16.0
- connectivity_plus_platform_interface 1.2.3 [flutter meta plugin_platform_interface]
- convert 3.1.1 [typed_data]
- cross_file 0.3.3+2 [js meta]
- crypto 3.0.2 [typed_data]
- csslib 0.17.2 [source_span]
- dbus 0.7.8 [args ffi meta xml]
- fake_async 1.3.1 [clock collection]
- ffi 2.0.1
- file 6.1.4 [meta path]
- firebase_core_platform_interface 4.5.2 [collection flutter flutter_test meta plugin_platform_interface]
- firebase_core_web 2.0.1 [firebase_core_platform_interface flutter flutter_web_plugins js meta]
- firebase_messaging_platform_interface 4.2.7 [_flutterfire_internals firebase_core flutter meta plugin_platform_interface]
- firebase_messaging_web 3.2.7 [_flutterfire_internals firebase_core firebase_core_web firebase_messaging_platform_interface flutter flutter_web_plugins js meta]
- flutter_blurhash 0.7.0 [flutter]
- flutter_local_notifications_linux 2.0.0 [flutter flutter_local_notifications_platform_interface dbus path xdg_directories]
- flutter_local_notifications_platform_interface 6.0.0 [flutter plugin_platform_interface]
- flutter_localizations 0.0.0 [flutter intl characters clock collection material_color_utilities meta path vector_math]
- flutter_plugin_android_lifecycle 2.0.7 [flutter]
- flutter_test 0.0.0 [flutter test_api path fake_async clock stack_trace vector_math async boolean_selector characters collection matcher material_color_utilities meta source_span stream_channel string_scanner term_glyph]
- flutter_web_plugins 0.0.0 [flutter js characters collection material_color_utilities meta vector_math]
- geocoding_platform_interface 2.0.1 [flutter meta plugin_platform_interface]
- geolocator_android 4.1.4 [flutter geolocator_platform_interface]
- geolocator_apple 2.2.2 [flutter geolocator_platform_interface]
- geolocator_platform_interface 4.0.6 [flutter plugin_platform_interface vector_math meta]
- geolocator_web 2.1.6 [flutter flutter_web_plugins geolocator_platform_interface]
- geolocator_windows 0.1.1 [flutter geolocator_platform_interface]
- google_maps_flutter_android 2.3.2 [flutter flutter_plugin_android_lifecycle google_maps_flutter_platform_interface stream_transform]
- google_maps_flutter_ios 2.1.12 [flutter google_maps_flutter_platform_interface stream_transform]
- google_maps_flutter_platform_interface 2.2.4 [collection flutter plugin_platform_interface stream_transform]
- html 0.15.1 [csslib source_span]
- http 0.13.5 [async http_parser meta path]
- http_parser 4.0.1 [collection source_span string_scanner typed_data]
- image 3.2.2 [archive meta xml]
- image_picker_android 0.8.5+3 [flutter flutter_plugin_android_lifecycle image_picker_platform_interface]
- image_picker_for_web 2.1.10 [flutter flutter_web_plugins image_picker_platform_interface]
- image_picker_ios 0.8.6+1 [flutter image_picker_platform_interface]
- image_picker_platform_interface 2.6.2 [cross_file flutter http plugin_platform_interface]
- js 0.6.4
- json_annotation 4.7.0 [meta]
- matcher 0.12.12 [stack_trace]
- material_color_utilities 0.1.5
- meta 1.8.0
- nested 1.0.0 [flutter]
- nm 0.5.0 [dbus]
- octo_image 1.0.2 [flutter flutter_blurhash]
- path 1.8.2
- path_drawing 1.0.1 [vector_math meta path_parsing flutter]
- path_parsing 1.0.1 [vector_math meta]
- path_provider_android 2.0.20 [flutter path_provider_platform_interface]
- path_provider_ios 2.0.11 [flutter path_provider_platform_interface]
- path_provider_linux 2.1.7 [ffi flutter path path_provider_platform_interface xdg_directories]
- path_provider_macos 2.0.6 [flutter path_provider_platform_interface]
- path_provider_platform_interface 2.0.5 [flutter platform plugin_platform_interface]
- path_provider_windows 2.1.3 [ffi flutter path path_provider_platform_interface win32]
- pedantic 1.11.1
- permission_handler_android 10.2.0 [flutter permission_handler_platform_interface]
- permission_handler_apple 9.0.7 [flutter permission_handler_platform_interface]
- permission_handler_platform_interface 3.9.0 [flutter meta plugin_platform_interface]
- permission_handler_windows 0.1.2 [flutter permission_handler_platform_interface]
- petitparser 5.1.0 [meta]
- platform 3.1.0
- plugin_platform_interface 2.1.3 [meta]
- pointycastle 3.6.2 [collection convert js]
- process 4.2.4 [file path platform]
- provider 6.0.3 [collection flutter nested]
- rxdart 0.27.5
- sky_engine 0.0.99
- source_span 1.9.0 [collection path term_glyph]
- sqflite 2.1.0+1 [flutter sqflite_common path]
- sqflite_common 2.3.0 [synchronized path meta]
- stack_trace 1.10.0 [path]
- stream_channel 2.1.0 [async]
- stream_transform 2.0.0
- string_scanner 1.1.1 [source_span]
- synchronized 3.0.0+3
- term_glyph 1.2.1
- test_api 0.4.12 [async boolean_selector collection meta source_span stack_trace stream_channel string_scanner term_glyph matcher]
- timezone 0.9.0 [path]
- typed_data 1.3.1 [collection]
- universal_io 2.0.4 [collection crypto meta typed_data]
- url_launcher_android 6.0.22 [flutter url_launcher_platform_interface]
- url_launcher_ios 6.0.17 [flutter url_launcher_platform_interface]
- url_launcher_linux 3.0.1 [flutter url_launcher_platform_interface]
- url_launcher_macos 3.0.1 [flutter url_launcher_platform_interface]
- url_launcher_platform_interface 2.1.1 [flutter plugin_platform_interface]
- url_launcher_web 2.0.13 [flutter flutter_web_plugins url_launcher_platform_interface]
- url_launcher_windows 3.0.1 [flutter url_launcher_platform_interface]
- uuid 3.0.6 [crypto]
- vector_math 2.1.2
- visibility_detector 0.3.3 [flutter]
- win32 3.0.1 [ffi]
- xdg_directories 0.2.0+2 [meta path process]
- xml 6.1.0 [collection meta petitparser]
- yaml 3.1.1 [collection source_span string_scanner]

Device

iPhone 12

OS

iOS 16.1.2

Deployment Method

Amplify CLI

CLI Version

10.5.2

Additional Context

No response

Amplify Config

NA

HuiSF commented 1 year ago

Hello @skim037 field level auth is not support in DataStore. Defining field level auth may cause DataStore subscription failing which probably the reason you don't receive any event from the observe. Is there any specific use case that you have to use field auth?

skim037 commented 1 year ago

Hi @HuiSF I was following a recommendation from this document where is says

To prevent an owner from reassigning their record to another user, protect the owner field (by default owner: String) with a field-level authorization rule. For example, in a social media app, you would want to prevent Alice from being able to reassign Alice's Post to Bob.

I noticed that this document is for GraphQL. Is this not an issue when working with DataStore? Just wanted to make sure there are no security holes with my service.

I am still having issues with subscription even when I got rid of field-level authorization. I reverted my changes and redeployed with @auth(rules: [{allow: public}]) for all models. But subscription fails with Unauthorized exception as long as I have Amazon Cognito User Pool as a default authorization strategy. When I reverted back to use API key as a default strategy, I was able to subscribe and get notifications.

HuiSF commented 1 year ago

I noticed that this document is for GraphQL. Is this not an issue when working with DataStore?

Per my understanding within DataStore scope, model mutation is handled by the underlying sync engine, which have restrict rules setting owner field. In addition, if you don't define owner field overrides in your schema, the owner field won't be exposed in the model gen generated model classes as well, which should be able to avoid accident reassigning of this field.

If you have multiple auth rules defined on a model, I believe you need to configure DataStore to use multi-auth mode, have you tried this?

skim037 commented 1 year ago

I'm only using @auth(rules: [{allow: public}]) in schema.graphql. This is not using multi-auth mode. Is it?

I reverted my changes and redeployed with @auth(rules: [{allow: public}]) for all models. But subscription fails with Unauthorized exception as long as I have Amazon Cognito User Pool as a default authorization strategy.

HuiSF commented 1 year ago

So you haven't ran amplify update api to change the default authorization back to API key after your reverted back to {allow: public}. I think that's expected as the documentation indicates as below

By default, DataStore uses your API's default authorization type specified in the amplifyconfiguration.json/.dart/aws-exports.js file. Every network request sent through DataStore uses that authorization type, regardless of the model's @auth rule. To change the default authorization type, run amplify update api.

If you'd like the DataStore to pick up an appropriate auth strategy from your configuration (for your use case, userpool as default, API key as an addition), I think you'd need to configure the DataStore to multi-auth mode.

skim037 commented 1 year ago

Hi @HuiSF

I have enabled multi-auth mode and tried few combinations.

Simplified use case

When customers request to delete their account, the request is written to DDB which triggers Lambda function. Lambda function then deletes the user from Cognito pool and update DDB with completed status. The app listens to the DDB stream for completed status and shows delete success message to the customer when this event is received.

Case Default Auth Type Additional Auth Types Auth annotation in schema.graphql Delete request from App saved to DDB Delete complete status from Lambda Function saved to DDB Delete completed event from DDB is notified to App
1 Cognito User Pool API Key,
IAM
@auth(rules: [
{ allow: private, provider: userPools },
{ allow: private, provider: iam } ])
Success Success Success
2 Cognito User Pool API Key,
IAM
@auth(rules: [{ allow: private, provider: iam }]) DataStore failed to perform selective sync (See Error 1)
3 Cognito User Pool API Key,
IAM
@auth(rules: [
{ allow: owner },
{ allow: private, provider: iam } ])
Success(but shows warning 1) Success Fail. New record is in DDB, but no event fired.

So the only combination that satisfied my use case was case 1. I added IAM auth type so that I can update DDB from Lambda function using the approach shown here (https://docs.amplify.aws/cli/function/#iam-authorization).

Question 1 Regarding the error in case 2, It looks like DataStore selective sync doesn't work without userPools provider. Is this expected?

Question 2 Regarding the warning in case 3, Can I safely ignore this warning since DataStore doesn't support field-level auth?

Per my understanding within DataStore scope, model mutation is handled by the underlying sync engine, which have restrict rules setting owner field. In addition, if you don't define owner field overrides in your schema, the owner field won't be exposed in the model gen generated model classes as well, which should be able to avoid accident reassigning of this field.

Question 3 I want to use use owner auth strategy. But it seems impossible to use this auth strategy in DataStore with subscription since it doesn't support field-level auth. Can you please confirm if this is really the case? If so, is this by design?

To prevent sensitive data from being sent over subscriptions, the GraphQL Transformer needs to alter the response of mutations for those fields by setting them to null. Therefore, to facilitate field-level authorization with subscriptions, you need to either apply field-level authorization rules to all required fields, make the other fields nullable, or disable subscriptions by setting it to public or off. https://docs.amplify.aws/cli/graphql/authorization-rules/#field-level-authorization-rules

Error 1

W/amplify:aws-api( 4372): Websocket connection failed.
W/amplify:aws-api( 4372): A subscription error occurred.
W/amplify:aws-api( 4372): ApiException{message=Connection failed., cause=null, recoverySuggestion=Sorry, we don't have a suggested fix for this error yet.}
W/amplify:aws-api( 4372):   at com.amplifyframework.api.aws.SubscriptionEndpoint.requestSubscription(SubscriptionEndpoint.java:145)
W/amplify:aws-api( 4372):   at com.amplifyframework.api.aws.MutiAuthSubscriptionOperation.dispatchRequest(MutiAuthSubscriptionOperation.java:113)
W/amplify:aws-api( 4372):   at com.amplifyframework.api.aws.MutiAuthSubscriptionOperation.$r8$lambda$iziEcYpvlINdYbit2it7fDbbt8A(Unknown Source:0)
W/amplify:aws-api( 4372):   at com.amplifyframework.api.aws.MutiAuthSubscriptionOperation$$ExternalSyntheticLambda4.run(Unknown Source:2)
W/amplify:aws-api( 4372):   at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:463)
W/amplify:aws-api( 4372):   at java.util.concurrent.FutureTask.run(FutureTask.java:264)
W/amplify:aws-api( 4372):   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
W/amplify:aws-api( 4372):   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
W/amplify:aws-api( 4372):   at java.lang.Thread.run(Thread.java:1012)
W/3beagles.chummy( 4372): Long monitor contention with owner pool-26-thread-11 (5017) at void com.amplifyframework.api.aws.SubscriptionEndpoint.requestSubscription(com.amplifyframework.api.graphql.GraphQLRequest, com.amplifyframework.api.aws.AuthorizationType, com.amplifyframework.core.Consumer, com.amplifyframework.core.Consumer, com.amplifyframework.core.Consumer, com.amplifyframework.core.Action)(SubscriptionEndpoint.java:146) waiters=0 in void com.amplifyframework.api.aws.SubscriptionEndpoint.requestSubscription(com.amplifyframework.api.graphql.GraphQLRequest, com.amplifyframework.api.aws.AuthorizationType, com.amplifyframework.core.Consumer, com.amplifyframework.core.Consumer, com.amplifyframework.core.Consumer, com.amplifyframework.core.Action) for 10.203s
E/amplify:aws-datastore( 4372): Failure encountered while attempting to start API sync.
E/amplify:aws-datastore( 4372): DataStoreException{message=DataStore subscriptionProcessor failed to start., cause=DataStoreException{message=Error during subscription., cause=ApiException{message=Connection failed., cause=null, recoverySuggestion=Sorry, we don't have a suggested fix for this error yet.}, recoverySuggestion=Evaluate details.}, recoverySuggestion=Check your internet.}
E/amplify:aws-datastore( 4372):     at com.amplifyframework.datastore.syncengine.Orchestrator.lambda$startApiSync$3$com-amplifyframework-datastore-syncengine-Orchestrator(Orchestrator.java:303)
E/amplify:aws-datastore( 4372):     at com.amplifyframework.datastore.syncengine.Orchestrator$$ExternalSyntheticLambda5.subscribe(Unknown Source:2)
E/amplify:aws-datastore( 4372):     at io.reactivex.rxjava3.internal.operators.completable.CompletableCreate.subscribeActual(CompletableCreate.java:40)
E/amplify:aws-datastore( 4372):     at io.reactivex.rxjava3.core.Completable.subscribe(Completable.java:2850)
E/amplify:aws-datastore( 4372):     at io.reactivex.rxjava3.internal.operators.completable.CompletablePeek.subscribeActual(CompletablePeek.java:51)
E/amplify:aws-datastore( 4372):     at io.reactivex.rxjava3.core.Completable.subscribe(Completable.java:2850)
E/amplify:aws-datastore( 4372):     at io.reactivex.rxjava3.internal.operators.completable.CompletablePeek.subscribeActual(CompletablePeek.java:51)
E/amplify:aws-datastore( 4372):     at io.reactivex.rxjava3.core.Completable.subscribe(Completable.java:2850)
E/amplify:aws-datastore( 4372):     at io.reactivex.rxjava3.internal.operators.completable.CompletablePeek.subscribeActual(CompletablePeek.java:51)
E/amplify:aws-datastore( 4372):     at io.reactivex.rxjava3.core.Completable.subscribe(Completable.java:2850)
E/amplify:aws-datastore( 4372):     at io.reactivex.rxjava3.internal.operators.completable.CompletableSubscribeOn$SubscribeOnObserver.run(CompletableSubscribeOn.java:64)
E/amplify:aws-datastore( 4372):     at io.reactivex.rxjava3.core.Scheduler$DisposeTask.run(Scheduler.java:614)
E/amplify:aws-datastore( 4372):     at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:65)
E/amplify:aws-datastore( 4372):     at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:56)
E/amplify:aws-datastore( 4372):     at java.util.concurrent.FutureTask.run(FutureTask.java:264)
E/amplify:aws-datastore( 4372):     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:307)
E/amplify:aws-datastore( 4372):     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
E/amplify:aws-datastore( 4372):     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
E/amplify:aws-datastore( 4372):     at java.lang.Thread.run(Thread.java:1012)
E/amplify:aws-datastore( 4372): Caused by: DataStoreException{message=Error during subscription., cause=ApiException{message=Connection failed., cause=null, recoverySuggestion=Sorry, we don't have a suggested fix for this error yet.}, recoverySuggestion=Evaluate details.}
E/amplify:aws-datastore( 4372):     at com.amplifyframework.datastore.appsync.AppSyncClient.lambda$subscription$3(AppSyncClient.java:331)
E/amplify:aws-datastore( 4372):     at com.amplifyframework.datastore.appsync.AppSyncClient$$ExternalSyntheticLambda1.accept(Unknown Source:4)
E/amplify:aws-datastore( 4372):     at com.amplifyframework.api.aws.MutiAuthSubscriptionOperation.emitErrorAndCancelSubscription(MutiAuthSubscriptionOperation.java:178)
E/amplify:aws-datastore( 4372):     at com.amplifyframework.api.aws.MutiAuthSubscriptionOperation.lambda$dispatchRequest$2$com-amplifyframework-api-aws-MutiAuthSubscriptionOperation(MutiAuthSubscriptionOperation.java:135)
E/amplify:aws-datastore( 4372):     at com.amplifyframework.api.aws.MutiAuthSubscriptionOperation$$ExternalSyntheticLambda0.accept(Unknown Source:4)
E/amplify:aws-datastore( 4372):     at com.amplifyframework.api.aws.SubscriptionEndpoint.requestSubscription(SubscriptionEndpoint.java:144)
E/amplify:aws-datastore( 4372):     at com.amplifyframework.api.aws.MutiAuthSubscriptionOperation.dispatchRequest(MutiAuthSubscriptionOperation.java:113)
E/amplify:aws-datastore( 4372):     at com.amplifyframework.api.aws.MutiAuthSubscriptionOperation.$r8$lambda$iziEcYpvlINdYbit2it7fDbbt8A(Unknown Source:0)
E/amplify:aws-datastore( 4372):     at com.amplifyframework.api.aws.MutiAuthSubscriptionOperation$$ExternalSyntheticLambda4.run(Unknown Source:2)
E/amplify:aws-datastore( 4372):     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:463)
E/amplify:aws-datastore( 4372):     at java.util.concurrent.FutureTask.run(FutureTask.java:264)
E/amplify:aws-datastore( 4372):     ... 3 more
E/amplify:aws-datastore( 4372): Caused by: ApiException{message=Connection failed., cause=null, recoverySuggestion=Sorry, we don't have a suggested fix for this error yet.}
E/amplify:aws-datastore( 4372):     at com.amplifyframework.api.aws.SubscriptionEndpoint.requestSubscription(SubscriptionEndpoint.java:145)
E/amplify:aws-datastore( 4372):     ... 8 more
W/amplify:aws-datastore( 4372): API sync failed - transitioning to LOCAL_ONLY.
W/amplify:aws-datastore( 4372): DataStoreException{message=DataStore subscriptionProcessor failed to start., cause=DataStoreException{message=Error during subscription., cause=ApiException{message=Connection failed., cause=null, recoverySuggestion=Sorry, we don't have a suggested fix for this error yet.}, recoverySuggestion=Evaluate details.}, recoverySuggestion=Check your internet.}

Warning 1

⚠️ WARNING: owners may reassign ownership for the following model(s) and role(s): ActivityTimestamp: [owner], AppBadgeCount: [owner], BlockedProfile: [owner], Conversation: [owner], Delete: [owner], DeviceRegistration: [owner], Filter: [owner], Message: [owner], MessageQueue: [owner], MoreProfile: [owner], NotificationSetting: [owner], Photo: [owner], Profile: [owner], ProfileInterest: [owner], MembershipType: [owner], PreferredLocation: [owner], PushNotificationSetting: [owner], ReportedProfile: [owner], Username: [owner], UserSearchProperty: [owner], Verification: [owner], ViewProfile: [owner]. If this is not intentional, you may want to apply field-level authorization rules to these fields. To read more: https://docs.amplify.aws/cli/graphql/authorization-rules/#per-user--owner-based-data-access.
HuiSF commented 1 year ago

Hello @skim037 thanks for the details.

Question 1 Regarding the error in case 2, It looks like DataStore selective sync doesn't work without userPools provider. Is this expected?

Looking at the exception in Error 1, it failed to create subscription. Selective sync is a functionality with in the sync process though, and the sync expression you specified is not involved creating subscription. The exception indicates that it may be caused by network connection recoverySuggestion=Check your internet. I'm not sure if it's relevant to your auth configuration.

Question 2 Regarding the warning in case 3, Can I safely ignore this warning since DataStore doesn't support field-level auth?

Per my understanding within DataStore scope, model mutation is handled by the underlying sync engine, which have restrict rules setting owner field. In addition, if you don't define owner field overrides in your schema, the owner field won't be exposed in the model gen generated model classes as well, which should be able to avoid accident reassigning of this field.

Yes within DataStore you can ignore this warning. I suggest not to override the owner field in schema in order to hide the owner filed from the public interface.

Question 3 I want to use use owner auth strategy. But it seems impossible to use this auth strategy in DataStore with subscription since it doesn't support field-level auth. Can you please confirm if this is really the case? If so, is this by design?

Yes DataStore doesn't support field-level at this moment.

skim037 commented 1 year ago

hi @HuiSF

The auth mode I tried is

default: Cognito User Pool
Additional: API Key, IAM

My model is configured to use

@auth(rules: [{ allow: public }])

And I have enabled muti-auth in my code

AmplifyDataStore(
  modelProvider: ModelProvider.instance,
  authModeStrategy: AuthModeStrategy.multiAuth,

When I run the app, I get No authorization error. It looks like API Key access is not allowed when using Cognito User Pool as a default auth mode. Is this a correct statement?

Problem with this approach is that, in order to use owner auth annotation, Cognito User Pool has to be the default auth mode (see issue I reported before). So this makes it impossible to use owner auth annotation for one model and public auth annotation for another model. Is this the proper intention?

HuiSF commented 1 year ago

Hi, sorry for lacking activities on this issue. I've tested DataStore multi-auth based on a few similar issues in the repo, and found that both amplify-swift (v1) and amplify-android (v1+v2) have issues around multi-auth. This causes common issue that models with public permission cannot work correctly without signing in a user.

I'm going to close this issue as a duplicate, so we can track the progress in https://github.com/aws-amplify/amplify-flutter/issues/1693.