Closed mattiLeBlanc closed 3 months ago
Hello @mattiLeBlanc. Sorry for any inconvenience using the library, and thank you for providing the code snippets.
Based on the code bellow, the fetchUserAttributes
API will fail if there is not connectivity or if there is a network delay, hence hitting the catch block and logging the user out. So the absence of auth tokens would explain the No federated jwt
error.
try {
await fetchUserAttributes();
const currentSession = await fetchAuthSession();
if (currentSession.tokens) {
const idToken = currentSession.tokens.idToken?.toString();
return { Authorization: idToken };
} else {
return undefined
}
} catch (error) {
signOut()
return undefined;
}
If you indeed are getting a NetworkError
, a potential solution is to add a retry logic until the connection is back and then continue with the original flow.
Hello, I can confirm that I am experiencing the same issue but on a Next.js v14 project. I'm using plain cognito (phone number + password) without any federated auth providers.
I don't have any logic to sign out the user as in the OP's issue. Here's a snippet of how I initialize my client
import amplifyConfig from "@repo/aws-exports";
Amplify.configure(amplifyConfig, { ssr: true });
ConsoleLogger.LOG_LEVEL = "DEBUG";
Then, I have the ssrClient
utility initialized as follows:
import { createServerRunner } from "@aws-amplify/adapter-nextjs";
import { generateServerClientUsingCookies } from "@aws-amplify/adapter-nextjs/api";
import { cookies } from "next/headers";
import config from "@repo/aws-exports";
export const ssrClient = generateServerClientUsingCookies({
cookies,
config,
authMode: "userPool",
});
export const { runWithAmplifyServerContext } = createServerRunner({
config,
});
This issue occurs anytime I try to use the ssrClient to perform a query to appsync.
Also, one weird thing I have noticed is that this seems to appear only on localhost. The production build seems to work okay (so far). This follows upgrading the latest versions of aws-amplify
(6.3.7
), @aws-amplify/adapter-nextjs
(1.2.5
) and next
. (14.2.4
)
Initially, I was getting a different No current user
error (despite my tokens not being expired) as reported here. But after upgrading, this is the new error I am getting
@ndaba1, thank you for the additional context here. We'll work on reproducing on our side and investigate this further.
@cwomack any progress/updates on this ?
@HuiSF
Hi @ndaba1 your issue seemed different from the OP, I will follow up in your linked issue. Please take a look there.
Hi @mattiLeBlanc I dug into the GraphQL API implementation and how it uses the headers
callback function specified in the libraryOptions
. It looks like a .graphql()
call always calls fetchAuthSession()
internally to attempt to resolve auth tokens or credentials before invoking headers()
.
In your comment in the code example:
// test if user still exists (could be removed via Admin Resident Unlock). It will then trigger an error and be caught in catch block
How the "remove" is implemented? Are you revoking access token and refresh token on remove? That may happen that when .graphql()
happened, the end user has been removed where access token has been revoked, the underlying fetchAuthSession()
call would fail before reaching to your headers
callback. Hence the error.
In addition, can you confirm, have you enabled unauthenticated access (or guest access) in your identity pool?
@HuiSF I will try to answer your questions
This my aws config with the section for the Angular app:
{
API: {
GraphQL: {
endpoint: 'https://XXXXX.appsync-api.ap-southeast-2.amazonaws.com/graphql',
region: 'ap-southeast-2',
defaultAuthMode: 'userPool'
}
},
Auth: {
Cognito: {
userPoolId: 'ap-southeast-2_XXXXXXXX,
userPoolClientId: 'XXXXX',
identityPoolId: 'ap-southeast-2:XXXXXXXX,
allowGuestAccess: true
}
}
}
I authenticted and unauthenticated modes.
My graphql function in my api.service looks like this:
graphql<V>({ statement: query, variables, type, iam = false }: queryInput) {
const payload: any = {
query,
authMode: iam ? 'iam' : 'userPool',
}
if (variables) {
payload.variables = variables;
}
return from(this.client.graphql(payload) as Promise<GraphQLResult<object>>)
.pipe(
The answer your question about removing a user session: What happens is we can logout a user from the client app via the admin app, by revoking their refresh token. Then when the user navigates anywhere in the client app, that bid of code in the try/catch will detect the user session is no longer valid and do a logout. This is a temp solution, I rather use graphql subscriptions to logout a user but I haven't had time to enable that.
Do you think that bit of code is responsible for our errors where our customers are apparently not logged in, whilst the refresh token is valid for 10 years?
In angular I am using a custom error handler, which is pretty greedy and if there is a graphql authentitcation issue, it will immediately jump to an Error page. But I have not being able to reproduce this issue on my phone or laptop or ipad. I leave the user logged in and come back days later, open the tab and I am seeing our client application work fine. But some of our customers are triggering multiple different errors:
Runtime error running query getResidentsV2. Authmode Cognito. Error: NoValidAuthTokens: No federated jwt
Graphql Error running query getMusicStream. Authmode Cognito. Error: Network error Error: Graphql Error running query getMusicStream. Authmode Cognito. Error: Network error
=> this code be a network issue on their device??No federated jwtNoValidAuthTokens: No federated jwt
Graphql Error running query getMusicStream. Authmode Cognito. Error: Unauthorized Error: Graphql Error running query getMusicStream. Authmode Cognito. Error: Unauthorized
runtime error running query logMusicStream. Authmode Cognito. Error: NoSignedUser: No current user
We have a music streamin app and they will leave it open on a tablet for ever and sometimes trigger the above errors.
@cwomack @HuiSF I am looking in the source code of Amplify and I see for example that the No Federated JWT error is thrown here:
// SRC from internalGraphqlApi.mjs line 77
case 'userPool': {
let token;
try {
token = (await amplify.Auth.fetchAuthSession()).tokens?.accessToken.toString();
}
catch (e) {
// fetchAuthSession failed
throw new GraphQLApiError({
...NO_SIGNED_IN_USER,
underlyingError: e,
});
}
// `fetchAuthSession()` succeeded but didn't return `tokens`.
// This may happen when unauthenticated access is enabled and there is
// no user signed in.
if (!token) {
throw new GraphQLApiError(NO_VALID_AUTH_TOKEN); // <-------- HERE
}
headers = {
Authorization: token,
};
break;
I triggered this error in my test env by revoking the RefreshToken of the logged in user and when the AccessToken expired and I tried to do a graphql call, I got this error.
The function fetchAuthSession
triggered it, because with no refreshtoken, it could not issue a accessToken, so that is all logical.
The errors we are getting listed in the prev message above are triggered by customers. Can a refreshtoken be removed or denied by WIFI security settings in the facilties or because of ContentBlockers on the devives?
However, if that error is triggered by the same bit of code, the refreshtoken must have been revoked on the cognito side or can a missing refreshToken in LocalStorage also trigger this error? I will try to test this.
Hi @mattiLeBlanc
What happens is we can logout a user from the client app via the admin app, by revoking their refresh token. Then when the user navigates anywhere in the client app, that bid of code in the try/catch will detect the user session is no longer valid and do a logout. This is a temp solution, I rather use graphql subscriptions to logout a user but I haven't had time to enable that.
By this flow, I don't think there is a way to make Amplify aware of that the refresh token stored in the client has been revoked, without actually using it to hit the service endpoint. If you need this forceful sign out function, I think you'd need to implement a mechanism that when you perform token revocation from your Admin app, to send a notification to your end-user facing client app, to clear tokens stored on the client and redirect to a sign-in page.
The errors we are getting listed in the prev message above are triggered by customers.
You mean with revoking these customers' token or no?
An end user can configure their browser to reject any cookie, without effective refresh token in the scope, Amplify fetchAuthSession
will not function.
@HuiSF Thank you for you response. I came to the same conclusion, I need a mechanism (graphql subscription) to signal the client app that tokens have been revoked. But this will only work if client is active. A tableet could be asleep, suspending javascripts. Even the webworker will get suspended at some points.
So I am also looking at an interval based ping that checks every X time if the user still exists or is logged in. That would be less invasive then doing that check in the graphql header because that gets excuted on each graphql request, which could be many.
In regards to the other issue, the errors; I am still trying to find out but I noticed that if I remove the refreshtoken manually and let the accesstoken expire, I get the 'Fedarated JWT' error. So I need to figure out if those customers are either having contentblockers or if ther corporate wifi may be filtering out headers? Because how else does a JWT token get removed? I can't reproduce this on any of my devices unless I forcefully remove tokens myself from localstorage.
@HuiSF @cwomack I have resolved the JWT errors by getting rid of the force logout method I was using in amplify 5.4, which didn't work well in v6. I am still getting Unauthorised errors:
Graphql Error running query getMedia. Authmode Cognito. Error: Unauthorized Error: Graphql Error running query getMedia. Authmode Cognito. Error: Unauthorized
and I wonder if it is because my Angular error handler is to greedy. I have seen this error happen just on same the accessToken expired (15min lifespan) but we still have a valid refresh token.
Does the accessToken renewal trigger an unauthorised error on the first graphql request and then automatically fetch a new accessToken and try the query again, or will the query that happens just on the cusp of accesstoken expiry always fail? Ergo, is my error handler correct but to aggressive. Should I ignore the unauthorised error in the graphql execution and rely on an RXJS retry mechanism instead?
Hi @mattiLeBlanc
Does the accessToken renewal trigger an unauthorised error on the first graphql request and then automatically fetch a new accessToken and try the query again, or will the query that happens just on the cusp of accesstoken expiry always fail?
It's always that the GraphQL API requests the access token via the fetchAuthSession()
call, the latter refreshes access token if the access token has already expired at this point. So GraphQL API should get the effective access token in most cases.
Have you able to capture the access token used to trigger the Unauthorized
error, if you have, was the access token actually expired? (There is a possibility that the access token indeed doesn't have access to the GQL records you were accessing)
Should I ignore the unauthorised error in the graphql execution and rely on an RXJS retry mechanism instead?
You meant the RxJS used by the GraphQL API internally? Hum I don't think the retry would happen on Unauthorized
error.
@HuiSF I started a new issue because I dived deep into this problem: https://github.com/aws-amplify/amplify-js/issues/13710 and I were able to reproduce now consistently in my app. Have a look at the other cleaner issue, with many examples.
@mattiLeBlanc, we'll close this one out as a duplicate since the last remaining issue here is what is captured in #13710.
Before opening, please confirm:
JavaScript Framework
Angular
Amplify APIs
Authentication
Amplify Version
v6
Amplify Categories
auth, api
Backend
None
Environment information
Describe the bug
My Angular error handler is reporting several Authentication related errors when doing Appsync calls:
No federated jwtNoValidAuthTokens: No federated jwt
Runtime error running query getMediaPath. Authmode Cognito. Error: NoValidAuthTokens: No federated jwt
Graphql Error running query getMediaPath. Authmode Cognito. Error: Unauthorized Error: Graphql Error running query getMediaPath. Authmode Cognito. Error: Unauthorized
These are all from different customers using our platform. I can't see in the stack trace what caused it because all the code is uglified and it happens in 3th party lib (amplify).
These users SHOULD be logged in, because our refresh token is set to multiple years expiry. What may happen, they are running on a tablet which goes to sleep,and next day they come back to the application and it tries to do a query, maybe before the refreshtoken fetched a new accesstoken?
I can't reproduce it on my macbook, ipad of lenove android tablet.
Am I looking at headers being filtered by corporate networks or is their something else I should do?
I configure my angular app by the books:
I am using the latest version of Amplify 6 and before I was using Amplify 5.4 I didnt not get these JWT errors. Maybe just authorisation errors.
Expected behavior
I expect no authorisation errors if refreshtoken is valid and app is activated
Reproduction steps
I don't even know how to reproduce it myself. It is happening on customers devices.
I realise this issue is vague, and it is for me too. I would have to get a hold of some of our users devices or talk their ID department to find out if has something to do with contentblockers or other security settings.
But I hope maybe these errors ring bells and you can give me a nudge in the right direction.
Code Snippet
Log output
aws-exports.js
No response
Manual configuration
No response
Additional configuration
No response
Mobile Device
No response
Mobile Operating System
No response
Mobile Browser
No response
Mobile Browser Version
No response
Additional information and screenshots
No response