Unable to fetch a valid error code when AWS Cognito authentication fails - amazon-web-services

I am using AWS Cognito for Authentication using user pools and I have all my APIs configured on the API gateway. I directly hit cognito from the Angular client, store the tokens returned by Cognito in local storage and use them in subsequent calls.
The problem however is, if the token I send from Angular has expired the Cognito authentication fails and no Integration backend is hit in this case. As a result, I am getting a 401 error in Chrome.
The interesting thing however is that this 401 code is not available to me in the HTTP response object that is passed to Angular. A default 0 code is received by the Angular and this seems to be the case with all the error code received from server (either cognito or backend).
I tried to explore around and found that the issue might be because the gateway is not sending proper CORS headers in the error cases. I have read following related docs but unfortunately I couldn't find out a way to resolve the issue.
Can someone suggest a solution to this.
Edit: I also read somewhere that it is a known AWS issue. Is that the case ?

You can manage the Cognito session before making the call to the API Gateway.
In the example below, the getSession method has a callback that prints out any error messages to the console, and returns.
////// Cognito.js
import {
CognitoUserPool,
CookieStorage
} from 'amazon-cognito-identity-js'
import config from '../config'
const cookieSettings = {
domain: '.xxxxxxxxxxx.com',
secure: true
}
export default {
cookieSettings,
pool: new CognitoUserPool({
UserPoolId: config.UserPoolId,
ClientId: config.ClientId,
Storage: new CookieStorage(cookieSettings)
})
}
//////
////// actions.js
import Cognito from './Cognito'
export function fetchUser() {
// get cognito user cookies
const cognitoUser = Cognito.pool.getCurrentUser()
if (cognitoUser != null) {
// try to get session from cognito
cognitoUser.getSession(function(err, session) {
if (err) {
console.log(err)
return;
}
fetch('https://api-gateway-url', {
headers: {
Authorization: 'Bearer ' + session.getAccessToken().getJwtToken(),
}
})
.then(response => response.json())
.then(json => console.log(json))
})
}
}

Related

Access AWS Api Gateway API from localhost without auth gives Missing Authentication Token

I set up my first API Gateway in AWS and trying to access the API from A Express route. This gives the following error: {"message":"Missing Authentication Token"}.
When accessing the exact same API through Postman it works fine!
Code is as followed:
router.get(
'/users',
async (req, res) => {
console.log(`${API_BASE_URL}/users}`);
try {
const apiRes = await needle('post', `${API_BASE_URL}/users}`)
const data = apiRes.body;
res.status(200).json(data);
} catch(e) {
res.status(500).json({error})
}
}
);
The logged url works fine as a post request in Postman.
Completely missed the extra } in ${API_BASE_URL}/users}

How to configure authorization for API Gateway for Cognito users?

My project is react native app that uses aws amplify and a rest api through api gateway. i am trying to add an item into my dynamoDB table by calling an API request like so :
const createItemDB = async () => {
API.post("nutritionAPI", "/items", {
body: {
userID: "authenticated user 1 ",
dateID: "1-28-2023",
},
headers: {
Authorization: `Bearer ${(await Auth.currentSession())
.getIdToken()
.getJwtToken()}`,
},
})
};
But this gets the error "request failed with 403 at node_modules\axios\lib\helpers\cookies.js:null in write" Prior to calling createItemDB, i authenticated myself as an "end-user" by Auth.signIn and each time i restart the app, I am still authenticated through my function below
const loadApp = async () =>{
await Auth.currentAuthenticatedUser()
.then(user=>{
console.log("successfully authd" + user)
})
.catch(()=>{
console.log("error signing in")
})
}
So I know i am authenticated. Im new to aws services, and im unsure how to configure the api gateway correctly. the only way to add an item to dynamodb is if I remove the header in my createItemDB call which means any unauthenticated user can add to my dynamodb? I tried to enable CORS inside api gateway but it still doesnt work

AWS Amplify post request fails with "status code 403 at node_modules/axios"

I configured and initialized AWS Amplify for my ReactNative/Expo app and added a REST Api. Im new to AWS in general, but im assuming that once I add the API, my project is populated with amplify/backend folders and files and is ready for consumption.
So i tried to create a simple post request to create an item in my DynamoDB table with
import { Amplify, API } from "aws-amplify";
import awsconfig from "./src/aws-exports";
Amplify.configure(awsconfig);
const enterData = async () => {
API.post("API", "/", {
body: {
dateID: "testing",
},
headers: {
Authorization: `Bearer ${(await Auth.currentSession())
.getIdToken()
.getJwtToken()}`
}
})
.then((result) => {
// console.log(JSON.parse(result));
})
.catch((err) => {
console.log(err);
});
};
const signIn = async () => {
Auth.signIn('test#test.com', 'testpassword')
.then((data) => {
console.log(data)
enterData() //enterData is attempted after signin is confirmed.
})
.catch((err) => {
console.log(err)
})
}
signIn()
I did not touch anything else in my project folder besides including the above in my App.tsx because im unsure if i need to and where. I got a 403 error code and it "points" to the axios package but im not sure if issue is related to aws integration.
I configured the REST Api with restricted access where Authenticated users are allowed to CRUD, and guests are allowed to Read. How could I even check if I am considered an "Authorized User" .
Yes, AWS Amplify API category uses Axios under the hood so axios is related to your problem.
Probably you get 403 because you didn't authorized, for Rest API's you need to set authorization headers,
I don't know how is your config but you can take help from this page. Please review the "Define Authorization Rules" section under the API(REST) section.
https://docs.amplify.aws/lib/restapi/authz/q/platform/js/#customizing-http-request-headers
To check authorization methods, you can use "Auth" class like that also you can see auth class usage in the above link.
import { Amplify, API, Auth } from "aws-amplify";
https://aws-amplify.github.io/amplify-js/api/classes/authclass.html

