Problem retrieving bearer access token using timer triggered Azure javascript function - javascript

I have an Azure Timer Triggered Function that needs to make various calls to the Graph API, which means I need an OAuth2 Bearer token issued by my tenancy's AAD.
So I've written the function below, which based on code I've previously written to make Ajax calls, which makes a call to AAD to get the Bearer token I need.
I've tested the URL, Client ID, Client Secret, and Grant Type settings using Postman and they returned a valid Bearer Token for me to use.
However the code makes the call, and nothing is returned, it just seems to hang.
When test-run in the Azure portal I get a
Status 503 Service Unavailable.
async function getToken() {
return new Promise((resolve, reject) => {
try {
let https = require("https");
let url =
"https://login.microsoftonline.com/<azure_tenancy>.onmicrosoft.com/oauth2/token";
let options = {
method: "POST",
headers: {
"Content-Type": "application/json; charset=utf-8",
"Cache-Control": "no-cache"
}
};
let body = {
grant_type: "client_credentials",
client_id: "<client_id>",
client_secret: "client_secret>",
resource: "https://graph.microsoft.com"
};
var req = https
.request(url, options, res => {
let data = "";
res.on("data", chunk => {
data += chunk;
});
res.on("end", () => {
resolve(JSON.parse(data));
});
})
.on("error", err => {
throw new Exception(e.message);
});
req.write(JSON.stringify(body));
req.end();
} catch (e) {
context.log("error caught");
reject(e);
}
});
}
Postman returns:
{
"token_type": "Bearer",
"expires_in": "3600",
"ext_expires_in": "3600",
"expires_on": "1558542984",
"not_before": "1558539084",
"resource": "https://graph.microsoft.com",
"access_token": "eyJ...e8mw"
}
So I know the URL, ID, and Secret I'm passing are correct. It must be something else in the code but I'm baffled as to what. Any clues?

Related

How to pass file(Image) via NextJS API Routes to external API?

