Authenticate Apollo Client to AWS AppSync with Cognito User Pools - javascript

I am trying to connect to my AWS AppSync API using the plain Apollo Client but I am not sure how to structure the authentication header correctly.
So far I have followed the header authentication documentation here: https://www.apollographql.com/docs/react/recipes/authentication.html
And have this code, which I adapted to include the token call to the Amplify authentication service but it returns a 401 error:
const httpLink = createHttpLink({
uri: '[API end point address]/graphql'
});
const authLink = setContext((_, { headers }) => {
const token = async () => (await Auth.currentSession()).getAccessToken().getJwtToken();
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : ""
}
}
})
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
})
The only documentation I can find relating to this doesn't provide any technical instructions:
When using Amazon Cognito User Pools, you can create groups that users
belong to. This information is encoded in a JWT token that your
application sends to AWS AppSync in an authorization header when
sending GraphQL operations.
From here: https://docs.aws.amazon.com/appsync/latest/devguide/security.html
I know that token is fine because if I use the AppSync JavaScript API then it works. Is there anywhere I can go to find out how to achieve this or does someone know how?
Edit:
So far i have tried changing this line:
authorization: token ? `Bearer ${token}` : ""
The following attempts:
token
jwtToken: token
authorization: token
Authorization: token
None of these have worked either.

Disclaimer: Never tried it, but here is what I would do:
Check out the AppSync Client code here as a foundation for creating a an Authentication link for Apollo Client and the AppSync server. It looks like that code provides the scaffolding for each of the available authentication methods.
Specifically, if you are trying to use the OPENID_CONNECT method of authentication, it appears as if the JWT token does not need to be prepended by Bearer (line 156).

You can see an example of it on Github from AWS sample.
Works with AppSync but very similar.
// AppSync client instantiation
const client = new AWSAppSyncClient({
url: GRAPHQL_API_ENDPOINT_URL,
region: GRAPHQL_API_REGION,
auth: {
type: AUTH_TYPE,
// Get the currently logged in users credential.
jwtToken: async () => (await Auth.currentSession()).getAccessToken().getJwtToken(),
},
// Amplify uses Amazon IAM to authorize calls to Amazon S3. This provides the relevant IAM credentials.
complexObjectsCredentials: () => Auth.currentCredentials()
});
Link to the AWS repo

Related

Clash Royale Node Fetch with Key

