Auth0.js - Why make second call to /userinfo in parseHash? - javascript

In the Auth0 docs (link here), the following example is given for retrieving user info after parsing the access token, id token and expiry from the URI:
webAuth.parseHash({ hash: window.location.hash }, function(err, authResult) {
if (err) {
return console.log(err);
}
webAuth.client.userInfo(authResult.accessToken, function(err, user) {
// Now you have the user's information
});
});
I don't understand what the use of the call to webAuth.client.userInfo() is. When viewing the contents of authResult as passed to the callback for parseHash() there appears to be a property called idTokenPayload that already includes all the user information.
In fact, when looking at the network log it appears that parseHash() is making its own call to /userinfo to retrieve the full user information and include it in the authResult object passed to the callback.
Why then do the docs suggest that you repeat the call to /userinfo and get the user information again? Isn't this redundant?

Related

Overwriting an existing command with Cypress

I'm trying to overwrite an existing command in Cypress.io. I'm looking to log() a route response's status & the route's url to extend the functionality of the built-in route(). Unfortunately, I get this message The route undefined had a undefined status code. in the console. Note, I'm using the browser's console momentarily. Eventually, I'll use the log() built-in method. This is what I have tried so far:
cypress/support/commands.js
Cypress.Commands.overwrite('route', (originalFn, response) => {
console.log(`The route ${response.url} had a ${response.status} status code.`);
return originalFn(response);
});
Update:
I'm getting the route now, but I still don't get response or status. This is my current code:
Cypress.Commands.overwrite('route', (originalFn, url, response) => {
console.log(`The route ${url} had ${response} status code`);
return originalFn(url, response);
});
When using the pattern cy.route(method, url, response), the response parameter is use to stub the call and return the supplied response to your app, see (route() - Arguments)
response (String, Object, Array)
Supply a response body to stub in the matching route.
Note that creating an overwrite of cy.route() will be hooking into the route configuration, not the capture of the route.
The pattern cy.route(options) has an onResponse option which can be used to console.log() the response, but cy.log() does not work there, probably because we invoke a command inside a command.
Cypress.log() can be used instead.
cy.route({
url: 'http://example.com',
method: 'GET',
onResponse: (response => {
const message = `The route '${response.url}' had a ${response.status} status code.`;
const log = Cypress.log({
displayName: 'Route debugging',
message: message,
consoleProps: () => {
// return an object which will
// print to dev tools console on click
return {
message: message,
}
}
})
log.finish(); // remove log spinner
})
})
/*
Command log output:
ROUTE DEBUGGING
The route 'http://example.com' had a 200 status code.
*/
Depending on what you're trying to achieve, there are a couple of options. Richard's answer above describes one approach - I'll attempt to cover some others.
(Note: The Cypress documentation at https://docs.cypress.io/ probably will give you a better understanding than this answer. I'll try to link the relevant articles inline)
(You can skip ahead to the section on 'Inspecting Api Responses' if you don't care why your code isn't working)
What's happening in your code
Let's look at the example code from https://docs.cypress.io/api/commands/route.html#Examples
cy.server()
cy.route('**/users').as('getUsers')
cy.visit('/users')
cy.wait('#getUsers')
Without your overwrite, cy.route here just registers the route, so you can wait for it later (Remember, cy.route does not make any api calls itself). With your overwrite, cy.route is completely replaced with your callback:
Cypress.Commands.overwrite('route', (originalFn, url, response) => {
console.log(`The route ${url} had ${response} status code`);
return originalFn(url, response);
});
So when cy.route('**/users') is called, it will instead evaluate
(originalFn, url, response) => {
console.log(`The route ${url} had ${response} status code`); // Logs "The route **/users had undefined status code"
return originalFn(url, response); // Registers the route with an mock value of undefined
})(originalCypressRouteFn, '**/users')
You can see why response is undefined - it's not passed in to the route call at all, since the request hasn't even been made.
Note that if we were attempting to mock the call instead (See https://docs.cypress.io/api/commands/route.html#With-Stubbing)
cy.route('https://localhost:7777/surveys/customer?email=john#doe.com', [
{
id: 1,
name: 'john'
}
])
You would instead log
"The route https://localhost:7777/surveys/customer?email=john#doe.com had [object Object] status code"
Inspecting Api Responses
If you just want to inspect the response from an api, you can use the using the built-in debugging tools (after calling cypress open). The browser's Network tab is available (which will record all requests made during a given test run), and you can additionally click on the response recorded in the left panel, which will log the request and response to the browser console.
If you're attempting to assert on the response to an api call, you can use cy.wait (See https://docs.cypress.io/guides/guides/network-requests.html#Waiting) to get access to the underlying xhr request after it finishes:
cy.wait('#apiCheck').then((xhr) => {
assert.isNotNull(xhr.response.body.data, '1st API call has data')
})
If you want a record of the APIs calls made during a CLI run (using cypress run), you can:
Print debug info, which will give you a lot of information, including all requests and responses (See https://docs.cypress.io/guides/references/troubleshooting.html#Print-DEBUG-logs): DEBUG=cypress:* cypress run (You can change cypress:* to limit the scope of the debug to just api calls, though I don't know off the top of my head what the namespace you'll want is)
Use a plugin that records all requests (e.g. https://github.com/NeuraLegion/cypress-har-generator)

How to create Microsoft Account Sign-in to my website, similar to Google?

I'm working on a web project (HTML, CSS, JavaScript, with back-end in PHP). I've successfully got a Google Sign-in working, using their simple API, but can't get the Microsoft equivalent to function. The official online solutions to this seem to rely on .NET or PHP Composer. I'll try composer if that's the only way but a pure JS/PHP method would be easiest.
I've tried to use the following:
https://github.com/microsoftgraph/msgraph-sdk-javascript
https://github.com/AzureAD/microsoft-authentication-library-for-js
The code below is the closest I've come to a working solution. I can get some kind of user ID (which appears to be unique and constant for each user). This might be enough to set up the login system I want, but it would be ideal if I could also fetch their name and profile picture.
<script class="pre">
var userAgentApplication = new Msal.UserAgentApplication("MY CLIENT ID", null, function (errorDes, token, error, tokenType) {
// this callback is called after loginRedirect OR acquireTokenRedirect (not used for loginPopup/aquireTokenPopup)
})
userAgentApplication.loginPopup(["user.read"]).then(function (token) {
var user = userAgentApplication.getUser(); //this is good
//user.userIdentifier seems to be a unique ID
//I will store this and use it for future verification
console.log(user);
//START
// get an access token
userAgentApplication.acquireTokenSilent(["user.read"]).then(function (token) {
console.log("ATS promise resolved");
}, function (error) {
console.log(error);
// interaction required
if (error.indexOf("interaction_required") != -1) {
userAgentApplication.acquireTokenPopup(["user.read"]).then(function (token) {
// success
console.log("s2");
}, function (error) {
console.log("e2");
// error
});
}
});
//END
// signin successful
}, function (error) {
console.log(error);
// handle error
});
</script>
(this code won't run as I've pasted it because it relies on the MSAL script from the second github link, and needs an application client ID)
After getting the access token with scope user.read , you could call microsoft graph api to get sign-in user's profile information such as displayName , businessPhones :
https://graph.microsoft.com/v1.0/me
Content-Type:application/json
Authorization:Bearer {token}
To get user's profile photo :
GET https://graph.microsoft.com/v1.0/me/photo/$value
In addition , if you are using Microsoft Graph JavaScript Client Library in first link , you could get user's displayName and profile photo by :
client
.api('/me')
.select("displayName")
.get((err, res) => {
if (err) {
console.log(err);
return;
}
console.log(res);
});
// Example of downloading the user's profile photo and displaying it in an img tag
client
.api('/me/photo/$value')
.responseType('blob')
.get((err, res, rawResponse) => {
if (err) throw err;
const url = window.URL;
const blobUrl = url.createObjectURL(rawResponse.xhr.response);
document.getElementById("profileImg").setAttribute("src", blobUrl);
});
Please refer to code sample here .

User changed by cloud function when queried from client side returns stale data

I am having the logged in user make some changes on itself. These changes happen in the cloud function called by the client code. The cloud code executes correctly and returns back after which I query the user table to get the updated data but it gives back old data and the updated fields are missing. Using the latest available parse-latest.js from CDN.
I have already tried sending the users session token in the save call and also the useMasterKey but no change in behaviour.
Steps to reproduce
Call a cloud function that changes a field for the currently logged in User
Once cloud function has completed and returned to client side query the user table to get latest data.
If you check this data is stale data and does not include the latest data.
Here is the sample code
This is the call to the cloud function which is inside a promise.
Parse.Cloud.run("notification_update", params))
.then(function(obj) {
dfd.resolve(obj);
}, function onError(err) {
console.error(err)
dfd.reject(err);
}
The relevant code in cloud function
currentUser.set("team", team);
console.log("Session token ");
console.log(currentUser.getSessionToken());
promises.push(currentUser.save(null, { sessionToken: currentUser.getSessionToken() }));
Once the cloud function returns I call this function. I check in the parse DB the data is updated.
var refresh = function onRefresh(dfd) {
console.debug("Refresh user was triggered..");
var currentUser = Parse.User.current();
var q = new Parse.Query(Parse.User);
q.include("team");
q.get(currentUser.id)
.then(function success(user) {
currentUser.set({
team: user.get("team"), //Here the data is not updated
});
if (dfd && dfd.resolve) {
dfd.resolve(currentUser);
}
}, function onError(error) {
if (dfd && dfd.reject) {
dfd.reject(error);
}
});
}
Additionally when I check the response from the server in my Network tab in Chrome, I see that the user has all the updated data.
Why don't you just do a query against the user (me) instead of running a full pledged query to fetch the user.
Parse.User.become(sessionToken).then(function(user){
//User Object is available to you here
})

Node.js: POST request sent twice by system

I send JSON POST data via a form in a MEAN environment to my server. On the server side, I process the data inside of a waterfall function, using the async library, including various operations such as:
[...]
- create a database entry for a new author
- create a database entry for a new book
- associate the new book to an author (reference to book ID)
[...]
This is the method called by my route, which handles the associated POST-request:
exports.createAuthor = function(req, res) {
console.log(req.url+' !!!POST REQUEST INCOMING!!! '+req.body);
async.waterfall([
function(callback){
//create Author db entry
},
function(parameter, callback){
//add author to additional directory (db action)
},
function(parameter, callback){
//create book db entry
},
function(parameter, callback){
//associate book to author (db action)
}
], function (err, result) {
console.log('DONE!!!');
res.send('200');
});
}
This is the client-side AngularJS controller code:
searchApp = angular.module("searchApp",[]);
searchApp.controller('authorCreator', function ($scope,$http) {
$scope.tags = [];
$scope.sendAuthor = function(){
alert('I was called!');
$http({
method: 'POST',
url: '/newauthor/',
data: { 'authorname' : $scope.authorName,
'authordescription' : $scope.authorDescr,
'bookname' : $scope.bookName,
'tags' : $scope.tags }
})
.success(function(data){
//no actions yet
})
.error(function(){
//no actions yet
});
};
});
This is the AngularJS form:
<div ng-controller="authorCreator">
<form>
<p>Author name: <input ng-model="authorName"></p>
<p>Author description: <input ng-model="authorDescr"></p>
<p>Book name: <input ng-model="bookName"></p>
<p>Tags:<input ng-model="tags"></p>
<p><button ng-click="sendAuthor()">Send</button></p>
</form>
</div>
I noticed that, if the waterfall-process is "stuck" somewhere, meaning the client does not get an answer to it's request whatsoever, the POST request seems to be sent a second time automatically (as soon as the browser is giving a timeout according to firebug). According to firebug, a second POST request does not seem to be sent by the browser, so the call must be initiated from somewhere else.
I found out by checking the database (multiple documents with identical values, except the ObjectID of course) and monitoring the node.js console window where I output incoming POST data. Again: as soon as the entire waterfall-process completes, hence the client browser does not abort the post request after a while, and res.send('200') executes, the error does not occur (= no multiple db entries).
Can anyone please tell me, who does initiate this second POST request and how may I deactivate it?
Cheers
Igor
Try adding this:
exports.createAuthor = function(req, res) {
if(req.method == 'POST' && req.url = 'REQUESTEDURL'){
console.log('POST REQUEST INCOMING!!! '+req.body);
async.waterfall([
//TODO...
]);
}
Maybe the problem is that the favicon or some other resource is doing a request to
After spending some time on that issue I found out, that this error seems to be based on missing answers to the client (be it via res.json, res.sendfile, ...). Therefore the client seems to re-send the request after some time, thus executing server-side code a second time. Responding to the client in reasonable time solves this issue. Sorry for the confusion.
i "fixed" this by adding
.get('/favicon.ico:1', (req, res) =>{
//do nothing because i dont care
})

Express: How does this function work?

router.post('/register', function(req, res) {
User.register(new User({ username : req.body.email }), req.body.password, function(err, account) {
if (err) {
return res.render('register', { account : account });
}
passport.authenticate('local')(req, res, function () {
res.redirect('/');
});
});
});
I get the general idea of this routing post, but I don't understand all of it.
What information generally comes in through req? It seems like if I do req.body.email and if in the body of my register html page I've submitted a form with an email and password field, I can simply access them this way?
And in the function(err, account) callback, where exactly is it getting the err and account variables? I've never really understood for the callback functions how the variables were decided or what even they are, it seems like when you route it somehow takes two variables err and account?
Thanks so much!
1st-
Well you should read the ExpressJS documentation and see the difference between body, query and params but basically goes like this
body refers to the body of the request, which is the submitted data through POST or PUT
query refers to the search part of the url or query string everything after the "?"
params refers to the part of the path that is parameterized.
hope that gives you a clue of how and where to look for the information
2nd- is nodes convention that always the first variables passed to a callback is the error variablem the rest is according to the function in this case is account because the function is suppose to create db account and return the entire account information so ir can bu used by the callback
What information generally comes in through req?
The request object (req) comes with data on the request you are processing. For instance:
Fields used in the form that triggered the request (under req.body)
Parameters used in the request URL (under req.params)
Session/cookie information (under req.session)
See here for more.
And in the function(err, account) callback, where exactly is it getting the err and account variables?
By convention in Node, callbacks pass any errors as the first parameter, and any non-error results from the second parameter onwards. So you'll typically see the first parameter called err. The structure of the err object is not completely standard, but it is normal to assume there will be a message field in the err object, and perhaps some error code.
In your example, you are handing that callback to, it seems, Mongoose, or some other database handling library. This library will try to execute a register function on the User object/model/schema. When it's done, if it encountered any errors, they'll come back to you on the err object. Else, you can expect the account object to hold details on the user account.
Also: you could name err and account whatever you want, of course.

Categories