I use NextJS (combined: SSR and SPA for authorized dashboard) with Django Rest FW on the backend. For auth I use JWT token, which is stored in cookies. For that reason, I need a middleware at /pages/api/* for each request to append from cookie access token.
Question:
How to implement a protected request to send file to /pages/api/upload and send it to DRF with an access token?
Sample of small API middleware
export default async (req, res) => {
const { id } = req.query
const cookies = cookie.parse(req.headers.cookie ?? "");
const access = cookies["access"] ?? false;
if (access === false) {
return res.status(401).json({
error: "User unauthorized to make this request"
});
}
if (req.method === "GET") {
try {
const apiRes = await fetch(`${LOCAL_API_URL}/items/${id}`, {
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": `Bearer ${access}`
}
});
const data = await apiRes.json();
if (apiRes.status === 200) {
return res.status(200).json(data);
} else {
return res.status(apiRes.status).json({
error: data.error
});
}
} catch(err) {
console.log(err);
return res.status(500).json({
error: "Something went wrong"
});
}
} else
res.setHeader("Allow", ["GET"]);
return res.status(405).json({
error: `Method ${res.method} is not allowed`
});
}
For sending image you should use FormData.
Firstly create an instance of FormData.
const formData = new FormData()
Then, you can add image into that.
formData.append('fieldName', someFileInput.current.files[0])
Also, if you want to add some more data with the image, you can append it to FormData too, the similar way.
formData.append('fieldName', someMoreData)
Then, you should set Content-Type to 'multipart/form-data', this is to server understand you pass the FormData.
And, finally, send the form data via Fetch.
I was glad to answer you, I hope it helps you!
the solution was raiser simple. Just passed everything I received and appended token to headers/
export default async (req, res) => {
// all extra validation
const apiRes = await fetch(`${LOCAL_API_URL}/upload/`, {
method: "POST",
headers: { ...req.headers, ...{ "Authorization": `Bearer ${access}` } },
body: req.body
});
// all extra validation
}

PostMan Pre-request script for fetching JWT using Laravel

I am trying to write a pre-request script for getting JWT for my postman tests using Laravel. I tried the js code that works perfect when I use .NET REST API, but now in Laravel it is not working. If I hit the login endpoint it works I got my JWT, and the response look like this:
{
"status_code": 200,
"access_token": "15|we59pMz1wA6TqwALTJg9IT8pNs3mc4Omwibm7Lkd",
"token_type": "Bearer"
}
Here is my pre-request JS script:
const requestBody =
{
"Email" : "username",
"Password" : "password"
}
pm.sendRequest
({
url: 'http://localhost:8000/api/login',
method: 'POST',
header:
{
'content-type': 'application/json'
},
body:
{
mode: 'raw',
raw: requestBody
}
}, function (err, res)
{
if(err)
{
console.log("Login failed:");
console.log(JSON.stringify(err));
return;
}
else
{
const response = res.json();
const token = 'Bearer ' + response.access_token;
pm.environment.set("TOKEN", token);
console.log("Login succeeded!");
}
});
The response in pre-request is this:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="refresh" content="0;url='http://localhost:8000'" />
<title>Redirecting to http://localhost:8000</title>
</head>
<body>
Redirecting to http://localhost:8000.
</body>
</html>
Your token doesn't seem like a JWT.
This is not base64 and does not contain the 3 parts: header, payload, signature.
Don't hesitate to look at my Postman pre request gist for JWT there are some explanations here too.
It contains one function to check the token validity:
/** Checks if the JWT is present and not expired
The token is expected to be found in `token` environment variable
*/
function isValidToken() {
const token = pm.environment.get("token");
if (!token) {
console.log("Token is missing");
return false;
}
// Payload is retrieved by
// JSON parsing the base64 decoded `atob()` 2nd part of the JWT `[1]`
// (1st is the header, 3rd is the signature)
const payload = JSON.parse(atob(token.split('.')[1]));
// Expiration timestamp (in seconds) is located in the `exp` key
const millisecBeforeExpiration = (payload.exp * 1000) - (new Date()).getTime();
if (millisecBeforeExpiration <= 0) {
console.log("Token is expired");
return false;
}
console.log("Token is valid",
`will expire in ${millisecBeforeExpiration / 1000} seconds`);
return true;
}
and one to get a new token
/** Gets a new JWT
This can be entirely custom authentication.
Here we rely on `user`/`pass` environment variables.
`host` also needs to be set, feel free to use another route instead of /give-me-a-jwt :)
*/
function login() {
const body = JSON.stringify({
"user": pm.collectionVariables.get("user"),
"pass": pm.collectionVariables.get("pass")
});
const request = {
url: pm.collectionVariables.get("host") + "/give-me-a-jwt",
method: "POST",
header: {
"Content-Type": "application/json",
"Accept": "application/json",
},
body,
};
pm.sendRequest(request, (err, res) => {
if (res.code !== 200) throw new Error(res.status);
console.log("Token refreshed");
pm.environment.set("token", res.json().token);
});
}
Now you can just get a new token if not valid:
if (!isValidToken()) login();
Of course don't forget to use your brand new token in your Postman's Bearer Token authentication type

How to set headers while requesting a page in nodejs?

I saw many tutorials on jwt authentication but every video maker uses Postman to show what's happening and they pass on the header in the headers section while requesting a URL in Postman. I tried to do it with JavaScript but I was not able to do it.
I want to do jwt authentication but after token generation, I send it to client side to use it for further requests but I failed to do so after trying it a few times. I also tried to set req.headers in server side but it didn't do what I wanted to..
I want to set request headers for authentication of the form "Bearer {token}" for every request after token generation. How to do it with JS??
What I am most concerned about is that every tutorial does it with postman but they didn't show how they implemented it in their own app. I hope my question is clear.
You can easily add header on your http request like that
it has been solved here Node.JS: How to send headers with form data using request module
In vanilla nodejs:
const uri = "http://example.com";
const options = {
headers: {
"Authorization": "Bearer ...."
}
}
// require http/https lib
let req = require("http").request(uri, options, (res) => {
const chunks = [];
res.on("data", function (chunk) {
chunks.push(chunk);
});
res.once("end", () => {
// concat body chunks
let body = Buffer.concat(chunks);
console.log(body.toString());
});
});
req.on("error", (err) => {
console.log(err);
});
req.end();
https://nodejs.org/dist/latest-v12.x/docs/api/http.html#http_http_request_options_callback
Something like that:
$.ajax({
url: url,
beforeSend: function(xhr) {
xhr.setRequestHeader("custom_header", "value");
},
success: function(data) {
}
});
First install jwt and express framework using npm then make a middleware file which will check if the tokek is set or not.
Middleware.js :
let jwt = require('jsonwebtoken');
const config = require('./config.js');
let checkToken = (req, res, next) => {
let token = req.headers['authorization']; // Express headers are auto converted to lowercase
if (token) {
if (token.startsWith('Bearer ')) { // Checks if it contains Bearer
// Remove Bearer from string
token = token.slice(7, token.length); //Separate Bearer and get token
}
jwt.verify(token, config.secret, (err, decoded) => { //Inser the token and verify it.
if (err) {
return res.json({
status: false,
message: 'Token is not valid'
});
} else {
req.decoded = decoded;
next();
}
});
} else {
return res.json({
status: false,
message: 'Access denied! No token provided.'
});
}
};
Next, create a config file which will contain the secrets.
Config js:
module.exports = {
secret: 'worldisfullofdevelopers'
};
Finally, create a token route which will create your token and after that the rest of the calls will be authenticated for that token.
Index.js :
const middleware = require('./middleware');
const jwt = require("jsonwebtoken");
const config = require('./config.js');
//Call token Route
app.use('/token', (req, res, next) => {
//Generate Token
let token = jwt.sign({ username: "test" },
config.secret,
{
expiresIn: '1h' // expires in 1 hours
}
);
//Send Token
res.json({
success: true,
message: 'Authentication successful!',
token: token
});
});
//Add Authentication to all routes
app.use(middleware.checkToken);
//===> All the routes after middleware will be checked for token
app.use('/getUser', (req, res, next) => {;
console.log('do something')
});
If I understand correctly, you want to set the HTTP header on the client, in order to pass an authentication token to the server. I would recommend that you use a library like **axios*.
Using axios, once you receive the toke, set the header for every outgoing communication with the following line of code:
axios.defaults.headers.common['Authorization'] = "Bearer " + token;
This will set the authentication http header to the form you need.