AWS Cognito TOKEN Endpoint giving a 400 Bad Request error "unauthorized_client"

Following the documentation from https://docs.aws.amazon.com/cognito/latest/developerguide/token-endpoint.html after successfully retrieving an authentication code.
As far as I can tell this is exactly how the request is supposed to be setup:
import request from 'request'
function fetchToken(code: any, clientId: string, clientSecret: string) {
try {
let tokenEndpoint = `https://example.auth.us-east-1.amazoncognito.com/oauth2/token`
const clientIdEncoded = Buffer.from(`${clientId}:${clientSecret}`).toString('base64')
request.post({
url:tokenEndpoint,
headers: {
'Content-Type':'application/x-www-form-urlencoded',
'Authorization':`Basic ${clientIdEncoded}`
},
form: {
code,
'grant_type':'authorization_code',
'client_id':clientId,
'redirect_uri':'http://localhost:3000'
}},
function(err,httpResponse,body){
console.log(httpResponse.statusCode)
//400
console.log(httpResponse.statusMessage)
//Bad Request
if(err) {
console.error(err)
}
console.log(body)
//{"error":"unauthorized_client"}
})
} catch (error) {
console.error(error)
}
}
Why would be getting unauthorized_client? Is there an easier way to debug this?
Edit: tested this in Postman with the same request and getting the same error
Headers
Body
Please check if the Cognito User Pool App is using secret key. If you have created with secret key option, that must be included in the Authorization header of the request.

AWS API Gateway / Cognito Userpools / Lambdas not able to pass caller credentials

I'm working on an AWS API Gateway implementation with a Lambda backend. I use the API Gateway integration with the Cognito Userpools (fairly new) instead of building a custom authorizer using Lambda (which was the recommended way before it was integrated).
I've created a proof of concept (javascript) that authenticates a user with Cognito and then makes a call to the API Gateway with those credentials. So, basically, the end call to the API Gateway is with the JWT token that I received from Cognito (result.idToken.jwtToken) in the Authorization header. This all works and I can validate that only with this token you can access the API.
All working fine, but now I want to get access to the Cognito identity in my Lambda; for instance the identy id or the name or email. I have read how to map all the parameters, but I'm actually just using the standard 'Method Request Passthrough' template in the integration request. I log all the parameters in the lambda and all the 'cognito' parameters are empty.
I've looked through many similar questions and they all propose to enable the 'Invoke with caller credentials' checkbox on the integration request. That makes perfect sense.
However, this checkbox can only be enabled if you are using AWS_IAM as authorization and not if you have selected your cognito UserPool. So it is just not possible to select it and is actually disabled.
Does anybody know what to do in this case? Is this still work in progress, or is there a reason why you can't enable this and get the cognito credentials in your Lambda?
Many thanks.
If you need to log the user information in your backend, you can use $context.authorizer.claims.sub and $context.authorizer.claims.email to get the sub and email for your Cognito user pool.
Here is the documentation about Use Amazon Cognito Your User Pool in API Gateway
For anyone else still struggling to obtain the IdentityId in a Lambda Function invoked via API-Gateway with a Cognito User Pool Authorizer, I finally was able to use the jwtToken passed into the Authorization header to get the IdentityId by using the following code in my JavaScript Lambda Function:
const IDENTITY_POOL_ID = "us-west-2:7y812k8a-1w26-8dk4-84iw-2kdi849sku72"
const USER_POOL_ID = "cognito-idp.us-west-2.amazonaws.com/us-west-2_an976DxVk"
const { CognitoIdentityClient } = require("#aws-sdk/client-cognito-identity");
const { fromCognitoIdentityPool } = require("#aws-sdk/credential-provider-cognito-identity");
exports.handler = async (event,context) => {
const cognitoidentity = new CognitoIdentityClient({
credentials: fromCognitoIdentityPool({
client: new CognitoIdentityClient(),
identityPoolId: IDENTITY_POOL_ID,
logins: {
[USER_POOL_ID]:event.headers.Authorization
}
}),
});
var credentials = await cognitoidentity.config.credentials()
var identity_ID = credentials.identityId
console.log( identity_ID)
const response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Headers": "*",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods" : "OPTIONS,POST,GET,PUT"
},
body:JSON.stringify(identity_ID)
};
return response;
}
After a Cognito User has signed in to my application, I can use the Auth directive of aws-amplify and fetch() in my React-Native app to invoke the lambda function shown above by sending a request to my API-Gateway trigger (authenticated with a Cognito User Pool Authorizer) by calling the following code:
import { Auth } from 'aws-amplify';
var APIGatewayEndpointURL = 'https://5lstgsolr2.execute-api.us-west-2.amazonaws.com/default/-'
var response = {}
async function getIdentityId () {
var session = await Auth.currentSession()
var IdToken = await session.getIdToken()
var jwtToken = await IdToken.getJwtToken()
var payload = {}
await fetch(APIGatewayEndpointURL, {method:"POST", body:JSON.stringify(payload), headers:{Authorization:jwtToken}})
.then(async(result) => {
response = await result.json()
console.log(response)
})
}
More info on how to Authenticate using aws-amplify can be found here https://docs.amplify.aws/ui/auth/authenticator/q/framework/react-native/#using-withauthenticator-hoc