Retrieve oauth access token using azure implicit flow - node.js

I am using the azure node based api to set up an implicit flow of oauth v2. Upon typing in the url within the browser, although the page doesn't return anything - the browser url is updated to contain access_token and other parameters, following the redirect. I am looking to extract these by using a curl command instead and execute it in nodejs on the server side. I have been trying to send in the below curl request:
curl -i -g -H "Content-Type: application/json" -d "{"client_id":"[client_id]","response_type":"id_token+token","scope":"open_id api://[client_id]/access_as_user","response_mode":"fragment","state":"12345","nonce":"678910","redirect_uri":"http://localhost:3000/account/"}" 'https://login.microsoftonline.com/microsoft.onmicrosoft.com/oauth2/v2.0/authorize'
the error i am getting is :
HTTP/1.1 400 Bad Request
Cache-Control: private
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.5
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
x-ms-request-id: b64ffac8-500a-48c9-ab61-7e64d74f0600
Set-Cookie: x-ms-gateway-slice=005; path=/; secure; HttpOnly
Set-Cookie: stsservicecookie=ests; path=/; secure; HttpOnly
X-Powered-By: ASP.NET
Date: Wed, 10 Jan 2018 09:02:16 GMT
Content-Length: 381
{"error":"invalid_request","error_description":"AADSTS90004: Malformed JSON\r\nTrace ID: b64ffac8-500a-48c9-ab61-7e64d74f0600\r\nCorrelation ID: 64d444f1-1dd6-4ba6-b75f-876778515239\r\nTimestamp: 2018-01-10 09:02:19Z","error_codes":[90004],"timestamp":"2018-01-10 09:02:19Z","trace_id":"b64ffac8-500a-48c9-ab61-7e64d74f0600","correlation_id":"64d444f1-1dd6-4ba6-b75f-876778515239"}
At this point, I created a node app, and using adal-js to retrieve the access token on the end point.
var express = require('express');
var AuthenticationContext = require('adal-node').AuthenticationContext;
var app = express();
var authorityUrl = 'https://login.windows.net/' + sampleParameters.tenant + '/oauth2/authorize?response_type=code&client_id=<client_id>&response_type=<response_type>&scope=api://<client_id>/access_as_user&response_mode=<response_mode>&state=<state>&nonce=<nonce>&redirect_uri=http://localhost:3000/account';
app.get('/account', function(req, res) {
var authenticationContext = new AuthenticationContext(authorityUrl);
authenticationContext.acquireToken(authorityUrl, function(err, response) {
var message = '';
if (err) {
message = 'error: ' + err.message + '\n';
}
message += 'response: ' + JSON.stringify(response);
if (err) {
res.send(message);
return;
}
});
app.listen(3000);
The error, I see on the browser, when i log in: (note that all valid parameters have been entered above)
Error: acquireToken requires a function callback parameter.
Could anyone help with resolving the issue - trying to extract the access token from the output?
Reference link: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols-implicit

Basically, implicit flow is used for JavaScript Single Page Application (SPA), while adal-node is used for Azure AD v1.0 endpoint, not for v2.0 endpoint. See Azure AD v2.0 authentication libraries.
For your scenario (Node.js Web API), I would recommend you use OAuth 2.0 authorization code flow to protect your Web APIs. Here is a step by step instruction for securing a web API by using Node.js.

Related

using nodejs, express and basic auth how do i get username and password

I am using nodejs, express
A third party that will be calling my api will be using basic auth in the format of http://{username}:{password}#yourdomain.com/
I have tried
var auth = require('basic-auth')
app.use(function(req, res, next) {
var credentials = auth(req)
if (req.headers.authorization) {
console.log("found headers");
}
}
But this only works if the basic auth is passed in the header.
I can not seem to get the username and password from the URL(which is the only way this external party can call my api)
I then tried as suggested use url
here is what i am seeing now when i do a POST to
http://myusername:mypassword#localhost:4050/api/callback
var express = require('express');
var http = require('http');
var url = require('url');
var app = express();
app.use(function(req, res, next) {
console.log("req.protocol=",req.protocol);
console.log("req.get('host')=",req.get('host'));
console.log("req.originalUrl=",req.originalUrl);
}
http.createServer(app).listen(config.port, function () {
console.log("HTTP BandWidth listening on port " + config.port);
});
My console looks like
req.protocol= http
req.get('host')= localhost:4050
req.originalUrl=/api/callback
if i dump the entire req obj i do not see myusername or mypassword
i must be missing something obvious
Thanks
Randy
You should be able to use the built-in url package in Node.
https://nodejs.org/dist/latest-v6.x/docs/api/url.html#url_urlobject_auth
const url = require('url');
app.use(function(req, res, next) {
const urlObj = url.parse(req.protocol + '://' + req.get('host') + req.originalUrl);
console.log('Auth info: ' + urlObj.auth);
}
Hope this helps!
EDIT: Well, I take that back. It looks like the use of username and password in the URI has been deprecated, and browsers may just be ignoring that information. See RFC 3986:
3.2.1. User Information
The userinfo subcomponent may consist of a user name and,
optionally, scheme-specific information about how to gain
authorization to access the resource. The user information, if
present, is followed by a commercial at-sign ("#") that delimits it
from the host.
userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
Use of the format "user:password" in the userinfo field is
deprecated. Applications should not render as clear text any data
after the first colon (":") character found within a userinfo
subcomponent unless the data after the colon is the empty string
(indicating no password). Applications may choose to ignore or
reject such data when it is received as part of a reference and
should reject the storage of such data in unencrypted form. The
passing of authentication information in clear text has proven to be
a security risk in almost every case where it has been used.
And...
7.5. Sensitive Information
URI producers should not provide a URI that contains a username or
password that is intended to be secret. URIs are frequently
displayed by browsers, stored in clear text bookmarks, and logged by
user agent history and intermediary applications (proxies). A
password appearing within the userinfo component is deprecated and
should be considered an error (or simply ignored) except in those
rare cases where the 'password' parameter is intended to be public.
I added the bold and italics...
I tried this using the developer tools in Chrome and got the following:
General
Request URL:http://foobar:password#localhost:8888/test
Request Method:GET
Status Code:200 OK
Remote Address:127.0.0.1:8888
Response Headers
Connection:keep-alive
Content-Length:19
Content-Type:application/json; charset=utf-8
Date:Thu, 08 Dec 2016 03:52:35 GMT
ETag:W/"13-uNGID+rxNJ6jDZKj/wrpcA"
Request Headers
GET /test HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
So, it doesn't even look like the username and password info is being passed along by Chrome. Unfortunately, I think you're out of luck if you're trying to use this schema. You may have to set the authorization headers, set your own custom headers (which is what I have done in the past), or pass your credentials in the query string.

Emotion API Project Oxford base64 image

I am trying to make a call to the Emotion Api via JavaScript with in a PhoneGap app. I encoded the image into base64 and verified that the data can be decoded by one of the online tools. this is the code that i found on the web to use.
var apiKey = "e371fd4333ccad2"; //(you can get a free key on site this is modified for here)
//apiUrl: The base URL for the API. Find out what this is for other APIs via the API documentation
var apiUrl = "https://api.projectoxford.ai/emotion/v1.0/recognize";
"file" is the base64 string.
function CallAPI(file, apiUrl, apiKey)
{
// console.log("file=> " +file);
$.ajax({
url: apiUrl,
beforeSend: function (xhrObj) {
xhrObj.setRequestHeader("Content-Type", "application/octet-stream");
xhrObj.setRequestHeader("Ocp-Apim-Subscription-Key", apiKey);
},
type: "POST",
data: file,
processData: false
})
.done(function (response) {
console.log("in call api a");
ProcessResult(response);
})
.fail(function (error) {
console.log(error.getAllResponseHeaders())
});
}
function ProcessResult(response)
{
console.log("in processrult");
var data = JSON.stringify(response);
console.log(data);
}
I got back this:
Expires: -1
Access-Control-Allow-Origin: *
Pragma: no-cache
Cache-Control: no-cache
Content-Length: 60
Date: Fri, 01 Apr 2016 13:34:32 GMT
Content-Type: application/json; charset=utf-8
X-Powered-By: ASP.NET
So i tried their console test page.
https://dev.projectoxford.ai/docs/services/5639d931ca73072154c1ce89/operations/563b31ea778daf121cc3a5fa/console
I can put in an image like your "example.com/man.jpg" and it works great. but if i take the same image and have it encoded as a base 64 image all i get is "Bad Body" i have tried it both as content type "application/octet-stream" and "application/json" and get the same error. sample of the encoded looks like..and http request
POST https://api.projectoxford.ai/emotion/v1.0/recognize HTTP/1.1
Content-Type: application/octet-stream
Host: api.projectoxford.ai
Ocp-Apim-Subscription-Key: ••••••••••••••••••••••••••••••••
Content-Length: 129420
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/...
i get back:
Pragma: no-cache
Cache-Control: no-cache
Date: Fri, 01 Apr 2016 16:23:09 GMT
X-Powered-By: ASP.NET
Content-Length: 60
Content-Type: application/json; charset=utf-8
Expires: -1
{
"error": {
"code": "BadBody",
"message": "Invalid face image."
}
}
I am now not sure if you can send an image like this or not from Javascript. Can anyone tell me if my javascript is correct or if they can send an encoded base64 string image to the site.
thanks for your help
tim
This API does not accept data URIs for images. What you'll need to do is convert it to a binary blob. Though this answer is for a different Project Oxford API, you can apply the same technique.

Restricting properties sent in a ODATA4 update

When I am trying to update the a property (AvaliableQuantity) on an object and then update it via ODATA (using the oData v4 Client Code Generator). The problem is when I send the request (Fiddler output below) I get an error saying that "Only AvailableQuantity may be updated." Is there a way of specifying which of the properties can be updated (maybe via attributes?)
PATCH https://*****************/v1/DCQuantities(ProductID=33578709,DistributionCenterID=17) HTTP/1.1
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: application/json;odata.metadata=minimal
Accept: application/json;odata.metadata=minimal
Accept-Charset: UTF-8
User-Agent: Microsoft ADO.NET Data Services
Authorization: Bearer ************************
Host: **************
Content-Length: 153
Expect: 100-continue
{"#odata.type":"#ChannelAdvisor.RestApi.V1.Models.DCQuantity","AvailableQuantity":30,"DistributionCenterID":17,"ProductID":33578709,"ProfileID":22001149}
Below is the raw response from Fiddler
HTTP/1.1 400 Bad Request
Content-Length: 88
Content-Type: application/json; odata.metadata=minimal; charset=utf-8
Server: Microsoft-IIS/7.5
OData-Version: 4.0
X-ServerName: **********
Date: Wed, 16 Dec 2015 15:50:52 GMT
{
"error":{
"code":"","message":"Only AvailableQuantity may be updated."
}
}
I have been able to update properties on other objects so I know that the authentication (OAUTH2) and the basic classes are working.
Does anybody have any ideas on what I could try please?
Many thanks
Jonathan
OK, I have realised what I was doing wrong from this post (http://blogs.msdn.com/b/odatateam/archive/2014/04/10/client-property-tracking-for-patch.aspx). I was using code like this to get the object before changing it:
var product = target.Products.Expand("DCQuantities").Where(p => p.Sku == "JRSTEST1").First();
product.First().DCQuantities[0].AvailableQuantity = 42;
target.UpdateObject(product.DCQuantities[0]);
var result = target.SaveChanges();
What I needed to do was assign the result of the query to a DataServiceCollection<>() class which would then look after tracking what was actually changed and manage the whole update for me.
var product = new DataServiceCollection<Product>(target.Products.Expand("DCQuantities").Where(p => p.Sku == "JRSTEST1"));
product.First().DCQuantities[0].AvailableQuantity = 42;
var result = target.SaveChanges();

Chaining Express.js 4's res.status(401) to a redirect

I'd like to send a response code of 401 if the requesting user is not authenticated, but I'd also like to redirect when the request was an HTML request. I've been finding that Express 4 doesn't allow this:
res.status(401).redirect('/login')
Does anyone know of a way to handle this? This might not be a limitation of Express, since I'm asking to essentially pass two headers, but I don't see why that should be the case. I should be able to pass a "not authenticated" response and redirect the user all in one go.
There are some subtle diferences with the methods for sending back a new location header.
With redirect:
app.get('/foobar', function (req, res) {
res.redirect(401, '/foo');
});
// Responds with
HTTP/1.1 401 Unauthorized
X-Powered-By: Express
Location: /foo
Vary: Accept
Content-Type: text/plain; charset=utf-8
Content-Length: 33
Date: Tue, 07 Apr 2015 01:25:17 GMT
Connection: keep-alive
Unauthorized. Redirecting to /foo
With status and location:
app.get('/foobar', function (req, res) {
res.status(401).location('/foo').end();
});
// Responds with
HTTP/1.1 401 Unauthorized
X-Powered-By: Express
Location: /foo
Date: Tue, 07 Apr 2015 01:30:45 GMT
Connection: keep-alive
Transfer-Encoding: chunked
With the original (incorrect) approach using redirect:
app.get('/foobar', function (req, res) {
res.status(401).redirect('/foo')();
});
// Responds with
HTTP/1.1 302 Moved Temporarily
X-Powered-By: Express
Location: /foo
Vary: Accept
Content-Type: text/plain; charset=utf-8
Content-Length: 38
Date: Tue, 07 Apr 2015 01:26:38 GMT
Connection: keep-alive
Moved Temporarily. Redirecting to /foo
So it looks like redirect will abandon any previous status codes and send the default value (unless specified inside the method call). This makes sense due to the use of middleware within Express. If you had some global middleware doing pre-checks on all requests (like checking for the correct accepts headers, etc.) they wouldn't know to redirect a request. However the authentication middleware would and thus it would know to override any previous settings to set them correctly.
UPDATE: As stated in the comments below that even though Express can send a 4XX status code with a Location header does not mean it is an acceptable response for a request client to understand according to the specs. In fact most will ignore the Location header unless the status code is a 3XX value.
You can certainly send a Location: /login header alongside with your 401 page, however, this is ill-advised, and most browsers will not follow it, as per rfc2616.
One way to do overcome this, is to serve <meta http-equiv="refresh" content="0; url=/login"> alongside with your 401 page:
res.set('Content-Type', 'text/html');
res.status(401).send('<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=/login"></head></html>');
I fell on the same issue and decided to use the session to handle this kind of job.
I didn't want to have an intermediate view...
With the below code, I can redirect to the homepage, which will be rendered with 401 unauthorized code.
app.get('patternForbiddenRoute', (req, res, next) => {
// previousCode
if (notForbidden === true) {
return res.render("a_view");
}
req.session.httpCode = 401;
res.redirect('patternHomeRoute');
});
app.get('patternHomeRoute', (req, res, next) => {
res.render("my_home_view", {}, (error, html) => {
// ... handle error code ...
const httpCode = req.session.httpCode;
if (httpCode !== undefined) {
delete req.session.httpCode;
res.status(httpCode);
}
res.send(html);
}));
});

Handling Accept headers in node.js restify

I am trying to properly handle Accept headers in RESTful API in node.js/restify by using WrongAcceptError as follows.
var restify = require('restify')
; server = restify.createServer()
// Write some content as JSON together with appropriate HTTP headers.
function respond(status,response,contentType,content)
{ var json = JSON.stringify(content)
; response.writeHead(status,
{ 'Content-Type': contentType
, 'Content-Encoding': 'UTF-8'
, 'Content-Length': Buffer.byteLength(json,'utf-8')
})
; response.write(json)
; response.end()
}
server.get('/api',function(request,response,next)
{ var contentType = "application/vnd.me.org.api+json"
; var properContentType = request.accepts(contentType)
; if (properContentType!=contentType)
{ return next(new restify.WrongAcceptError("Only provides "+contentType)) }
respond(200,response,contentType,
{ "uri": "http://me.org/api"
, "users": "/users"
, "teams": "/teams"
})
; return next()
});
server.listen(8080, function(){});
which works fine if the client provides the right Accept header, or no header as seen here:
$ curl -is http://localhost:8080/api
HTTP/1.1 200 OK
Content-Type: application/vnd.me.org.api+json
Content-Encoding: UTF-8
Content-Length: 61
Date: Tue, 02 Apr 2013 10:19:45 GMT
Connection: keep-alive
{"uri":"http://me.org/api","users":"/users","teams":"/teams"}
The problem is that if the client do indeed provide a wrong Accept header, the server will not send the error message:
$ curl -is http://localhost:8080/api -H 'Accept: application/vnd.me.org.users+json'
HTTP/1.1 500 Internal Server Error
Date: Tue, 02 Apr 2013 10:27:23 GMT
Connection: keep-alive
Transfer-Encoding: chunked
because the client is not assumed to understand the error message, which is in JSON, as
seen by this:
$ curl -is http://localhost:8080/api -H 'Accept: application/json'
HTTP/1.1 406 Not Acceptable
Content-Type: application/json
Content-Length: 80
Date: Tue, 02 Apr 2013 10:30:28 GMT
Connection: keep-alive
{"code":"WrongAccept","message":"Only provides application/vnd.me.org.api+json"}
My question is therefore, how do I force restify to send back the right error status code and body, or am I doing things wrong?
The problem is actually that you're returning a JSON object with a content-type (application/vnd.me.org.api+json) that Restify doesn't know (and therefore, creates an error no formatter found).
You need to tell Restify how your responses should be formatted:
server = restify.createServer({
formatters : {
'*/*' : function(req, res, body) { // 'catch-all' formatter
if (body instanceof Error) { // see text
body = JSON.stringify({
code : body.body.code,
message : body.body.message
});
};
return body;
}
}
});
The body instanceof Error is also required, because it has to be converted to JSON before it can be sent back to the client.
The */* construction creates a 'catch-all' formatter, which is used for all mime-types that Restify can't handle itself (that list is application/javascript, application/json, text/plain and application/octet-stream). I can imagine that for certain cases the catch-all formatter could pose issues, but that depends on your exact setup.

Resources