amplify-js: TypeScript types are incorrect

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

  1. import { Auth } from 'aws-amplify'
  2. import { CognitoUser } from '@aws-amplify/auth'
  3. const user: CognitoUser = await Auth.currentAuthenticatedUser()
  4. try to access user.attributes.something
  5. Get TypeScript error

Expected behavior TypeScript types include all of the properties that are returned from the specific function.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 51
  • Comments: 41 (6 by maintainers)

Most upvoted comments

Still seeing this issue in the latest "aws-amplify": "^4.2.6". It’s really frustrating. Can amplify team take a look? How can TypeScript definition not matching the public API and the response object?

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.

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 />
    );
}

We are currently working on re-writing our Auth category, and consolidating our TS types, and exposing them more elegantly. It has been long overdue, but we are working on this as our highest priority to simplify the experience of using our library, and making it as seamless as possible.

Also affected by this. Really off-putting that this is still an ongoing issue. I mean we’re not asking for anything crazy just give us type definitions that actually match responses…

This is still an issue.

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;

import { CognitoUserInterface } from ‘@aws-amplify/ui-components’;

I came here just to say THANK YOU BECAUSE I hadn’t even THOUGHT to look in ui-components for exported interfaces/types for this! I’d been struggling/fighting/using // import { CognitoUser } from 'amazon-cognito-identity-js' instead.

Lack of documentation, info on this hurts…but thank goodness for this thread!

Just hit this issue. getUsername() is defined in the type definition, but returns undefined; the username property is populated, but not defined in the type definition 😦

CognitoUserInterface is not a solution as I’m not using the UI library, just the Auth library

That’s it! It’s a little scarry how Bush-league AWS looks under the hood.

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).

Hello - we are currently working on improving our TypeScript coverage, and we have this RFC proposal open that we would love to get feedback on.

https://github.com/aws-amplify/amplify-js/issues/11113

Now that ui-components is deprecated, how do we import the CognitoUserInterface type?

This has been an issue for 3 years and still isn’t fixed? How is that possible? Just provide the types!

Screenshot 2022-10-11 at 09 18 02 Screenshot 2022-10-11 at 09 18 11

For those who end up here looking for a proper interface for Auth.SignIn method… The differences in shape of response are significant

Especially that the signInUserSession contains everything I am interested in, in my case 😝

Interesting that Auth.d.ts nor the auth-types.d.ts contains no mention of it (and this is the object where all tokens arrive xD) image

Here’s a fun one, in case anyone has any ideas:

Snippet:

import { VuexModule, Module, Mutation, Action } from 'vuex-module-decorators'
import { CognitoUserInterface } from '@aws-amplify/ui-components'

@Module
export default class AuthModule extends VuexModule {
  user: CognitoUserInterface = {}
}

All I wanna do is shove my Auth state into Vuex. No big deal, right? So, when I import CognitoUserInterface, typescript complains, stating: Type ‘{}’ is missing the following properties from type ‘CognitoUserInterface’: challengeName, challengeParamts(2739)

So, I need to initialize state this way:

@Module
export default class AuthModule extends VuexModule {
  user: CognitoUserInterface = {
    challengeName: '',
    challengeParam: {}
  }
}

This also works, using Partial -->

import { VuexModule, Module } from 'vuex-module-decorators'
import { CognitoUserInterface } from '@aws-amplify/ui-components'

@Module
export default class AuthModule extends VuexModule {
  user: Partial<CognitoUserInterface> = {}

  get isAuthenticated (): boolean {
    return !!this.user
  }

  get firstName (): string {
    // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
    return this.user.attributes?.given_name as string ?? 'First'
  }

  get lastName (): string {
    // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
    return this.user.attributes?.family_name as string ?? 'Last Name'
  }

  get userAttrbutes (): Partial<CognitoUserInterface> {
    // eslint-disable-next-line @typescript-eslint/no-unsafe-return
    return this.user?.attributes ?? 'no attributes'
  }
}

Not a huge pain, but why? When I look at the typing for CognitoUserInterface, it looks like practically everything is optional, EXCEPT for challengeName and challengeParam. What makes those special? What’s the reasoning?

export interface CognitoUserInterface {
    Session?: string | null;
    authenticationFlowType?: string;
    client?: {
        endpoint?: string;
        userAgent?: string;
    };
    keyPrefix?: string;
    pool?: {
        advancedSecurityDataCollectionFlag?: boolean;
        clientId?: string;
        userPoolId?: string;
    };
    username?: string;
    userConfirmed?: boolean;
    userSub?: string;
    challengeName: string;
    challengeParam: {
        [key: string]: any;
    };
    unverified?: {
        email?: string;
        phone_number?: string;
    };
    [attributes: string]: any;
}

Why tho? Could someone explain to me if initializing the state object as type CognitoUserInterface with empty string + object OR using Partial is a bad idea? If so, why? 😃

Thank you!

Hi @vehm - we would really appreciate your feedback on the RFC for the TypeScript types we want to introduce in our next major version here. https://github.com/aws-amplify/amplify-js/issues/11113

Seeing a reaction from myself in a comment made 3 years ago is sad. Is there a deadline?

@mcanvar - can you please submit a new Github issue so that we can collect the needed information from you and properly track this?

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

why is this closed?

Thanks for the help in this thread, got me past an error with signInUserSession missing from AmplifyUser type when I’m trying to get the group membership:

import type { AmplifyUser } from '@aws-amplify/ui';

export interface MyUserType extends AmplifyUser {
  signInUserSession: {
    accessToken: {
      payload: {
        auth_time: number;
        client_id: string;
        'cognito:groups'?: string[];
        event_id: string;
        exp: number;
        iat: number;
        iss: string;
        jti: string;
        origin_jti: string;
        scope: string;
        sub: string;
        token_use: string;
        username: string;
      };
    };
  };
}

@AgoraRich until this happens you can use as unknown as YourTSinterface to overwrite the clunk ones. Not perfect but better than any, or you can use Zod validators

@abdallahshaban557 Sure, will going to add here.

@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"