Azure Chatbot Token Server - javascript

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 .

Related

Fetch problem with node.js express server

I'm having some trouble with the fetch and node.js.
In my frontend when i click a button, i would like to send a post request in order to receive an array from my backend as answer. I'n my backend i'm using node.js with express, in my frontend i'm using the fetch function.
The error that occours is the following:
Access to fetch at 'http://localhost:8080/api' from origin 'real localhost address' 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.
Code Here
const getArray = async() => {
const data = await fetch ("http://localhost:8080/api");
const dataJson = await data.json();
console.log(dataJson)
}
getArray();
In my server i've got
app.post("/api", (req,res) => {
res.sendFile(JSON.stringify(arr));
});
You need to add request options. Please refer to the MDN docs for further information.
As #Kudah said, you should read the docs.
Fetch (and XMLHttpRequest) follow the same-origin policy. This means that browsers restrict cross-origin HTTP requests from within scripts. A cross-origin request occurs when one domain (for example http://example2.com/) requests a resource from a separate domain (for example http://example1.com/).
The easiest way to solve this, (If you don't want to dig too much into this)
const whiteList = [ "https://myRealBackendUrl-1", "https://myRealBackendUrl-2" ];
// you can also pass a string here instead here instead of array
const corsOptions = {
credentials: true,
origin: process.env.NODE_ENV !== production ? "http://localhost:4000" : whiteList
// if you are in a dev environment, you probably want something like localhost
// http://localhost:4000 is just a demo backend. replace it with your own.
// if you are in a production environment, for example heroku then your backend
// url will be something like http://example.herokuapp.com
// in that case `const whiteList = [ "http://example.herokuapp.com" ];`
};
app.use(cors(corsOptions));
The above code should be enough for the normal use case.
There is also callback function, it is if you want to run some function of your own. Don't read it if you dont plan to use any dynamic checking
var corsOptionsDelegate = async (req, callback) => {
var corsOptions = { origin: false };
try {
// you can do some dynamic check here
// For example: check database for some conditions then allow access
if( myDatabaseSays == true ) corsOptions.origin = true;
else corsOptions.origin = false;
} catch (err) {
console.log(err);
// corsOptions.origin = false;
}
callback(null, corsOptions) // chain it
}
Anyway read the docs properly for more info
[1]: https://expressjs.com/en/resources/middleware/cors.html

CORS policy issue while reading RSS Feed in React

I am trying to create a react App where I need to parse some RSS news feeds from url "https://news.google.com/news/rss" but I am getting an error "request has been blocked by CORS policy: "No 'Access-Control-Allow-Origin' header is present on the requested resource". I did the similar project in Android app where I fetched some feeds using AsyncTasks in java it didn't showed me any CORS issue, I want to understand why it worked on Android app and not in Web Application? Is it the browser that is enforcing the CORS or is the google server that is enforcing some sort of CORs policy?
let xmlText;
axios
.get(
"https://news.google.com/news/rss",
)
.then((response) => {
xmlText = response;
return response;
})
.then((textResponse) => {
console.log("Fetching response as", textResponse);
xmlText = textResponse;
})
.catch((error) => {
console.log(error);
});
You can use a plugin
google-news-json
Google News JSON API
Installation
npm install --save google-news-json
Or
yarn add google-news-json
Usage
Usage example:
let googleNewsAPI = require("google-news-json");
let news = await googleNewsAPI.getNews(googleNewsAPI.TOP_NEWS, null, "en-GB");
Also supports callback
googleNewsAPI.getNews(googleNewsAPI.SEACRH, "apple", "en-GB", (err,response) => {
console.log(response);
});
Parameters
Method (defaults to TOP_NEWS or HIGHLIGHTS)
Query (this is ignored when method is TOP_NEWS or HIGHLIGHTS)
Locale (defaults to en-GB)
Callback (not required)
Methods
HIGHLIGHTS, TOP_NEWS, LOCATION, SEARCH, TOPIC, GEO
Supported TOPICS
TOPICS_WORLD, TOPICS_NATION, TOPICS_BUSINESS, TOPICS_TECHNOLOGY, TOPICS_ENTERTAINMENT, TOPICS_SCIENCE, TOPICS_SPORTS, TOPICS_HEALTH

Api is not fetching in reactJs

I am trying to fetch food by its key. In postman api is working fine but is the forntend it has no response.
backend code
app.get('/foods/:key', (req, res) => {
foodsCollection.find({ key: req.params.key }).toArray((err, documents) => {
res.send(documents[0])
})
})
frontend code
const { key } = useParams()
const [foodById, setFoodById] = useState({})
useEffect(() => {
fetch(`http://localhost:5000/foods/${key}`)
.then((res) => res.json())
.then((data) => {
setFoodById(data)
})
}, [key])
Although you've added some images above, the most important is missing, namely, what are the Browser's Developer Tools stating the problem is. You should see some message in the Console tab, as well as in the Network tab for that particular request, if it is indeed being made. Until anyone sees this, it will be very difficult to help in fixing your problem.
If your not already, I suggest scaffolding any react app with create-react-app (CRA). This will give you a working app to start from. You can ignore CORS related issues in development, if using CRA, by adding "proxy": "http://localhost:5000", to your package.json file, see here for more on this method, but remember, this is only works for local development. You can also start Chrome to ignore Web Security by running it with the --disable-web-security flag e.g. chromium --disable-web-security, but that isn't a great idea really, more a way to quickly determine if you are having CORS problems, as Chrome masks some problems as CORS related, when in fact they aren't.
I'd also suggest changing your fetch code to use await, so instead you'd have:
const response = await fetch(`http://localhost:5000/foods/${key}`);
if (!response.ok) {
console.error(`Error message: ${response.statusText} ${response.status}`);
}
const result = response.json();
console.log(result);
This isn't necessary, but I've always found it way easier to read than the then/catch/finally method.
Reason for error
You need to stringify an object before sending it to the client with the JSON.stringify() method. When we exchange data to/from a web server, it must be a string.
Solution:
Proper way to send response to the client would to wrap the entire API in a try-catch block and explicitly specify the HTTP Status Code along with the stringified data in every response.
Note: Although 500 status code is used for error handling, you should choose one as per the use case.
app.get('/foods/:key', (req, res) => {
try {
/*
rest of the code
*/
foodsCollection.find({ key: req.params.key }).toArray((err, documents) => {
if (err) {
// 500 stands for internal server error
return res.status(500).send(JSON.stringify('Here goes a meaningful error message!'));
}
// 200 stands for success
res.status(200).send(JSON.stringify(documents[0]));
});
/*
rest of the code
*/
} catch (error) {
// 500 stands for internal server error
res.status(500).send(JSON.stringify('Here goes another meaningful error message!'));
}
})
The problem is that you haven't set the CORS headers of response in your backend code. and you are using different ports in your backend and frontend (5000 & 3000) so the Same Origin Policy disallows reading the remote resource, indicating that the request was blocked due to violating the CORS security rules.
you've to set the CORS headers.
you can install the CORS npm package and follow it's instructions to resolve the issue like this:
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
.
.
.
And one other issue that I'm seeing is that you've put the react-router default route before your specified path. so move the <route path="*"> after <route path="/foods/:key">

Authenticate Apollo Client to AWS AppSync with Cognito User Pools

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

How to set username and password in axios get method header

I want to fetch some data from a server via axios in my react project. When i put the url on browser and hit enter browser ask me username and password and after that, i can see the json data. But i dont know how to set the password and username in axios header in a get method. I have searched it in many forums and pages,especially this link didin't help me: Sending axios get request with authorization header . So finally i tried (many things before this, but i was more confused):
componentDidMount() {
axios.get('http://my_url/api/stb', {auth: {
username: 'usrnm',
password: 'pswrd'
}})
.then(function(response) {
console.log(response.data);
console.log(response.headers['Authorization']);
}).catch(err => console.log(err));
}
And i can not get anything. I get this error in console:
Error: Network Error
Stack trace:
createError#http://localhost:3000/static/js/bundle.js:2195:15
handleError#http://localhost:3000/static/js/bundle.js:1724:14
Actually, the api documentation mentioned that with these words:
If there is no header or not correct data - server's answer will
contain HTTP status 401 Unauthorized and message:
< {"status":"ERROR","results":"","error":"401 Unauthorized request"}
For successful authentification is sufficient to add in every request
header to the API:
Authorization: Basic <base64encode("login":"password")>
The weird thing is, when i use postman, the response send me a "401 unauthorized" response below the body section. But i can not see any 401 errors in browser's console.
Ok i found the solution. As i mentioned in the comments that i wrote for my question, there was a cors problem also. And i figured out that cors problem was appearing because of that i can not authorize correctly. So cors is a nature result of my question. Whatever.. I want to share my solution and i hope it helps another people because i couldent find a clear authorization example with react and axios.
I installed base-64 library via npm and:
componentDidMount() {
const tok = 'my_username:my_password';
const hash = Base64.encode(tok);
const Basic = 'Basic ' + hash;
axios.get('http://my_url/api/stb', {headers : { 'Authorization' : Basic }})
.then(function(response) {
console.log(response.data);
console.log(response.headers['Authorization']);
}).catch(err => console.log(err));
}
And dont forget to get Authorization in single quotes and dont struggle for hours like me :)

Categories