Describe the bug
I am calling Auth.currentAuthenticatedUser()
to get the current users data, attributes, and sub. The function states that it returns a CognitoUser
, which I import like this import { CognitoUser } from '@aws-amplify/auth'
. And it says that CognitoUser
does not have an attributes
property, but if I console.log
the result from Auth.currentAuthenticatedUser()
I can clearly see an attributes
property. On CognitoUser
there is a method getUserAttributes
but it is async and receives a callback which is very weird.
Are the types wrong? Or I shouldn't access the attributes
directly and through the function that it provides?
To Reproduce
import { Auth } from 'aws-amplify'
import { CognitoUser } from '@aws-amplify/auth'
const user: CognitoUser = await Auth.currentAuthenticatedUser()
user.attributes.something
Expected behavior
TypeScript types include all of the properties that are returned from the specific function.
@idanlo Can you provide the TypeScript error you are receiving? Also, can you provide a code snippet of your code along with the package.json
, this helps us with understanding what is currently being used.
@sammartinez The error I'm getting - Property 'attributes' does not exist on type 'CognitoUser'.ts(2339)
.
The code snippet exists in the reproduction I gave, as simple as that. version - aws-amplify: "^2.2.5"
Yes, the types in Cognito are messed up. They don't match actual response objects and thus if you use them your app breaks but if you don't use them, then passing the cognito user around into other Cognito methods breaks things too. It's quite a mess.
The way I did to overcome this problem (at least temporary) was to create a new custom type like that extends the base CognitoUser type like following:
type MyCognitoAttributes = {
'custom:xxx': string;
...
}
type MyCognitoUser = CognitoUser & {
attributes: MyCognitoAttributes;
}
This way I am covering my custom attributes also
Here's my temporary fix, which also extends the CognitoUser
type, but does so via a Typescript interface
that extends the CognitoUser
type:
[ Note: ...but first, I define a custom UserAttributes
type to only have the fields used in my app, since those could be anything. ]
import { CognitoUser } from '@aws-amplify/auth';
...
/*
* Custom attributes type defined according to the attributes used in this app
*/
export interface UserAttributes {
sub: string;
email: string;
email_verified: string;
name: string;
updated_at: string;
'custom:bytesQuota': string;
'custom:bytesUsed': string;
}
...
/*
* The following interface extends the CognitoUser type because it has issues
* (see github.com/aws-amplify/amplify-js/issues/4927). Eventually (when you
* no longer get an error accessing a CognitoUser's 'attribute' property) you
* will be able to use the CognitoUser type instead of CognitoUserExt.
*/
interface CognitoUserExt extends CognitoUser {
attributes: UserAttributes;
}
...
// usage, in the code:
this.user = await Auth.currentAuthenticatedUser();
return this.user ? this.user.attributes : null;
Just noting that a similar thing occurs with challengeName (as has been noted) and was brought up almost a year ago but then closed (#3733).
Just noting that a similar thing occurs with challengeName (as has been noted) and was brought up almost a year ago but then closed (#3733).
Same here. 'Property 'challengeName' does not exist on type 'CognitoUser'.Vetur(2339)
The types need to be fixed.
There is a workaround: https://github.com/aws-amplify/amplify-js/issues/3733#issuecomment-533817764
Bump. Any updates on this, Amplify team?
Meanwhile I was able to work around following some internal code [1] and [2].
Basically importing import { CognitoUserInterface } from '@aws-amplify/ui-components';
and using it to declare variables and also for type assertions as CognitoUserInterface
.
e.g.
const AuthStateApp: React.FunctionComponent = () => {
const [authState, setAuthState] = React.useState<AuthState>();
const [user, setUser] = React.useState<CognitoUserInterface | undefined>();
React.useEffect(() => {
return onAuthUIStateChange((nextAuthState, authData) => {
setAuthState(nextAuthState);
setUser(authData as CognitoUserInterface);
});
}, []);
return authState === AuthState.SignedIn && user ? (
<div className="App">
<div>Hello, {user.username}</div>
<AmplifySignOut />
</div>
) : (
<AmplifyAuthenticator />
);
}
I noticed that when I log the return value, it's the class instance of CongnitoUser, and you can call the class methods on it like such.
if (response && response instanceof CognitoUser) {
return <div>{response.getUsername()}</div>;
}
Most helpful comment
Yes, the types in Cognito are messed up. They don't match actual response objects and thus if you use them your app breaks but if you don't use them, then passing the cognito user around into other Cognito methods breaks things too. It's quite a mess.