aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.41k stars 2.12k forks source link

Custom claims not working when also having @auth { allow: private, provider: iam } #8372

Closed pr1ze closed 1 year ago

pr1ze commented 3 years ago

Describe the bug Trying to use custom claims (groupClaim or identityClaim) does not work when also having @auth { allow: private, provider: iam }

The example provded in "to Reproduce" is based on group claim - however its neither working if doing @auth { allow: owner, ownerField: "name", identityClaim: "shopName"

What i am trying to solve is doing multi tenancy while also being able to access the API from lambdas using IAM roles.

To Reproduce

type Shop
@model
@auth(rules: [
    { allow: groups, groupsField: "name", groupClaim: "shopName" }
    { allow: groups, groups: ["administrators"] },
    { allow: private, provider: iam }
])
{
    id: ID!
    name: String!
    employees: [User]! @connection(keyName: "byShop", fields: ["id"])
}
type User
@model
@auth(rules: [
    { allow: owner },
    { allow: groups, groups: ["administrators"] },
    { allow: private, provider: iam }
])
@key(name: "byEmail", fields: ["email", "id"], queryField: "userByEmail")
@key(name: "byShop", fields: ["shopID"]) {
    id: ID!
    email: String!
    shopID: ID!
    shop: Shop! @connection(fields: ["shopID"])
    owner: String!
}
exports.handler = async (event, context, callback) => {
  event.response = {
    claimsOverrideDetails: {
      claimsToAddOrOverride: {
        shopName: "fooShop",
      },
    },
  };
  // Return to Amazon Cognito
  callback(null, event);
};
await Auth.signIn("Some user created in cognito", "with owner id saved in user "owner" field");
await DataStore.query(User);
await DataStore.query(Shop);

Then this error is thrown in console:

> _[WARN] 06:50.942 DataStore - queryError User is unauthorized, some items could not be returned._
> Both queries return 0 entities.
> 

Expected behavior I Expected the user & the shop to be synced to the DataStore and be returned from the quries shown above.

Code Snippet

I am able to make the first "sync" work if i do:

Amplify.configure({
    ...config,
    API: {
        graphql_headers: async () => {
            const session = await Auth.currentSession();
            return {
                Authorization: session.getIdToken().getJwtToken(),
            };
        },
    }
})

However this just results in another error, where it get an Unauthorized for setting up the subscription. So removed this code peace again, as it is neither documented in the official amplify docs.

Both with & without adding the idtoken as authorization header i observed: Doing some digging i discovered that it is using IAM instead of AMAZON_COGNITO_USER_POOLS for setting up subscriptions (Same applies for all subscriptions):

[DEBUG] 06:49.561 PubSub - subscribe options 
Object {provider: Symbol(INTERNAL_AWS_APPSYNC_REALTIME_PUBSUB_PROVIDER), appSyncGraphqlEndpoint: "https://ubgjraf4zbcbxndz3pcqc3xvda.appsync-api.eu-central-1.amazonaws.com/graphql", authenticationType: "AWS_IAM", apiKey: undefined, query: "subscription operation {\n onDeleteUser {\n id\n …d\n shop {\n id\n _deleted\n }\n }\n}\n", ...}

I would have expected that is was using AMAZON_COGNITO_USER_POOLS because it is configured as default in my aws-exports.js. I do not know if this is related to the problem with syncing. But it looks like

´datastore/src/sync/processors/subscription.ts´ -> `private getAuthorizationInfo(
        model: SchemaModel,
        userCredentials: USER_CREDENTIALS,
        cognitoTokenPayload: { [field: string]: any } = {},
        oidcTokenPayload: { [field: string]: any } = {}
    ):` is not respecting the  `"aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS",`

From this code piece in getAuthorizationInfo - the IAM is chosen over AMAZON_COGNITO_USER_POOLS.

private getAuthorizationInfo(
        model: SchemaModel,
        userCredentials: USER_CREDENTIALS,
        cognitoTokenPayload: { [field: string]: any } = {},
        oidcTokenPayload: { [field: string]: any } = {}
    ): {
        authMode: GRAPHQL_AUTH_MODE;
        isOwner: boolean;
        ownerField?: string;
        ownerValue?: string;
    } {
.....
        // check if has iam authorization
        if (
            userCredentials === USER_CREDENTIALS.unauth ||
            userCredentials === USER_CREDENTIALS.auth
        ) {
            const iamPublicAuth = rules.find(
                rule => rule.authStrategy === 'public' && rule.provider === 'iam'
            );

            if (iamPublicAuth) {
                return { authMode: GRAPHQL_AUTH_MODE.AWS_IAM, isOwner: false };
            }

            const iamPrivateAuth =
                userCredentials === USER_CREDENTIALS.auth &&
                rules.find(
                    rule => rule.authStrategy === 'private' && rule.provider === 'iam'
                );

            if (iamPrivateAuth) {
                return { authMode: GRAPHQL_AUTH_MODE.AWS_IAM, isOwner: false };
            }
        }

What is Configured?

React native app - package.json:

{
    "main": "node_modules/expo/AppEntry.js",
    "scripts": {
        "start": "expo start",
        "android": "expo start --android",
        "ios": "expo start --ios",
        "web": "expo start --web",
        "eject": "expo eject",
        "amplify-modelgen": "node amplify\\scripts\\amplify-modelgen.js",
        "amplify-push": "node amplify\\scripts\\amplify-push.js"
    },
    "dependencies": {
        "@aws-amplify/api": "^3.2.7",
        "@aws-amplify/datastore": "^2.6.1",
        "@aws-amplify/pubsub": "^3.2.5",
        "@react-native-community/masked-view": "0.1.10",
        "@react-native-community/netinfo": "5.9.6",
        "@react-native-community/slider": "3.0.3",
        "@react-navigation/drawer": "^5.5.1",
        "@react-navigation/material-bottom-tabs": "^5.2.10",
        "@react-navigation/native": "^5.5.1",
        "@react-navigation/stack": "^5.5.1",
        "@unimodules/core": "~5.5.0",
        "aws-amplify": "^3.3.4",
        "aws-amplify-react-native": "^4.2.7",
        "expo": "^39.0.0",
        "expo-av": "~8.6.0",
        "expo-camera": "~9.0.0",
        "expo-constants": "~9.2.0",
        "expo-device": "~2.3.0",
        "expo-font": "~8.3.0",
        "expo-image-manipulator": "~8.3.0",
        "expo-keep-awake": "~8.3.0",
        "expo-linking": "^1.0.3",
        "expo-notifications": "~0.7.2",
        "expo-permissions": "~9.3.0",
        "expo-screen-orientation": "~2.0.0",
        "expo-splash-screen": "~0.6.1",
        "expo-updates": "~0.3.3",
        "expo-video-player": "^1.5.8",
        "expo-video-thumbnails": "~4.3.0",
        "expo-web-browser": "~8.5.0",
        "inquirer": "^7.2.0",
        "react": "16.13.1",
        "react-dom": "16.13.1",
        "react-native": "https://github.com/expo/react-native/archive/sdk-39.0.3.tar.gz",
        "react-native-aws3": "^0.0.9",
        "react-native-fs": "^2.16.6",
        "react-native-gesture-handler": "~1.7.0",
        "react-native-keyboard-aware-scroll-view": "^0.9.1",
        "react-native-paper": "~3.10.1",
        "react-native-reanimated": "~1.13.0",
        "react-native-safe-area-context": "3.1.4",
        "react-native-screens": "~2.10.1",
        "react-native-search-header": "^0.3.5",
        "react-native-svg": "12.1.0",
        "react-native-svg-transformer": "^0.14.3",
        "react-native-tab-view": "^2.14.4",
        "react-native-uuid": "^1.4.9",
        "react-native-vector-icons": "^6.6.0",
        "react-native-web": "~0.13.7",
        "react-navigation": "^4.3.9",
        "react-navigation-material-bottom-tabs": "^2.2.12"
    },
    "devDependencies": {
        "@babel/core": "^7.8.6",
        "@types/react": "~16.9.35",
        "@types/react-native": "~0.63.2",
        "babel-preset-expo": "^8.3.0",
        "ini": "^1.3.5",
        "typescript": "~3.9.2"
    },
    "private": true
}

aws-exports.js

const awsmobile = {
    "aws_project_region": "eu-central-1",
    "aws_cognito_identity_pool_id": "eu-central-1:b6caf1e1-a91e-4099-9249-ea99a9d95132",
    "aws_cognito_region": "eu-central-1",
    "aws_user_pools_id": "eu-central-1_TpCHLAtpD",
    "aws_user_pools_web_client_id": "7uim50j5pgha43fvjekd34fv48",
    "oauth": {},
    "aws_cloud_logic_custom": [
        {
            "name": "AdminQueries",
            "endpoint": "https://nrng6ss6sl.execute-api.eu-central-1.amazonaws.com/test",
            "region": "eu-central-1"
        },
        {
            "name": "LandingLinkAPI",
            "endpoint": "https://y9o25l4f42.execute-api.eu-central-1.amazonaws.com/test",
            "region": "eu-central-1"
        }
    ],
    "aws_appsync_graphqlEndpoint": "https://ubgjraf4zbcbxndz3pcqc3xvda.appsync-api.eu-central-1.amazonaws.com/graphql",
    "aws_appsync_region": "eu-central-1",
    "aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS",
    "aws_user_files_s3_bucket": "diialoggstatic211857-test",
    "aws_user_files_s3_bucket_region": "eu-central-1"
};

How amplify is configured in App.tsx

Amplify.configure({
    ...config,
    Analytics: {
        disabled: true
    }
})
loganpowell commented 2 years ago

I am also trying to do this to enable dynamic unauthenticated access to some resources

chrisbonifacio commented 2 years ago

@pr1ze Apologies for the delay on this issue. We've since made many updates to how we handle authorization. If you are still trying to get this to work, please try updating to the latest version of the aws-amplify package as well as the Amplify CLI @aws-amplify/cli and let us know if you are still in need of assistance.

@loganpowell if you are also still experiencing your issue, please open a new issue and fill out the bug form thoroughly so that we are able to reproduce and assist with feedback or a fix if there is a bug.

prafullatandel commented 2 years ago

Hi I am having same issue where the idToken does receive the custom:tenantId but the Datastore syncs all the tenant records. I have exact same setup as above. I am using latest aws-amplify "aws-amplify": "^4.3.33" type Tenant @model @auth( rules: [ { allow: private, provider: iam, operations: [read] } { allow: groups, groups: ["ITSuperAdmin", "ITAdmin"] } { allow: owner, ownerField: "id", identityClaim: "custom:tenantId"} ] ) { id: ID! @primaryKey name: String! }

can you please help understand what i might have missed.

erinleigh90 commented 1 year ago

@pr1ze @prafullatandel, have you tried setting the DataStore authModeStrategyType to AuthModeStrategyType.MULTI_AUTH in Amplify.configure()? If you check the documentation here it may help with your issue.

chrisbonifacio commented 1 year ago

Hi 👋 Closing this as we have not heard back from you. If you are still experiencing this issue and in need of assistance, please feel free to comment and provide us with any information previously requested by our team members so we can re-open this issue and be better able to assist you.

Thank you!