I couldn't find any documentation that showed how to do this so I tried my best to figure it out (is this not a common use case)? I've set up my resource to use IAM authentication, set up CORS, etc. Then I deployed it, and downloaded the generated the SDK.
On the client-side I'm using the credentials from AWS.CognitoIdentityCredentials with apigClientFactory.newClient. When I try to post to my resource, I get a 403 error response with no body.
The response headers contain: x-amz-ErrorType: UnrecognizedClientException
Could this error possibly be coming from some other AWS service (do they bubble up like that)? If so, how can I tell which one? What else might be causing the error?
The code I'm using test test client-side looks like this:
function onFacebookLogin(fbtoken) {
// get cognito credentials
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:abcd6789-1234-567a-b123-12ab34cd56ef',
Logins: {'graph.facebook.com': fbtoken}
});
AWS.config.credentials.get(function(err) {
if (err) {return console.error('Credentials error: ', err);}
/* I'm assuming that this is what I use for accessKey and secretKey */
var credentials = AWS.config.credentials;
apigClient = apigClientFactory.newClient({
accessKey: credentials.accessKeyId,
secretKey: credentials.secretAccessKey
});
});
}
I think what might be happening is you're not setting the sessionToken field with the access key and secret key. Can you try setting it up to look like the below example and see if that works?
var client = apigClientFactory.newClient({
accessKey: ACCESS_KEY,
secretKey: SECRET_KEY,
sessionToken: SESSION_TOKEN
});
This previous question has a bit more detail, if needed.
Yes I believe the sessionToken is required.
Here's a basic example using cognito unauthenticated identities: https://github.com/rpgreen/aws-recipes/blob/master/app/index.html
Related
I want to use kinesis video streams webrtc javascript sdk for producing video stream from a web page.
The sdk readme says i need to supply accessKeyId and secrectAccessKey
signalingClient = new KVSWebRTC.SignalingClient({
channelARN,
channelEndpoint: endpointsByProtocol.WSS,
clientId,
role: KVSWebRTC.Role.VIEWER,
region,
credentials: {
accessKeyId,
secretAccessKey,
},
systemClockOffset: kinesisVideoClient.config.systemClockOffset,
});
Is there a way to make this more secure and avoid supplying the secret access key inside the javascript code?
Doesn't it mean anyone viewing my web page source can take these credentials from the web page and use them to access the signaling channel?
Can I use amplify-js Auth class to use the signaling client with an authenticated user?
Turns out I can use credentials inside the backend, and send a presigned link to the client using the class SigV4RequestSigner.
There's no need to supply credentials on the client side.
Found it in the documentation:
This is a useful class to use in a NodeJS backend to sign requests and send them back to a client so that the client does not need to have AWS credentials.
When creating the SignalingClient you can either specify the credentials or a requestSigner that returns a Promise<string>, see:
https://github.com/awslabs/amazon-kinesis-video-streams-webrtc-sdk-js/blob/master/README.md#class-signalingclient
credentials {object} Must be provided unless a requestSigner is provided.
Be aware that when not using credentials in the browser you will also need to run the KinesisVideoSignalingChannels related code on the server side, because this class does not supports request signer.
For Kinesis, one of the possibilities is to implement in your NodeJS backend a function for signing your URLs.
const endpointsByProtocol = getSignalingChannelEndpointResponse.ResourceEndpointList.reduce((endpoints, endpoint) => {
endpoints[endpoint.Protocol] = endpoint.ResourceEndpoint;
return endpoints;
}, {});
console.log('[VIEWER] Endpoints: ', endpointsByProtocol);
const region = "us-west-2";
const credentials = {
accessKeyId: "XAXAXAXAXAX",
secretAccessKey: "SECRETSECRET"
};
const queryParams = {
'X-Amz-ChannelARN': channelARN,
'X-Amz-ClientId': formValues.clientId
}
const signer = new SigV4RequestSigner(region, credentials);
const url = await signer.getSignedURL(endpointsByProtocol.WSS, queryParams);
console.log(url);
I am developing a widget that users in my company can use to communicate with end-users through Smooch.
The widget is accessible through the web browser and the communication goes mostly through a layer developed in node. However, I was trying to send attachments directly to Smooch to reduce the load in the server.
As I understand, it is necessary to use a token with a appUser scope to avoid issues with CORS.
I create the token using the following code
app.get('/getjwt', (req, res) => {
var token = jwt.sign({ scope: 'appUser', userId: req.body.userId }, SECRET, { header: { 'alg': 'HS256', 'type': 'JWT', 'kid': '[app key ID]' } });
res.send({ jwt: token });
});
I try to use the generated token (using Postman for tests) by making a request with Authorization Bearer [my generated token] and I get the following error:
{
"error": {
"code": "invalid_auth",
"description": "Invalid JWT header. Missing key id (kid)"
}
}
I have tried changing the 'kid' value to the app ID, the API key ID, and the API key Secret and I'm always getting the same error. What am I missing? Am I supposed to pass the Key ID somewhere else?
Thank you,
Your code works fine for me, what version of jsonwebtoken are you using? In v6.0.0 the headers option was renamed to header, so if you're using 5.x or lower your code should look like this instead
var token = jwt.sign({ scope: 'appUser', userId: req.body.userId }, SECRET, { headers: { 'alg': 'HS256', 'type': 'JWT', 'kid': '[app key ID]' } });
That said, Smooch already provides a fully functional web messenger / widget that you should use instead of attempting to build your own. It provides event hooks and methods to build a fully custom UI if that's what you're trying to achieve. See https://docs.smooch.io/guide/web-messenger/ and https://www.npmjs.com/package/smooch
I am not sure how to send signed http request do AppSync GraphQL endpoint. There is no library for do that in AWS.
aws-amplify don't work because works only in browser, not in Lambda function.
aws-sdk for AppSync is only for admin usage, it doesn't have methods for call user side api
It is possible to make IAM signed HTTP request from AWS Lambda? (in some easy way)
i would recommend reading this article: Backend GraphQL: How to trigger an AWS AppSync mutation from AWS Lambda,
quoting the author, https://stackoverflow.com/users/1313441/adrian-hall, we've:
GraphQL is routed over HTTPS. That means we can simulate the GraphQL client libraries with a simple HTTPS POST. Since we are using IAM, we need to sign the request before we deliver it. Here is my code for this:
// ... more code here
// POST the GraphQL mutation to AWS AppSync using a signed connection
const uri = URL.parse(env.GRAPHQL_API);
const httpRequest = new AWS.HttpRequest(uri.href, env.REGION);
httpRequest.headers.host = uri.host;
httpRequest.headers['Content-Type'] = 'application/json';
httpRequest.method = 'POST';
httpRequest.body = JSON.stringify(post_body);
AWS.config.credentials.get(err => {
const signer = new AWS.Signers.V4(httpRequest, "appsync", true);
signer.addAuthorization(AWS.config.credentials, AWS.util.date.getDate());
const options = {
method: httpRequest.method,
body: httpRequest.body,
headers: httpRequest.headers
};
fetch(uri.href, options)
// ... more code here
I've been using it as a template for all my Lambda->AppSync communication!
You can use any graphql client or a sigv4 signed HTTP request. Here's how you create the signature for your request (https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html). If you attach an execution role to your lambda you can access it access key from lambda environment variables (https://docs.aws.amazon.com/lambda/latest/dg/lambda-environment-variables.html).
This question is already answered but since it came up first for me I thought I'd share another solution.
My use-case was to send a signed request to custom HTTP API hosted on AWS where cognito was used as authentication backend that only had ALLOW_USER_SRP_AUTH enabled (so no ALLOW_ADMIN_USER_PASSWORD_AUTH nor ALLOW_USER_PASSWORD_AUTH)
I ended up combining this example from AWS showing how to do cognito authentication in node:
https://www.npmjs.com/package/amazon-cognito-identity-js (Use case 4)
With the other example from AWS showing how to sign request:
https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-request-signing.html#es-request-signing-node
You plug in the second example into the first example by replacing this line (from first example):
//(...)
//refreshes credentials using AWS.CognitoIdentity.getCredentialsForIdentity()
AWS.config.credentials.refresh(error => {
if (error) {
console.error(error);
} else {
// Instantiate aws sdk service objects now that the credentials have been updated.
// example: var s3 = new AWS.S3();
console.log('Successfully logged!'); // <-- replace this line
}
});
//(...)
Second example needs some tweaks to fit your requirements, things I had to change was:
HTTP method (I needed GET)
signer declaration - I had to change service (replaced es with execute-api)
In signer.addAuthorization I had to use AWS.config.credentials (already initialized by the code from first example) instead of AWS.EnvironmentCredentials('AWS')
Hope this helps someone!
I am using instafeed.js like so:
var feed = new Instafeed({
get: 'user',
userId: 19191919191,
limit: 9,
accessToken: 'myaccesstokenhere',
target: 'instagram',
resolution: 'standard_resolution',
after: function() {
var el = document.getElementById('instagram');
if (el.classList)
el.classList.add('show');
else
el.className += ' ' + 'show';
}
});
but I am getting this error:
The access_token provided is invalid.
I got the access_token by https://www.instagram.com/developer I registered my application and put the Client Secret as the accessToken and I got my userID from here https://smashballoon.com/instagram-feed/find-instagram-user-id/
but its still saying The access_token provided is invalid. what am I doing wrong?
You cannot use your Client Secret in place of accessToken, as the Client Secret is used server-side as part of the OAuth process in order to get the user accessToken.
I suggest reviewing Instagram's Authentication docs here to be sure you're using the Authentication strategy that makes sense for your application.
It sounds like you're more likely to want to use their Client Side Implicit Authentication (at the bottom) to get an access token. You can even do this yourself manually to just get an accessToken for testing. Once you have the accessToken, then you can simply use that in the correct field for instafeed.js to load what you want from Instagram.
You can also just get your own accessToken by going to http://instagram.pixelunion.net/ and using the one generated there.
Seems you confused accessToken with Client Secret, check it here https://www.instagram.com/developer/authentication/
To make life easy you can try to generate one here http://instagram.pixelunion.net/
Update
I resolved this by removing the old pool and creating a new one. I believe the problem was in the end wrongly configured cognito roles. It works as expected now.
I'm having some issues with the API Gateway client (Javascript/Browser) when trying to access an protected lambda (Authorization : AWS_IAM) via the API Gateway.
The problem is that I keep getting "Missing Authentication Token" when calling the resource via the apiClient for my API Gateway.
I'm following the "enhanced flow" under Developer Authenticated Identities Authflow ref: http://docs.aws.amazon.com/cognito/latest/developerguide/authentication-flow.html?shortFooter=true
Is it possible to protect lambdas like this at all, or do I have to use an authorizer for my case? I was under the impression that my temporary credentials from cognito could be used as a IAM for the duration, but it could explain a lot. I have an authorizer planned for later - for protecting other resources.
The actual call to the lambda is like this:
var params = {};
var body = {
message: vm.message
};
var additionalParams = {
headers: {},
queryParams: {}
};
var apiClient = apigClientFactory.newClient( {
accessKey : data.awstoken.AccessKeyId,
secretKey : data.awstoken.SecretAccessKey,
sessionToken : data.awstoken.SessionToken,
region : Config.aws.region
});
apiClient.testEchoPost( params, body, additionalParams )
.then( function ( result ) {
console.info( 'TEST_RESULT', result );
})
.catch( function ( error ) {
console.error( 'dang', error );
});
}
Forgot to answer correctly. I had to recreate the pool (could probably have fixed the roles instead), after that the thing woked up and I could protect the resource with AWS_IAM.