Categories
Cognito

How to manage machine user in AWS and Cognito Pools

Consumers

If you are buildg an app you will have two types of consumers.

  • User consumers
  • Machine Consumers

User consumers

Users consumers are human users.

Also called internal user.

These are the users of Your WebApp.

Users will authenticate against a Cognito User pool using Authorization code grant.

Machine User Consumer

These are bots or programatic clients.

These users are also called external users.

They will consume the API in a different way.

Machine users are different from users, they shouldnt access a browser to authenticate themselves and also they cant confirm via email when the credentials are created or restored.

From the AWS official documentation

See Client Credentials Grant

The steps for the process are as follows:

  1. An app makes a POST request to https://AUTH_DOMAIN/oauth2/token, and specifies the following parameters:
    • grant_type – Set to “client_credentials” for this grant type.
    • client_id – The ID for the desired user pool app client.
    • scope – A space-separated list of scopes to request for the generated access token.
    In order to indicate that the app is authorized to make the request, the Authorization header for this request is set as “Basic BASE64(CLIENT_ID:CLIENT_SECRET)“, where BASE64(CLIENT_ID:CLIENT_SECRET) is the base64 representation of the app client ID and app client secret, concatenated with a colon.
  2. The Amazon Cognito authorization server returns a JSON object with the following keys:
    • access_token – A valid user pool access token.
    • expires_in – The length of time (in seconds) that the provided access token is valid for.
    • token_type – Set to ” Bearer“.
    Note that, for this grant type, an ID token and a refresh token aren’t returned.
  3. The app uses the access token to make requests to an associated resource server.
  4. The resource server validates the received token and, if everything checks out, executes the request from the app.

The following diagram illustrates the steps used in a client credentials grant:

Resource Server

To be able to configure a client settings to use the client credentials flow it is necessary to create a resource server which controls the access level of the machine user. You can add several.

Example of getting a token:

var axios = require('axios');
var qs = require('qs');
var data = qs.stringify({
 'grant_type': 'client_credentials',
'client_id': 'someid',
'client_secret': 'somecredential',
'scope': 'https://your.apidomain.com/somestage/external/api.readwrite' 
});
var config = {
  method: 'post',
  url: 'https://your.userpool.domain/oauth2/token',
  headers: { 
    'Content-Type': 'application/x-www-form-urlencoded' // this is important
  },
  data : data
};

axios(config)
.then(function (response) {
  console.log(JSON.stringify(response.data));
})
.catch(function (error) {
  console.log(error);
});

Example on how to verify the token

var jwt = require('jsonwebtoken');
var jwkToPem = require('jwk-to-pem');
var token = "sometoken";
var keys = [] // the content of the keys key of https://cognito-idp.<region>.amazonaws.com/<user-pool-id>/.well-known/jwks.json
function validate(token, keys) {
    let pems = {};
    // console.log('antes del for', keys.length);
    for(let i = 0; i < keys.length; i++) {
        //Convert each key to PEM
        let key_id = keys[i].kid;
        let modulus = keys[i].n;
        let exponent = keys[i].e;
        let key_type = keys[i].kty;
        let jwk = { kty: key_type, n: modulus, e: exponent};
        let pem = jwkToPem(jwk);
        // console.log('key_id', key_id);
        pems[key_id] = pem;
    }
    console.log('aaaa');
    //validate the token
    const decodedJwt = jwt.decode(token, {complete: true});
    if (!decodedJwt) {
        // console.log("Not a valid JWT token");
        return false;
    }


    const kid = decodedJwt.header.kid;
    const pem = pems[kid];
    if (!pem) {
        // console.log('Invalid token');
        return false;
    }


    jwt.verify(token, pem, { algorithms: ['RS256'] }, function(err, decodedToken) {
        console.log('decoded', decodedToken);
    });

}

validate(token, keys);

Usage Plans

As our application grows we will require to measure the usage at least for the external users, the machine users.

To achieve this AWS offers a service called Usage Plans within Api Gateway.

A usage plan can hold several api keys, there should be an api key per client.

Note: API key values must be unique. If you try to create two API keys with different names and the same value, API Gateway considers them to be the same API key.

Api Gateway will read an x-api-key header then it will check if the intent does not exeed the limits defined in the usage plan and then will save the Api Consumption intent, if the limits have been reached it will deny the access.

What are usage plans and API keys?

usage plan specifies who can access one or more deployed API stages and methods—and also how much and how fast they can access them. The plan uses API keys to identify API clients and meters access to the associated API stages for each key. It also lets you configure throttling limits and quota limits that are enforced on individual client API keys.

Best practices for API keys and usage plans

The following are suggested best practices to follow when using API keys and usage plans.

  • Don’t rely on API keys as your only means of authentication and authorization for your APIs. For one thing, if you have multiple APIs in a usage plan, a user with a valid API key for one API in that usage plan can access all APIs in that usage plan. Instead, use an IAM role, a Lambda authorizer, or an Amazon Cognito user pool.
  • If you’re using a developer portal to publish your APIs, note that all APIs in a given usage plan are subscribable, even if you haven’t made them visible to your customers.

A post explaining that cognito can handle machine users:

https://medium.com/faun/setting-up-a-machine-to-machine-authentication-system-with-amazon-cognito-4c8e2de41c2e

Official documentation for API Gateway usage plans

https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-usage-plans.html