This is a similar issue to https://github.com/aws-amplify/amplify-js/issues/1252
During auth installation via the cli, the cli gives the option to allow unauthenticated login. However. when trying to make requests to my graphql api (also setup via the clie) without any @auth
directive, it's still not possible to make requests. I still get the same No current user
error. I've checked the identity pool settings and it looks like the unauth and auth roles are both set. I assume something isn't set up correctly? Am I missing some permissions/policies on my unauth role?
Expected behaviour
It seems like a standard use case to have some methods to be public and some requiring authorisation. For example, all reads for a Post type are publicly accessible, however all writes require an authenticated user that's the owner of the Post. And comments require any authenticated user to write one for a Post, but any public/anonymous user can read the comments.
It sounds as if you are trying to use IAM auth but the API is setup to use AWS Cognito User Pools. Is this correct? The CLI does not currently support GraphQL APIs with IAM but support for this is on the backlog and will be worked on as soon as resources are available.
After creating the API, you can go to the AppSync console's settings page and enable IAM auth instead of user pools auth to verify if this fixes your issue.
Thanks @mikeparisstuff for getting back to me. I did try to use IAM auth and queries from the console worked fine. But when I try to make requests to my api from my frontend, I get the same No current user
error when no user is logged in, but when a user is logged in I get a 403 error (Incomplete signature exception) with the message:
Authorization header requires 'Credential' parameter. Authorization header requires 'Signature' parameter. Authorization header requires 'SignedHeaders' parameter. Authorization header requires existence of either a 'X-Amz-Date' or a 'Date' header. Authorization=my-auth-token"
@rayhaanq you need to update the authentication type in your UI as well. Like this:
import amplifyConfig from './src/aws-exports';
amplifyConfig.aws_appsync_authenticationType = 'AWS_IAM';
Amplify.configure(amplifyConfig);
@mikeparisstuff once IAM auth is set up, how do you actually configure permissions for the use case mentioned above? I have this:
type Thing
@model
@auth(
rules: [
{allow: owner, identityField: "cognitoIdentityId"},
]
)
{
id: ID!
name: String!
}
It'd be nice to be able to write a rule that looks something like this:
{allow: unauthenticated, queries: [get, list]}
Thoughts?
@cadejscroggins I tried the snippet above. Now I get a 401 with a Permission denied
error. This is whether the user is logged in or logged out.
@rayhaanq ah, you need to give your auth/unauth IAM roles appropriate permissions. The AWSAppSyncInvokeFullAccess
policy works for me.
@cadejscroggins even for the unuath role?
@rayhaanq If you want to allow unauthenticated api calls, yes.
@cadejscroggins ah wicked. Is it still possible to apply fine grained access control using the @auth
directive?
@rayhaanq for authenticated users it's pretty easy as you can add users to groups and give them specific permissions in @auth
, but, as I mentioned above, I haven't figured out how to give unauthenticated users specific permissions.
Edit: I just realized that allow: unauthenticated
should probably also apply to signed in users (other than owner) as well. Maybe something like "public" or "everyone" would work.
For a clarification, the @auth
directive currently supports Cognito User Pools only. In the future we will expand support for IAM and can do many of the same things but this needs to be worked on. As for the unauthenticated access, we have an issue https://github.com/aws-amplify/amplify-cli/issues/54 that is tracking this.
I see. Thanks @mikeparisstuff.
the @auth directive currently supports Cognito User Pools only. In the future we will expand support for IAM and can do many of the same things but this needs to be worked on
Is there an issue somewhere for this as well, or maybe a roadmap?
I think this could be done with Cognito Identity Pools, but I'm not sure if the currently generated resolvers are compatible.
Comfirmed working with Cognito Identity Pools. I'll need to add some @auth
to my app, now!
@cadejscroggins @blbradley we are looking at doing this, but the key problem is that doing Fine Grained Access control might be limited since AWS credentials aren't the same as OIDC tokens from Cognito and therefore the $context.identity
object which is available in a GraphQL resolver won't have a username
for us to filter requests on in the same manner.
If we opened up @auth
to allow for AWS IAM credentials what type of filtering would you expect here? Is coarse grained access acceptable meaning on IAM policies like so: https://docs.aws.amazon.com/appsync/latest/devguide/security.html#aws-iam-authorization
@undefobj for my personal use case, I just need a way to have content that is read/write for the owner and read-only for everyone else (including unauthenticated users). It seems IAM auth should work great for that. Unless there's a standardized, non-hacky (read: creating a hardcoded Cognito user) way to allow anonymous requests that I missed, I think it's worth adding IAM to @auth
.
Thoughts?
UPDATE
This actual only handles the _owner_ issue. I still can't see the records when I have no authenticated user :/
@cadejscroggins I have the same scenario:
I just need a way to have content that is read/write for the owner and read-only for everyone else (including unauthenticated users)
I just figured out how to do this via the AppSync interface. I'm going to note here in case it helps you and anybody else:
listOffers
)Mine was as simple as replacing the hand-built list with: $util.toJson($ctx.result)
I was then able to test the response using the AppSync Queries interface and just pasting in the same query that my app makes.
Ok, the trick lies in Cognito's custom authentication option: https://aws-amplify.github.io/docs/js/authentication#customizing-authentication-flow
Auth.signIn
only requires the _username_ param, so you can omit the _password_.
That configures your sign in to a CUSTOM_AUTH
flow type: https://github.com/aws-amplify/amplify-js/blob/4644b432/packages/auth/src/Auth.ts#L416
First you'll need to create a user in your Cognito User Pool鈥t just needs a username, nothing else. I called mine guest
.
Then you have to set up some Lambda functions as Triggers in your User Pool.
I'm not sure how much of this is required, but if you don't define them then you get an error about null
and Lambda
)
These establish a series of challenge steps.
Specifically:
Disclaimer
I'm still trying to figure these out, I just c&p'd from the Amplify docs and the author of this "optional password" patch: https://github.com/buggy/project-x-server/tree/master/shopify/src
Up to this point I haven't figured out how to define the challenge question (it's late, bed time 馃挙 ) so I ended up just setting the verifyAuthChallenge
fn to true
:
exports.handler = async (event, context) => {
event.response.answerCorrect = true;
event;
};
Finally I can call this:
(async () => {
try {
const user = await Auth.signIn ('guest');
await Auth.sendCustomChallengeAnswer (user, 'foo');
navigation.navigate ('App');
} catch (err) {
console.error (err);
}
}) ();
I hope to get the actual challenge pieces figured out next week, but for now I have the desired effect of readonly models for non authorized users!
I hope this helps somebody! I'll continue to post as I figure out the rest of the challenge bits.
I get GraphQLError: Request failed with status code 401
Now I have everything configured in aws-exports:
aws_appsync_graphqlEndpoint
aws_appsync_region
aws_appsync_authenticationType set to AWS_IAM
aws_appsync_apiKey "null"
aws_project_region
aws_cognito_identity_pool_id
aws_cognito_region
aws_user_pools_id
aws_user_pools_web_client_id
Unauthenticated role has full access to AppSync GraphQL
What else could be missing?
Any update on this? Is this going to be added to the '@auth' directive? I'd like to set up an API where everyone can read but only a specific group can create, update, delete.
The frustration here for me is that @auth doesn't work for IAM, even for users who are authenticated in the application itself.
One thing I've accomplished on my own is creating a lambda that strips the "Cognito Sub" out of the IdentityProvider variable in the context.identity for IAM users. This can be resolved to a Cognito user via the AWS-SDK. Could a similar functionality be added to appsync + Amplify to enable @auth for logged in users on an IAM_AUTH stack?
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: aws-amplify/amplify-cli#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
How do you allow read only for everyone else and read/write for owner? (by everyone else, I mean authenticated users). sounds like should be done with the schema only.
just to help anyone who might be stuck here and wants to make unauthorized graphql calls, i have had A LOT of painful cognito experience.
Create a guest user via aws console
i used the following
then call the following function with matching parameters, getCognitoAuthToken(<your-cognito-region>, '[email protected]', 'guest', 'guestguest')
it will return a jwt token you can pass as the value to your Authorization
header (note: the Bearer Prefix is not necessary)
import { config as AWSConfig, CognitoIdentityCredentials } from 'aws-sdk';
import { AuthenticationDetails, CognitoUserSession, CognitoUserPool, CognitoUser } from 'amazon-cognito-identity-js';
import config from '../aws-exports';
const getCognitoAuthToken = (region: string, email: string, username: string, password: string) => {
return new Promise<string>((resolve, reject) => {
try {
AWSConfig.region = region,
AWSConfig.credentials = new CognitoIdentityCredentials({
IdentityPoolId: config.aws_cognito_identity_pool_id,
});
const userPool = new CognitoUserPool({
UserPoolId: config.aws_user_pools_id,
ClientId: config.aws_user_pools_web_client_id,
});
const cognitoUser = new CognitoUser({
Username: username,
Pool: userPool,
});
let forceChangePassword = false; // addresses FORCE_CHANGE_PASSWORD flow for a newly created user in the aws console
const authenticationDetails = new AuthenticationDetails({
Username: username,
Password: password,
});
cognitoUser.authenticateUser(authenticationDetails, {
newPasswordRequired: function (userAttributes: any, requiredAttributes: any) {
const newPassword = password.split('').reverse().join('');
userAttributes.email = email;
userAttributes.locale = 'en';
userAttributes.preferred_username = username,
delete userAttributes.email_verified; // the api doesn't accept this field back
forceChangePassword = true;
cognitoUser.completeNewPasswordChallenge(newPassword, userAttributes, this);
},
customChallenge: (challengeParameters: any) => {
reject('custom challenge received');
},
mfaRequired: (challengeName: any, challengeParameter: any) => {
reject('mfa required');
},
onSuccess: (session: CognitoUserSession) => {
if (forceChangePassword) {
const newPassword = password.split('').reverse().join('');
cognitoUser.changePassword(newPassword, password, err => {
if (err) { reject('failed to revert password'); return; }
resolve(session.getIdToken().getJwtToken());
});
} else {
resolve(session.getIdToken().getJwtToken());
}
},
onFailure: (err: any) => {
reject(err);
},
});
} catch (err) {
reject(err);
}
});
};
We launched multi-auth support for AppSync API (which included public APIs - with API Keys + IAM) as a part of our CLI version 3.7+.
Please take a look at our documentation around it out here - https://aws-amplify.github.io/docs/cli-toolchain/graphql#public-authorization
@kaustavghosh06 thanks for the new release, from what I read on the doc, Public is not compatible with Owner.
So the use case with a post that any visitor can read, only log-in users can comment and only owner can edit is still not supported, can you confirm or did I misunderstood the doc?
I configured _multiple authentication modes_ on my @model
as described here but requests would be authenticated using only the _default_ authentication mode as specified using amplify api configure
.
As show below, I had tried specifying the auth mode inside my service class, but this would be overwritten every time I updated my schema.
//MyAppAPI.service.ts
const response = (await API.graphql(
{
query: statement,
variables: gqlAPIServiceArguments,
authMode: GRAPHQL_AUTH_MODE.API_KEY
});
````
I eventually found that I could instead change the auth mode programatically by doing the following:
```javascript
// import { AmplifyService } from 'aws-amplify-angular';
// import Amplify from '@aws-amplify/core';
// ...
// ...
this.amplifyService.authStateChange$
.subscribe(authState => {
this.isLoggedIn = authState.state === 'signedIn';
this.user = authState.user;
if(this.isLoggedIn)
{
Amplify.configure({
"aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS",
});
}
else
{
Amplify.configure({
"aws_appsync_authenticationType": "API_KEY",
});
}
});
// ...
I hope this helps someone.
Most helpful comment
@rayhaanq you need to update the authentication type in your UI as well. Like this:
@mikeparisstuff once IAM auth is set up, how do you actually configure permissions for the use case mentioned above? I have this:
It'd be nice to be able to write a rule that looks something like this:
Thoughts?