Amplify-js: Unauth. & Auth Access to AppSync

Created on 17 Dec 2018  路  21Comments  路  Source: aws-amplify/amplify-js

I try to expose some queries to the public but have mutations only for registered users accessable.

I have configured the cloud formation via amplify cli and auth with enabled unauth access. I have created a appsync project on aws with IAM access settings. Afterwards I added a rule for the unauth-role for one test query and a rule for auth-role with full access to appsync.

The unauth query works just fine but if I want to login via Auth.singIn() from a cognito user, I get a 401 response by trying to execute a auth-only mutation. I checked the identety pool and it has only unauth records. Also no ip acces records are available in the authenticated cognito user pool user.

The docs says that I dont have to call Auth.federatedLogin()after Auth.signIn() because it handles it automatically.

I would like to know what I have missunderstood with this conzept? Maybe its a fully wrong approach, because I also would need the user context in the resolvers?

EDIT I think this is probably the issue, can somebody confirm?

AppSync Auth pending-close-response-required

Most helpful comment

EDIT:
Fuck Amplify, just a major waste of time at this moment, sorry for that but it is just the truth. You will go from issue to issue..

Here the my reasons for this hard statement:

Setup

Amplify CLI Auth configuration will not let you switch authentication types via cli, you have to manually add it to the template file.
Unsolved Issue

_After this you are able to login via verified email or verified phonenumber (alias to username) - Switching to disable usernames and use email or phonenumber is still a unsolved mistery_
-

Auth

If you registered a user via web ui in cognito userpools, your password is temporary. Now if you want to change the password you will recognize the response from Auth.signIn() will have signInUserSession: null which will lead to an exception if you want to Auth.changePassword()
Unsolved Issue

_Could not find a solution for this_

-

After this you may come to the idea to just test, if you are even logged in after calling Auth.signIn(). No you are not - WTF. Try it yourself with calling Auth.currentAuthenticatedUser(s => console.log(s)) within your callback after Auth.signIn(). Now you will end up googling for "cannot load federated user from auth storage".
"Issue Merged - but I got still the same problem?

_At this point I gave up._
-

AppSync

If you want to build real world applications you possible, ok no, chances are about 99% that you will need authenticated and unauthenticated access to your API. Now from several searches you will get 3 possibilities:

  • 2 AppSync APIs (no way for me)
  • IAM Roles (you are in my situation)
  • Cognito with a guest account (no metrics possible for conversion calculations & all resolvers and ui must check if user is guest)
    A waste of time

Conclusion:

I am not an AWS Pro but I learned a bunch of stuff by going from issue to issue, maybe I am doing everything fundamentally wrong. But I dont think so, correct me if this is the case and tell me how to configure a basic setup for most applications.

Use this lib with their logic (withAuthenticator() & AppSync with Cognito User Settings), or leave it completely and use the cognito and appsync lib seperatly instead. Amplify at this state is just for showcasing how fast you could create basic To-Do Apps, embarrasing.

All 21 comments

@NikoMontana can you provide some code snippets about how you sign in and then call the auth-only mutation?

@powerful23 sure!

App.js

  constructor() {
    super();
    this.state = {
      auth: "none"
    };

    window.LOG_LEVEL = "DEBUG";

    this.load = this.load.bind(this);

    Auth.signIn("newUser", "newUserPass123")
      .then(user => {
        console.log(user);
        this.load();
      })
      .catch(err => console.log("err: " + err));
  }

  async load() {
    try {
      const allTodos = await API.graphql(
        graphqlOperation(mutations.createTask, {
          input: { title: "Test Task" }
        })
      );
      console.log(allTodos);
    } catch (error) {
      console.log(error);
    }
  }

index.js

import Amplify from "aws-amplify";
Amplify.configure(AWS_CONFIG);

aws-exports.js

// WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.

const awsmobile = {
  aws_project_region: "eu-central-1",
  aws_cognito_identity_pool_id:
    "eu-central-1:SOME_LONG_STRING",
  aws_cognito_region: "eu-central-1",
  aws_user_pools_id: "eu-central-SOME_LONG_STRING"",
  aws_user_pools_web_client_id: "SOME_LONG_STRING"",
  aws_appsync_graphqlEndpoint:
    "SOME_LONG_STRING",
  aws_appsync_region: "eu-central-1",
  aws_appsync_authenticationType: "AWS_IAM"
};

export default awsmobile;

