"No current user": Isn't it even possible to make unauth calls to AWS AppSync through Amplify with authentication type AMAZON_COGNITO_USER_POOLS?
Asked Answered
T

2

15

I have an AWS AppSync schema with the default authorization mode set to Amazon Cognito User Pool. I make calls to this AppSync endpoint from a web app using AWS Amplify GraphQL Client and, coherently, its configuration points Cognito User Pools as authentication type, too:

aws_appsync_authenticationType: 'AMAZON_COGNITO_USER_POOLS'

It works as expected when the user is authenticated; however (although the involving Cognito Identity Pool has proper Auth and Unath roles set already), when the website runs some Amplify fetch command like for a unauthenticated(guest) user:

const item = await API.graphql(graphqlOperation(getItem, { id: 'my-id' }))

Ends up with throwing an error:

"No current user"

Well, I expected it to perform if I allow unauthenticated users, but it simply fails. Seeking for a way out, I found some discussions like:

And, all of the above suggest revisiting the Amplify configs so that the AppSync authentication type is converted from AMAZON_COGNITO_USER_POOLS to AWS_IAM or API_KEY. However, for some detailed reason 1:

  • I want to stick with AMAZON_COGNITO_USER_POOLS authentication type,
  • And still be able to fetch some AppSync resources as a guest user unless they are restricted with @aws_auth decorators or such.

Is it possible in any way?


1 I have more granular controls depending on the user's group (admin, normal etc.) with decorators such as @aws_auth(cognito_groups: ["default-user-group"]) on the AppSync schema. So, I need Cognito User Pools for that usage.

Technocracy answered 4/8, 2020 at 13:35 Comment(2)
Hey, do you have any solution on this??Derk
same problem. my api requires me to have cognito pools methods, but then I wont be able to query while unauthenticated.Landrum
P
6

So, I just went through a similar issue and managed to get it sorted. I hope this might help you sort this out. The SO question you mentioned in your question is almost the right way to do it. However, there are one "little" tiny details that are not documented and took me a while to find out.

Apart from having to enable both authenticated and unauthenticated access by running amplify update auth (you can see how in the SO linked above) there are other tweaks you need to do.

First, in your model you need to adjust your rules to be something like:

  @auth(
    rules: [
      # allow owners ability to update and delete their these messages (user pools)
      { allow: owner },
      # allow all authenticated users to access this data
      { allow: private, provider: userPools },
      # allow all guest users (not authenticated) to access this data
      { allow: public, provider: iam }
    ]
  )

Once you set up this model to allow user pools to access all the data they "own", you can also let any "guest" user to access the data too.

In the frontend, let's get your code as an example, you need to use a bit of a different approach:

Instead of

const item = await API.graphql(graphqlOperation(getItem, { id: 'my-id' }))

try something like

// Check if the user is logged in or not
let isLoggedIn = await isLoggedIn();

const item = (await API.graphql({
      query: getItem,
      variables: { id: 'my-id' },
      authMode: isLoggedIn ? GRAPHQL_AUTH_MODE.AMAZON_COGNITO_USER_POOLS : GRAPHQL_AUTH_MODE.AWS_IAM,
  }));

by the way the "isLoggedIn" function look like this

async function isLoggedIn() {
  // Another way to get if its a guest or not
  //return await Auth.Credentials.getCredSource() === "guest"
  try {
      await Auth.currentAuthenticatedUser();
      return true;
  } catch {
      return false;
  }
}

So... this line is what it does the trick, which is not really well documented.

authMode: isLoggedIn ? GRAPHQL_AUTH_MODE.AMAZON_COGNITO_USER_POOLS : GRAPHQL_AUTH_MODE.AWS_IAM

you need to path the different methods according to the state of the user (logged in or guest), not just the AWS_IAM one.

This will only get you as far as READING the data, in order to make sure guests can do Create/Update/Delete, and separate the data ownership from each other and logged users, that's a completely different story that you will need to start digging up on resolvers to get it sorted. But the good news is, there is a way :)

Pathos answered 3/9, 2021 at 14:7 Comment(0)
S
1

Thanks so much! I only had to make a few changes to work with Amplify v6.

I don't know yet whether this is the best way to accomplish this common use case.

import { generateClient } from "aws-amplify/api";
import { listProducts } from "../graphql/queries";
import { useEffect, useState } from "react";
import { Product } from "../API";

const client = generateClient();

import { getCurrentUser } from "aws-amplify/auth";

async function checkForUser() {
  try {
    const { username, userId, signInDetails } = await getCurrentUser();
    console.log(`The username: ${username}`);
    console.log(`The userId: ${userId}`);
    console.log(`The signInDetails: ${signInDetails}`);
    return true;
  } catch (err) {
    console.log(err);
    return false;
  }
}

const ListProducts = () => {
  const [products, setProducts] = useState<Product[]>([]);

  useEffect(() => {
    fetchProducts();
  }, []);

  const fetchProducts = async () => {
    const isLoggedIn = await checkForUser();
    try {
      const productsData = await client.graphql({
        query: listProducts,
        authMode: isLoggedIn ? "userPool" : "iam",
      });
      setProducts(productsData.data.listProducts.items);
    } catch (error) {
      console.error("error fetching products", error);
    }
  };
Solidary answered 23/11, 2023 at 20:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.