Alexa node.js http request with authorization

I'm implementing a skill for Amazon Alexa.I want to determine the user's geolocation, but the http.get request is not working. I tried the request in postman and I get the desired result, but couldn't figure out what could be the problem. The request url should be like this https://api.amazonalexa.com/v1/devices/{deviceId}/settings/address with the Authorization header Authorization: Bearer {token}.
Here is my code
try{
var body = "";
console.log('Authorization ', consentToken);
var response = '';
var options = {
host: 'api.eu.amazonalexa.com',
path: '/v1/devices/'+deviceId+'/settings/address',
port: '443',
headers: {'Authorization': "Bearer "+consentToken},
method: 'GET'
};
https.get(options, function(res) {
console.log('status', res.statusCode);
if (res.statusCode >= 200 && res.statusCode < 400) {
res.on('data', function(data_) { response += data_.toString(); });
res.on('end', function() {
var data = JSON.parse(response);
console.log('data', data);
if (data.length > 0)
userLocation = data;
});
}
}).on('error', function(e) {
console.log("errrrror",e);
}).on('uncaughtException', function (err) {
console.log('uncaughtException',err);
});
}
catch(error) {
console.log('getAndProcessUserLocation',error);
}
I don't know why the https.get is not executed. No error is thrown, but I couldn't get any log from it. I followed the documentation from thr official site
https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/device-address-api
Without seeing the rest of your code, my guess is that you are not indicating to the Alexa request that you are handling it asynchronously, so the session is ending before the request completes.

Connecting to my Visual Studio Online API returns HTTP 302

I'm trying to connect to my VSTS build 2.0 api through Node to create a new build definition. I'm getting HTTP 302 , which is an redirect. I'm not sure why this happening, I was able to submit same request, with same URL through SOAP UI without any issue. Below is the code snippet.
var Client = require('node-rest-client').Client;
var client = new Client();
var accessToken = 'MY ACCESS TOKEN';
var args = {
data: JSON.stringify(configJSON),
headers: {
"Content-Type": "application/json",
"Accept": "application/json"}
,auth: {
'bearer': accessToken
}
};
client.post("https://firstname.lastname.visualstudio.com/SampleApp/_apis/build/definitions?api-version=2.0", args, function (data, response) {
console.log(response.statusCode);
}).on('error', function (err) {
console.log('something went wrong on the request', err);
});
The reason is that the authentication is failed, so it redirects to the login page, that's why you get the 302 response code.
I modify the code to add Authorization to header and remove auth parameter, after that it works fine.
var args = {
data: JSON.stringify(configJSON),
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"Authorization": "Bearer " + accessToken
}
//, auth: {
// "Bearer":accessToken
//}
};
On the other hand, I suggest that you can create build definition through vsts-node-api or PowerShell.
For VSTS-node-api, there is createDefinition function in BuildApi (xxx), there is a sample about how to use it.
For PowerShell, you can use Invoke-RestMethod to call REST API, there are some samples in this article.

Categories

Resources