IAM Auth Role (createTask - not working)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "appsync:GraphQL"
            ],
            "Resource": [
                "arn:aws:appsync:eu-central-1:SOME_LONG_STRING:apis/SOME_LONG_STRING/types/Query/fields/createTask"
            ]
        }
    ]
}

IAM UnAuth Role (getTask - working)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "appsync:GraphQL"
            ],
            "Resource": [
                "arn:aws:appsync:eu-central-1:SOME_LONG_STRING:apis/SOME_LONG_STRING/types/Query/fields/getTask"
            ]
        }
    ]
}

Debug from Window:
amplify_auth
ampify_auth2
amplify_auth3

EDIT:
Fuck Amplify, just a major waste of time at this moment, sorry for that but it is just the truth. You will go from issue to issue..

Here the my reasons for this hard statement:

Setup

Amplify CLI Auth configuration will not let you switch authentication types via cli, you have to manually add it to the template file.
Unsolved Issue

_After this you are able to login via verified email or verified phonenumber (alias to username) - Switching to disable usernames and use email or phonenumber is still a unsolved mistery_
-

Auth

If you registered a user via web ui in cognito userpools, your password is temporary. Now if you want to change the password you will recognize the response from Auth.signIn() will have signInUserSession: null which will lead to an exception if you want to Auth.changePassword()
Unsolved Issue

_Could not find a solution for this_

-

After this you may come to the idea to just test, if you are even logged in after calling Auth.signIn(). No you are not - WTF. Try it yourself with calling Auth.currentAuthenticatedUser(s => console.log(s)) within your callback after Auth.signIn(). Now you will end up googling for "cannot load federated user from auth storage".
"Issue Merged - but I got still the same problem?

_At this point I gave up._
-

AppSync

If you want to build real world applications you possible, ok no, chances are about 99% that you will need authenticated and unauthenticated access to your API. Now from several searches you will get 3 possibilities:

  • 2 AppSync APIs (no way for me)
  • IAM Roles (you are in my situation)
  • Cognito with a guest account (no metrics possible for conversion calculations & all resolvers and ui must check if user is guest)
    A waste of time

Conclusion:

I am not an AWS Pro but I learned a bunch of stuff by going from issue to issue, maybe I am doing everything fundamentally wrong. But I dont think so, correct me if this is the case and tell me how to configure a basic setup for most applications.

Use this lib with their logic (withAuthenticator() & AppSync with Cognito User Settings), or leave it completely and use the cognito and appsync lib seperatly instead. Amplify at this state is just for showcasing how fast you could create basic To-Do Apps, embarrasing.

@NikoMontana sorry about all those frustrating experiences you had. We will continue improving our code and doc to make it better.

I can help answer some of the questions you have.

If you registered a user via web ui in cognito userpools, your password is temporary. Now if you want to change the password you will recognize the response from Auth.signIn() will have signInUserSession: null which will lead to an exception if you want to Auth.changePassword()

Suggestion: When the user created the user account in the user pool, the sign in flow would be a little different with the normal one:

import { Auth } from 'aws-amplify';

Auth.signIn(username, password)
.then(user => {
    if (user.challengeName === 'NEW_PASSWORD_REQUIRED') {
        const { requiredAttributes } = user.challengeParam; // the array of required attributes, e.g ['email', 'phone_number']
        Auth.completeNewPassword(
            user,               // the Cognito User Object
            newPassword,       // the new password
            // OPTIONAL, the required attributes
            {
              email: '[email protected]',
              phone_number: '1234567890'
            }
        ).then(user => {
            // at this time the user is logged in if no MFA required
            console.log(user);
        }).catch(e => {
          console.log(e);
        })
    } else {
        // if no MFA required for the user, then the user is now logged in
        console.log(user);
    }
}).catch(e => {
    console.log(e);
});

After this you may come to the idea to just test, if you are even logged in after calling Auth.signIn(). No you are not - WTF. Try it yourself with calling Auth.currentAuthenticatedUser(s => console.log(s)) within your callback after Auth.signIn(). Now you will end up googling for "cannot load federated user from auth storage".

Suggestion: as documented in https://aws-amplify.github.io/docs/js/authentication#retrieve-current-authenticated-user, the method will only throw error when the user is not logged in. The cannot load federated user from auth storage is a debugging message indicating the user is not federated from other providers using Auth.federatedSignIn. That might be confusing to you. I can make a change to it.

I will also let other team members have a look at this issue. Hopefully we can tackle down those issues you are having.

