Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AWS Cognito services APIs? Amplify => Javascript SDK Angular app

I used Amplify for authentication and it seems to work fine. Now I want to setup an admin app for CRUD with the user pool. It seems that I have to leave Amplify and use the JavaScript SDK to use the appropriate api's.

How does this work? I've failed at figuring out how to get the tokens I receive in Amplify into AWS.config or wherever they are supposed to go.

What a struggle this had been. It seems that the code in the docs is dated and what little advice is online is worse. I suspect that is because an Amplify object contains the config options and I have to bring those to the AWS.config object. I've tried below and failed. Any idea what I need to do? I'm sure answers here will be useful for many AWS newbies.

I have this code in my Angular app but thinking about Lambda. I have an EC2 server with Node.js as another option.

This is for dev on my MBP but I'm integrating it with AWS.

With the code below I get an error message that contains in part:

Error in getCognitoUsers:  Error: Missing credentials in config
    at credError (config.js:345)
    at getStaticCredentials (config.js:366)
    at Config.getCredentials (config.js:375)

The JWT I inserted into the object below is the AccessKeyID that is in my browser storage and I used for authentication.

In console.log cognitoidentityserviceprovider I have this object, in part:

config: Config
    apiVersion: "2016-04-18"
    credentialProvider: null
    credentials: "eyJraWQiOiJwaUdRSnc4TWtVSlR...
    endpoint: "cognito-idp.us-west-2.amazonaws.com"
    region: "us-west-2"
    endpoint: Endpoint
        host: "cognito-idp.us-west-2.amazonaws.com"
        hostname: "cognito-idp.us-west-2.amazonaws.com"
        href: "https://cognito-idp.us-west-2.amazonaws.com/"

These functions flow down as a sequence. I left some vars in the bodies in case someone wants to know how to get this data from the user object. I used them in various attempts build objects but most aren't needed here, maybe. The all produce the correct results from the Amplify user object.

import { AmplifyService }  from 'aws-amplify-angular';
import Amplify, { Auth  } from 'aws-amplify';
import { CognitoIdentityServiceProvider } from 'aws-sdk';
import * as AWS from 'aws-sdk';

@Injectable()
export class CognitoApisService {

  private cognitoConfig = Amplify.Auth.configure();  // Data from main.ts
  private cognitoIdPoolID = this.cognitoConfig.identityPoolId;
  private cognitoUserPoolClient = this.cognitoConfig.userPoolWebClientId;
  private cognitoIdPoolRegion = this.cognitoConfig.region;
  private cognitoUserPoolID = this.cognitoConfig.userPoolId;

...

constructor(
    private amplifyService: AmplifyService,
  ) { }

public getAccessToken() {

return this.amplifyService
  .auth()  // Calls class that includes currentAuthenticaedUser.
  .currentAuthenticatedUser()  // Sets up a promise and gets user session info.
  .then(user => {
    console.log('user: ', user);

    this.accessKeyId = user.signInUserSession.accessToken.jwtToken;


    this.buildAWSConfig();

    return true;
  })
  .catch(err => {
    console.log('getAccessToken err: ', err);
  });
}

public buildAWSConfig() {

    // Constructor for the global config.
    this.AWSconfig = new AWS.Config({
      apiVersion: '2016-04-18',
      credentials: this.accessKeyId,
      region: this.cognitoIdPoolRegion
    });

    this.cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider(this.AWSconfig);

    /*  This doesn't get creds, probably because of Amplify.
        this.cognitoidentityserviceprovider.config.getCredentials(function(err) {
          if (err) console.log('No creds: ', err);    // Error: Missing credentials
          else console.log("Access Key:", AWS.config.credentials.accessKeyId);
        });
    */

    console.log('cognitoidentityserviceprovider: ', this.cognitoidentityserviceprovider);

    this.getCognitoUsers();
}


public getCognitoUsers() {

    // Used for listUsers() below.
    const params = {
      UserPoolId: this.cognitoUserPoolID,
      AttributesToGet: [
        'username',
        'given_name',
        'family_name',
      ],
      Filter: '',
      Limit: 10,
      PaginationToken: '',
    };


    this.cognitoidentityserviceprovider.listUsers(params, function (err, data) {

      if
        (err) console.log('Error in getCognitoUsers: ', err); // an error occurred
      else
          console.log('all users in service: ', data);
    });
  }
like image 300
Preston Avatar asked Oct 16 '22 09:10

Preston


1 Answers

The one problem was that the credentials needs the whole user object from Amplify, not just the access token as I show above. By the way, I have the Cognito settings in main.ts. They can also go in environment.ts. A better security option is to migrate this to the server side. Not sure how to do that yet.

// Constructor for the global config.
this.AWSconfig = new AWS.Config({
  apiVersion: '2016-04-18',
  credentials: this.accessKeyId, // Won't work.
  region: this.cognitoIdPoolRegion
});

My complete code is simpler and now an observable. Notice another major issue I had to figure out. Import the AWS object from Amplify, not the SDK. See below.

Yeah, this goes against current docs and tutorials. If you want more background on how this has recently changed, even while I was working on it, see the bottom of this Github issue. Amplify is mostly for authentication and the JavaScript SDK is for the service APIs.

import { AmplifyService }  from 'aws-amplify-angular';
// Import the config object from main.ts but must match Cognito config in AWS console.
import Amplify, { Auth  } from 'aws-amplify';
import { AWS } from '@aws-amplify/core';
import { CognitoIdentityServiceProvider } from 'aws-sdk';
// import * as AWS from 'aws-sdk';  // Don't do this.

@Injectable()
export class CognitoApisService {

  private cognitoConfig = Amplify.Auth.configure();  // Data from main.ts

  private cognitoIdPoolRegion = this.cognitoConfig.region;
  private cognitoUserPoolID = this.cognitoConfig.userPoolId;

  private cognitoGroup;
  private AWSconfig;

  // Used in listUsers() below.
  private params = {
    AttributesToGet: [
      'given_name',
      'family_name',
      'locale',
      'email',
      'phone_number'
    ],
    // Filter: '',
    UserPoolId: this.cognitoUserPoolID
  };


  constructor(
    private amplifyService: AmplifyService,
  ) { }

public getCognitoUsers() {

    const getUsers$ =  new Observable(observer => {
      Auth
        .currentCredentials()
        .then(user => {
            // Constructor for the global config.
            this.AWSconfig = new AWS.Config({
              apiVersion: '2016-04-18',
              credentials: user,    //  The whole user object goes in the config.credentials field!  Key issue.
              region: this.cognitoIdPoolRegion
            });

            const cognitoidentityserviceprovider = new CognitoIdentityServiceProvider(this.AWSconfig);

            cognitoidentityserviceprovider.listUsers(this.params, function (err, userData) {
              if (err) {
                  console.log('Error in getCognitoUsers: ', err);
              } else {
                observer.next(userData);
              }
            });

        });

    });

    return getUsers$;

  }

Let's call this service from a component. I'm putting the JS object parsing in the component but for now, I left the console.log here for you to get started and see if the code works for your app.

  // Called from button on html component.
  public getAllCognitoUsers() {
    this.cognitoApisService.getCognitoUsers()
      .subscribe(userData => {
        console.log('data in cognito component: ', userData);
    })
  }
like image 102
Preston Avatar answered Nov 15 '22 05:11

Preston