Im trying to make a discord bot where if you type -cr into the chat, it takes the Arguments of the user (Being the Clash Royale Player's player tag) and would then use the package node-fetch to receive data with my specified endpoint. I am constantly running into the error of { reason: 'accessDenied', message: 'Invalid authorization' }. Im rather new to this stuff, especially API's, but im hoping to access certain data which I can decide later on (Which I know how to do). My code is :
const fetch = require('node-fetch')
module.exports = {
name: 'clash',
aliases: ['cr', 'clashroyale'],
category: 'This',
utilisation: '{prefix}clash',
async execute(client, message) {
var msgArgs = message.content.slice(this.name.length + 1)
var endpoint = `/players/${msgArgs}`
var url = `https://api.clashroyale.com/v1`
var token = `hidingmytoken`
fetch(url + endpoint, {
method: 'POST',
headers: {
"Authorization": token
}
}).then(data => data.json()).then(json => {
console.log(json)
})
},
};
The message parts with msgArgs and discord sides all work but fetching that clash Royale API is a big hurdle for me. The API for Clash Royale can be found here https://developer.clashroyale.com/#/documentation and Im just generally stuck on this whole concept. Im using version 2.6.6 of node-fetch so I can use the require() method which should work if that does matter. In general, how can I pass my token properly to receive that API data?
Since the Clash Royale API uses bearer authentication, you need to specify that it will be a bearer token.
headers: {
'Authorization': `Bearer ${token}`
}
I've implemented the following functionality. The code is written in GO but you can copy the logic and translate into your language.
The library have the following functionality:
Login
Token generation
Token list
Token delete
https://github.com/alessiosavi/GoClashRoyale/blob/master/api/auth.go

How can I authenticate to request Firebase REST api to fetch the list of firebase projects of a signed-in user?

I wish to fetch the list of firebase projects of a signed-in user (not me) on my website.
I can't seem to request the firebase rest api correctly (endpoint) as I receive a 401 UNAUTHENTICATED error.
The steps I follow:
I am using firebase.auth().signInWithPopup on my browser to authenticate users. Here are the scopes used:
// Google provider
const provider = new GoogleAuthProvider();
// Additionnal scopes
provider.addScope("https://www.googleapis.com/auth/cloud-platform");
provider.addScope("https://www.googleapis.com/auth/firebase");
provider.addScope("https://www.googleapis.com/auth/datastore");
// Signin
signInWithPopup(auth, provider);
I am then using getIdToken method to get a token from the user.
const token = await auth.currentUser.getIdToken(true);
I use this token to request https://firebase.googleapis.com/v1beta1/projects:
const data = await window.fetch("https://firebase.googleapis.com/v1beta1/projects", {
headers: {
authorization: `Bearer ${token}`,
},
})
.then(res => res.json());
but I am always getting a 401 error:
{
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
What am I doing wrong?
To get a list of Firebase projects for a user you need an OAuth2 token for a user that is a collaborator on the projects. You are trying to use the ID token/JWT for a Firebase Authentication user, which is not the same.
Follow the documentation on generating an access token to get value that you can use to get a list of projects.
I just found the answer, the firebase SDK does give you an accessToken with the idToken. You can then use this token instead of the idToken for any of your REST api needs.
const result = await signInWithPopup(auth, provider);
// This gives you a Google Access Token. You can use it to access the Google API.
const credential = GoogleAuthProvider.credentialFromResult(result);

How do I make authenticated requests to Amplify GraphQL using Firebase Auth as OIDC?

I need help setting up Firebase Auth + Amplify GraphQL. I'm trying to log in using federated sign with securetoken.google.com/PROJECT-ID as the provider, and it seems to log in alright because when I call Auth.currentAuthenticatedUser() I get the token, and when listening to Hub "signIn" event I get the token. My problem is making authenticated requests to my GraphQL API.
const signIn = async () => {
try {
// already logged in using firebase so I just need to get the token from the current user
const tokenResult = await currentUser?.getIdTokenResult()
await Auth.federatedSignIn('securetoken.google.com/PROJECT-ID', {
token: tokenResult?.token,
})
const res = await Auth.currentAuthenticatedUser()
console.log('token', res.token) // eyjhxxxxxxxxxx...
} catch (error) {
// ...
}
}
const client = new AWSAppSyncClient({
url: AppSyncConfig.aws_appsync_graphqlEndpoint,
region: AppSyncConfig.aws_appsync_region,
auth: {
type: AppSyncConfig.aws_appsync_authenticationType,
jwtToken: () => getToken(),
},
})
const getToken = async () => {
const token = await Cache.getItem('#accessToken')
return token
}
When calling Auth.currentSession() I get "No current user". Also, I do see the token in the Authorization header when I attempt to fetch data.
I have had a similar issue so here are some things you can have a look at.
In the Appsync in the AWS Console
https://eu-west-1.console.aws.amazon.com/appsync/home
Make sure that your primary authorization mode is set to Open Id Connect, or add another authorization provider specifying "OpenId Connect" if you are happy with the primary.
If that does not solve it, you can try to add the #aws_oidc AppSync directive to your GraphQL schema.
type Query {
getPosts:[Post!]! #aws_oidc
}
or
type Post
#model
#auth(
rules: [
{ allow: owner, provider: oidc }
...
more here: https://aws.amazon.com/blogs/mobile/graphql-security-appsync-amplify/
Lastly, if you have more than one authorization provider, you might have to switch the primary authorization provider to "OpenId Connect" - the issue I had was that Cognito (primary) blocked my secondary API Key authorization provider.
Update
AWS uses IAM roles for everything related to security. So when you authenticate with whichever authentication provider an IAM role will be assigned to that request, and that IAM role needs permission on the resource in question, like execute permission on GraphQL queries, scanning of DynamoDB tables etc. as per this image:
So you might need specific rules set in the IAM console for the IAM role in question - or at least check that it has permission - if not, you'll also get an unauthorized error message in the Appsync GraphQL query console.
more here: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WIF.html?icmpid=docs_ddb_console
and here: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/specifying-conditions.html?icmpid=docs_ddb_console
Try removing the cookie storage configuration in aws-exports.js may solve it. Maybe this helps you.
More discussion here Link-1 and Link-2

Azure Chatbot Token Server

I have an azure chat bot and I use it per direct line channel.
It is working fine if I use the secret directly in the HTML, but due to safety reasons I want to use Tokens. Thats why I used that:
<script>
window
.fetch('http://XXXXXXXX.azurewebsites.net/token-generate',
{
method: 'POST'
})
.then(function(res) {
return res.json();
})
.then(function(json) {
const token = json.token;
window.WebChat.renderWebChat({
directLine: window.WebChat.createDirectLine({
token: token
})
},
document.getElementById('webchat'));
document.querySelector('#webchat > *').focus();
});
</script>
It is like that and not with an async function because it needs to work on IE11 too.
My index.js in my bot looks like this:
// Create HTTP server
const server = restify.createServer({
name: 'token-server'
});
server.listen(process.env.port || process.env.PORT || 3978, function() {
console.log(`\n${ server.name } listening to ${ server.url }`);
console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator');
console.log('\nTo talk to your bot, open the emulator select "Open Bot"');
});
server.post('/token-generate', async (_, res) => {
console.log('requesting token ');
res.setHeader('Access-Control-Allow-Origin', '*');
console.log(res);
try {
const cres = await fetch('https://directline.botframework.com/v3/directline/tokens/generate', {
headers: {
authorization: `Bearer ${ process.env.DIRECT_LINE_SECRET }`
},
method: 'POST'
});
// console.log(cres);
const json = await cres.json();
// console.log(json);
// json.header.append('Access-Control-Allow-Origin: *');
console.log(json);
if ('error' in json) {
res.send(500);
} else {
res.send(json);
}
} catch (err) {
res.send(500);
}
});
That is some code I found after some research how to use tokens to render the Webchat.
My problem is, that when I use this html code, I get some Errors:
Access to fetch at 'http://compliancebotbbraun-bot.azurewebsites.net/token-generate' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
testbot.html:1 Uncaught (in promise) TypeError: Failed to fetch
and I just don't know how to change the Access-Control-Allow-Origin header. I don't find anything online and if I find something, it is not even close to my code.
It is working exactly as I tought it would work in IE11, but in Chrome,Edge and Firefox (idk for others, only tested these) these Errors are occuring.
I hope someone here can help me.
Based on my understanding , you exposed an API to grant access tokens to your bot clients by post method to your bot clients. Your bot clients use JS script to invoke this API . As you are using post method, so your bot clients will encounter CORS issues .
Based on the host of /token-generate url , this API is hosted on Azure webapp , you can just refer to this doc to define allowed domains to call this API from a static page by JS on Azure portal directly.
You can find the Azure webapp which hostes your API code here :
And open CORS settings here :
If you are just testing your bot from local static html file , adding "*" and remove other domains in CORS config will solve this issue .
Test result :
Hope it helps . If you have any further concerns , pls feel free to let me know .

Postman AWS Rest API -- as javascript?

I made a simple IAM authenticated API that returns a random number. [GET only]
Postman call works ok:
https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-use-postman-to-call-api.html?shortFooter=true
What is a simple way of getting the postman call to plain javascript
(No npm or webpack)
Thanks heaps
You can use axios and aws4 library in javascript to make api calls and send a signed request respectively.You need to authenticate the user via Cognito and retrieve temporary credentials (access key, secret key, and session token)
let request = {
host: 'myapi.execute-api.us-west-2.amazonaws.com',
method: 'GET',
url: 'https://myapi.execute-api.us-west-2.amazonaws.com/foo/bar',
path: '/foo/bar'
}
let signedRequest = aws4.sign(request,
{
// assumes user has authenticated and we have called
// AWS.config.credentials.get to retrieve keys and
// session tokens
secretAccessKey: AWS.config.credentials.secretAccessKey,
accessKeyId: AWS.config.credentials.accessKeyId,
sessionToken: AWS.config.credentials.sessionToken
})
delete signedRequest.headers['Host']
delete signedRequest.headers['Content-Length']
let response = await axios(signedRequest)
This article might help you with the basic code to get temporary credentials from cognito and authenticate the user.

Categories