Unfortunately, I'm stumbling across the same problem for days as well. All I wanted was a guest user for read only access. I also considered removing Amplify and building all using Cloudformation by myself. In the current state, Amplify seems not be usable for a simple usecase like this. :/

@powerful23 Thank you for your support! I wish you and your team a happy new year!

Yes, this is the right solution for the particular auth problem! Now the status got changed to CONFIRMED for that user!

I am have established now public and unauth access with my API, great! Just wondering if I will be able for fine grained acccess controll within the resolvers, because I am using IAM authentication for AppSync!

If my questions get answered I can easily create a tutorial on my approach to public and private access to AppSync.

@NikoMontana glad to hear that! Can you give more description about the fine grained access control? Basically if you are using IAM authentication, you would choose the right policy for the authenticated role that you defined in the Cognito federated Identity pool. So that the signed in user would have the permission to do actions that are only allowed under auth state.

@powerful23 Yes, thats what it is all about. Execute some queries without authentication. But by choosing IAM authentication for AppSync, my last question is about how to handle the uuid of the user. In example for only let the owner of an item delete it etc..

I am talking about the identity within the resolver context. Using now "owner": {"S": "${context.identity.cognitoIdentityId}" } for owner logic within an item but I am really not sure if this is the safe way doing it? I assume that the JWT gets decoded within cognito user pool authentication and from that you get the sub from the context.

@NikoMontana , what was the ultimate solution to the "queries for Authed Users returning 401?" We have a similar issue, where all users logged in via Amplify appear to be inheriting the "unauthed" permissions, instead of the "Authed" one, when using AWS_IAM as the auth source for API

This results in any "Authed User" querying an "Authed API Endpoint" to recieve 401

@sollipse for me it was because I created the users in the webapp of cognito. So the user were initially set to a FORCE_PASSWORD_CHANGE state. In my case this was the solution to it. Or maybe you did not set the inline policy in the IAM Roles to have access to you public/private querries?

Hey @powerful23, can you shed some light on how to get access to the user鈥檚 groups on resolver level when using AWS IAM authentication?

@NikoMontana I think either identityId or the sub would work. They are both unique per user.
@dimitarrusev to get different roles based on different groups, you can check this doc: https://docs.aws.amazon.com/cognito/latest/developerguide/role-based-access-control.html
I would suggest using the Rule-Based Mapping way.

@powerful23 Thanks man! Unfortunately, it鈥檚 still not clear to me how should i approach this. Basically i have an AppSync API with AWS_IAM auth mode and all i need to do is check whether a user is in a specific group based on which i鈥檒l either allow or deny access. Something like this.

But, as far as i understand this only works if you鈥檙e using AMAZON_COGNITO_USER_POOLS auth mode (even though when i manually decode the token using jwt.io, i can see the "cognito:groups" property in it). It鈥檚 very confusing and i鈥檓 blocked at this point. Any help is appreciated.

@NikoMontana @dimitarrusev @floric we are working on automating this & making it easier. You are correct that the @auth features of the GraphQL transformer only work for User Pools at the moment and if you want to have Auth & UnAuth access to an AppSync API you will need to use AWS_IAM via Cognito Identity Pools right now. If you could take a moment and read our RFC please do so to ensure that we are capturing your requirements: https://github.com/aws-amplify/amplify-cli/issues/766

In the meantime, we have provided a sample to help unblock you here: https://github.com/dabit3/appsync-auth-and-unauth

cc: @dabit3 @manueliglesias @kaustavghosh06

@amlcodes What other type of user do you have?

Ah, ok. I think this would still fall into the category of authenticated & unauthenticated. The unauthenticated users, moderators, & admin would all fall under "unauthenticated users" but with special / unique security & authorization rules (since you would have access to their identity).

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

This issue has been automatically closed because of inactivity. Please open a new issue if are still encountering problems.

Ah, ok. I think this would still fall into the category of authenticated & unauthenticated. The unauthenticated users, moderators, & admin would all fall under "unauthenticated users" but with special / unique security & authorization rules (since you would have access to their identity).

@dabit3 - Has there been any updates around this ? I mean to be able to use AMAZON_COGNITO_USER_POOLS for both auth and unauth users ?

Another question - Can we use 2 AppSync APIs - one with AMAZON_COGNITO_USER_POOLS and other with AWS_IAM against the same DB ? Idea is to expose the one with AWS_IAM for UnAuth users and Auth users can get all benefits of using the API with AMAZON_COGNITO_USER_POOLS ? Thoughts ?

Was this page helpful?
0 / 5 - 0 ratings