I've calling a windows REST service which requires NTLM. When I call the service using a utility like SOAP-UI and supply my credentials for NTLM authorization it works fine, I get a result. when I use httpnltm (https://www.npmjs.org/package/httpntlm) to accomplish this from node.js I always get a 401 error, with the following message: "You do not have permission to view this directory or page.". The get options set are exactly the same used in SOAP-UI and it works there.
httpntlm.get({
url: 'endpoint',
username: 'my_username',
password: 'my_password',
domain: 'my_domain'
}, function (err, res){
if(err){
callback(err.message, null);
}else{
if(res.statusCode == 401){
// always the case!
}
}
});
Is there something I am missing from this get request?
Related
I know that the question is findable on the forum but no answer works for me. I have an angular service that calls a nodeJS API like this:
Angular service
public createUser(pUser: User): Observable<User> {
var url = "http://localhost:5000/users";
var json = JSON.stringify(pUser)
return this.http.post<User>(url, pUser)
}
NodeJS API
router.post('/', (req, res) => {
console.log(req.body)
User.create({ email: req.body.email, password: req.body.password })
res.sendStatus(200);
});
The API is well called, the insertion into database works, but I have this message in the browser:
SyntaxError: Unexpected token O in JSON at position 0
The status of the request is 200 on return but the error is still present in the browser
I do not know if the problem comes from the front end or the backend.
After doing some research, I try to parse my object before sending it. I try to stringify too.
Without success
Is this someone would have the solution? thank you very much
This error occurs when you try to parse invalid JSON.
Due to the default response type for angular is JSON and the default response of code 200 is 'OK'. You have to adapt one of them.
You can change the response type to text like this:
this.http.post<User>(url, pUser, {responseType: 'text'});
Or you return a JSON object:
res.status(200).send({ status: 'OK'});
It is good practise to send status 204 (No Content) if You don't send any content in response:
res.sendStatus(204);
Your Angular App should handle it and will not throw an error.
If You send status 200, it's good to add some json object, e.g. {status: "OK"} in res.status(200).send({status: "OK"}). Otherwise You will send the string 'OK' and will get "Unexpected token O ..." (O from 'OK').
From Express doc:
res.sendStatus(200) // equivalent to res.status(200).send('OK')
i am trying to implement a single sign on for my web application. I am using gravitee.io for the access managment and token generation.
I followed the steps in gravitees quickstart tutorial and i am now at the point that i want to verify my id_token.
In order to do that i am using the node-jsonwebtoken library. i am using total.js for my backend (which should not be as important, but i still wanted to mention it).
What i have done so far.
i have my client-id and my client-secret as well as my domain secret in the total.js config file
./configs/myconfig.conf (key/secret is changed)
url : https://sso.my-graviteeInstance.com/am
client-id : myClientId
client-secret : uBAscc-zd3yQWE1AsDfb7PQ7xyz
domain : my_domain
domain-public-key : EEEEEB3NzaC1yc2EAAAADAQABAAABAQCW4NF4R/sxG12WjioEcDIYwB2cX+IqFJXF3umV28UCHZRlMYoIFnvrXfIXObG7R9W7hk6a6wbtQWERTZxJ4LUQnfZrZQzhY/w1u2rZ3GEILtm1Vr1asDfAsdf325dfbuFf/RTyw666dFcCcpIE+yUYp2PFAqh/P20PsoekjvoeieyoUbNFGCgAoeovjyEyojvezxuTidqjaeJvU0gU4usiiDGIMhO3IPaiAud61CVtqYweTr2tX8KabeK9NNOXlTpLryBf3aTU1iXuU90mijwXZlmIzD28fWq+qupWbHcFZmmv3wADVddnxZHnFIN7DHGf5WVpb3eLvsGkIIQpGL/ZeASDFa
i added a model to handle the login workflow for total.js in order to get the jwt tokens from gravitee by REST-call.
So far everything works as expected. a session is created and stores the response in it. the gravitee response is the expected json which looks like this
{
access_token: 'some-long-token',
token_type: 'bearer',
expires_in: 7199,
scope: 'openid',
refresh_token: 'another-long-token',
id_token: 'last-long-token'
}
I split up the tokens in seperate cookies because when i tried to save them as a single cookie, i got an error that told me the cookie exceeds the 4096 length limit.
So far everything works just fine. in the frontend ajax call the success callback will be executed, just setting the window.location.href='/'; to call the dashboard of my application. I set this route to be accessible only when authorized, so that when my dashboard is called, the onAuthorize function is called by totaljs.
F.onAuthorize = function (req, res, flags, callback) {
let cookie = mergeCookies(req.cookie);
// Check the cookie length
if (!cookie || cookie.length < 20) {
console.log(`cookie not defined or length to low`);
return callback(false);
}
if (!cookie) {
console.log(`cookie undefined`);
return callback(false);
}
// Look into the session object whether the user is logged in
let session = ONLINE[cookie.id];
if (session) {
console.log(`there is a session`);
// User is online, so we increase his expiration of session
session.ticks = F.datetime;
jwt.verify(
session.id_token,
Buffer.from(CONFIG('client-secret')).toString('base64'),
function(err, decoded){
if (err) {
console.log(`jwt verify error: ${err}`);
return callback(false);
}
console.log(`decoded token user id: ${decoded.sub}`);
return callback(true, session);
})
}
console.log(`false`);
callback(false);
};
I also tried to just send the CONFIG('client-secret') without buffering. I also tried to send the CONFIG('domain-public-key'). But the error i get is always the same:
jwt verify error: JsonWebTokenError: invalid algorithm
When i copy and paste the id_token into the debugger at jwt.io with algorithm beeing set to RS256 i'll see the following decoded values:
// header
{
"kid": "default",
"alg": "RS256"
}
// payload
{
"sub": "some-generated-id",
"aud": "myClientId",
"updated_at": 1570442007969,
"auth_time": 1570784329896,
"iss": "https://sso.my-graviteeInstance.com/am/my_domain/oidc",
"preferred_username": "myUsername",
"exp": 1570798729,
"given_name": "Peter",
"iat": 1570784329,
"family_name": "Lustig",
"email": "peter.lustig#domain.com"
}
i copied the public key from my domain in to the respective textfield and i also tried to use the client-secret. no matter what i do, the error i am getting here is
Warning: Looks like your JWT signature is not encoded correctly using
base64url (https://www.rfc-editor.org/rfc/rfc4648#section-5). Note that
padding ("=") must be omitted as per
https://www.rfc-editor.org/rfc/rfc7515#section-2
I dont understand why there is an algorithm error when i try to verify the token in my backend and some encoding error at jwt.io debugger.
can somebody explain to me on how to fix the issue? Thanks in advance
Pascal
edit: changed title
Please I have written an API in express js for a DELETE request which checks for a password parameter before doing other things. This whole thing works locally on postman, but not with the hosted server on heroku.
Here is a snippet from the API
...
if (!req.body.password) {
return res.status(400).json({ message: "input password"})
}
...
And on my Client side, I have
axios.delete('url/id', ({password: 'password'}))
.then()
.catch()
The Issue is this, req.body.password is not being noticed, it is seen as undefined on the hosted server
It's not common to pass something into delete body so it's different than post, put and patch requests as you can see here
You should do it like this:
axios.delete('/some/uri', { data: 'my delete body' })
We are currently struggling with a Uncaught Error: Can't set headers after they are sent. error message when trying to chain a user sign in into a test with chai-http.
The test signs in as a user which already exists in the database via our API and then should attempts to GET all items from an existing route. Our current test is below which mirrors very closely the example given on the Chai-HTTP documentation http://chaijs.com/plugins/chai-http/#retaining-cookies-with-each-request.
it('should return all notes on /api/notes GET', function (done) {
agent
.post('/users/register')
.send(user)
.then(function() {
return agent
.get('/api/notes')
.end(function (err, res) {
// expectations
done();
});
});
});
Our stack trace
Uncaught Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:346:11)
at ServerResponse.header (node_modules/express/lib/response.js:718:10)
at ServerResponse.send (node_modules/express/lib/response.js:163:12)
at ServerResponse.json (node_modules/express/lib/response.js:249:15)
at app/routes/usersRouter.js:16:29
at node_modules/passport/lib/middleware/authenticate.js:236:29
at node_modules/passport/lib/http/request.js:51:48
at pass (node_modules/passport/lib/authenticator.js:287:14)
at Authenticator.serializeUser (node_modules/passport/lib/authenticator.js:289:5)
at IncomingMessage.req.login.req.logIn (node_modules/passport/lib/http/request.js:50:29)
at Strategy.strategy.success (node_modules/passport/lib/middleware/authenticate.js:235:13)
at verified (node_modules/passport-local/lib/strategy.js:83:10)
at InternalFieldObject.ondone (node_modules/passport-local-mongoose/lib/passport-local-mongoose.js:149:24)
This is the function being called on our users router which seems to be raising the error (not raised manually, just raised when using chai)
router.post('/register', function(req, res) {
User.register(new User({ username : req.body.username }), req.body.password, function(err, user) {
if (err) {
res.status(500).json({info: err});
}
passport.authenticate('local')(req, res, function () {
res.status(200).json({info: "success"});
});
});
});
Manually testing this functionality works correctly, the issue seems to purely be down to our test and how it is interacting with passport.
Does anyone have any suggestions or pointers which could be of help?
Is there an err object being passed in the User.register callback? If so, try putting return res.status(500).json({info: err}); so that the passport code will not run. The return will exit the function and will not attempt to set the headers twice.
This all comes back to two different pieces of code both answering the request (res.send or res.json).
Essentially there are two possibilities:
1. Passport is using the res object to answer the request, before you do. Check the docs on how this should be done.
2. You get an error and since you have a bug with a missing return in the error handler the res object is set twice.
This is a late answer but I was just having this exact problem.
Did you try putting your passport.authenticate function inside an else statement?
I found that fixed this problem for me.
I'm working on a project that uses Loopback as a framework, and includes users and authentication. I added a password reset route generated and sent in an email, and everything seemed to be working correctly. Recently, I discovered that the password reset does not appear to be working. The process for resetting the password here is:
Call password reset method for user
Send email from reset event, including user ID and access token
From reset link, set $http.defaults.headers.common.authorization to the passed token
Call user.prototype$updateAttributes (generated by lb-ng) to update password attribute based on a form
The expected behavior is that the password would be updated on the password reset form. Instead, I get an authorization error as either a 401 or a 500 (seems to go back and forth). I notice that in the actual headers sent to the API, the authorization token does not match what I'm passing from the route. Trying to set it using LoopBackAUth.setUser doesn't work, and neither doesn't updating the authorization property before actually sending the request.
I definitely spent time testing this when it was first added, and I can't figure out what would have changed to break this. I've been following the example from loopback-faq-user-management, but we have an Angular front-end instead of the server side views in that example.
Edit:
I tried opening up the ACLs completely to see if I could update the password (or any properties) of my user object (which inherits from User, but is its own type). I'm still getting a 401 when trying to do this.
Edit #2:
Here are my ACLs and sample code for how I'm calling this.
ACLs from model definition
...
{
"accessType": "*",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW"
},
{
"accessType": "EXECUTE",
"principalType": "ROLE",
"principalId": "$owner",
"permission": "ALLOW",
"property": "updateAttributes"
}
...
auth.js
...
resetPassword: function(user) {
return MyUser.prototype$updateAttributes(user, user).$promise;
}
...
Figured out what the issue was. In our app's server, we were not using Loopback's token middleware. Adding app.use(loopback.token()); before starting the server causes the access token provided in the reset link to work as expected!
While all of the above answers will prove to be helpful, be aware that Loopback destroys a token during validation when it proved it to be invalid . The token will be gone. So when you're working your way through a solution for the 401's, make sure you're creating a new password reset token each time you try a new iteration of your code.
Otherwise you might find yourself looking at perfectly healthy code to change a password, but with a token that's already deleted in a previous iteration of your code, leading you to the false conclusion that you need to work on your code when you see another 401.
In my particular case the access tokens are stored in a SQL Server database and the token would always be immediately expired due to a timezone problem that was introduced, because I had options.useUTC set to false. That cause all newly tokens to be 7200 seconds in the past which is more than the 900 seconds than the password reset tokens are valid. I failed to notice that those tokens were immediately destroyed and concluded I had still problems with my code as I saw 401's in return. Where in fact the 401 was caused by using a token that was already gone on the server.
#OverlappingElvis put me on the right track. Here's a more complete answer for others running into this. The loopback docs are quite limited in this area.
Make sure that you get both the user id and the token in your email and these get populated in the form.
From the form the following code does the job:
function resetPassword(id, token, password) {
$http.defaults.headers.common.authorization = token;
return User
.prototype$updateAttributes({id:id}, {
password: password
})
.$promise;
}
This was way more complicated than it ought to be. Here is my full solution:
1) I expose new method on server side which does the password updating from token.
Member.updatePasswordFromToken = (accessToken, __, newPassword, cb) => {
const buildError = (code, error) => {
const err = new Error(error);
err.statusCode = 400;
err.code = code;
return err;
};
if (!accessToken) {
cb(buildError('INVALID_TOKEN', 'token is null'));
return;
}
Member.findById(accessToken.userId, function (err, user) {
if (err) {
cb(buildError('INVALID_USER', err));
return;
};
user.updateAttribute('password', newPassword, function (err, user) {
if (err) {
cb(buildError('INVALID_OPERATION', err));
return;
}
// successful,
// notify that everything is OK!
cb(null, null);
});
});
}
and I also define the accessibility:
Member.remoteMethod('updatePasswordFromToken', {
isStatic: true,
accepts: [
{
arg: 'accessToken',
type: 'object',
http: function(ctx) {
return ctx.req.accessToken;
}
},
{arg: 'access_token', type: 'string', required: true, 'http': { source: 'query' }},
{arg: 'newPassword', type: 'string', required: true},
],
http: {path: '/update-password-from-token', verb: 'post'},
returns: {type: 'boolean', arg: 'passwordChanged'}
});
From the client-side, I just call it like this:
this.memberApi.updatePasswordFromToken(token, newPassword);