I have a problem with Patch in FeathersJS.
I want to update the data with axios.patch
but the message that appears is always No auth token
{"name":"NotAuthenticated","message":"No auth token","code":401,"className":"not-authenticated","data":{},"errors":{}}
This my axios :
Aktifasi() {
axios.patch(process.env.ROOT_API+'/ek_user?id_user=2',
qs.stringify({
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token'),
'Content-Type': 'application/json',
},
active_user: 1
}))
.then(request => this.AktifasiSuccesSend(request))
.catch((error) => this.AktifasiFailedSend(error))
},
AktifasiSuccesSend (request) {
console.log('Yay');
},
AktifasiFailedSend (error) {
console.log('Oh Fail');
}
And this Hook on FeathersJS :
before: {
all: [],
find: [ authenticate('jwt') ],
get: [ authenticate('jwt') ],
create: [ hashPassword() ],
update: [ hashPassword(), authenticate('jwt') ],
patch: [ hashPassword(), authenticate('jwt') ],
remove: [ authenticate('jwt') ]
},
As the Axios configuration documentation shows, headers are passed as a separate option not as a stringified query string (which shouldn't be necessary at all):
const data = {
active_user: 1
};
const config = {
headers: {
'Authorization': 'Bearer ' + localStorage.getItem('token'),
'Content-Type': 'application/json',
}
};
axios.patch(process.env.ROOT_API + '/ek_user?id_user=2', data, config);
I recommend becoming very good at using a proper Node debugger. Visual Studio Code has a great debugger. I even wrote an article about it on the Feathers blog: https://blog.feathersjs.com/debugging-feathers-with-visual-studio-code-406e6adf2882
I will give you some pointers to get you on your way, but you will be required to answer your own question by using a debugger.
The "No auth token" message that you are getting is coming from the authenticate('jwt') hook. Here are some typical steps you'd use to solve your own problem:
If you open that hook in your node_modules folder and put break points in it before the message, you'll be able to see where it's looking for a jwt token.
If you create a hook before all other hooks in the patch hooks, you'll be able to put a break point in it and inspect the hook context object to see if the request contains the jwt, or not (in the same location that the authenticate hook expects it.
If the jwt token is not in the place where the authenticate hook expects to find it, you may be missing a middleware function registration in your authentication.js setup file. You would check the feathers docs to make sure you've properly registered the authentication plugins.
Related
I'm trying to get a oauth token from the paypal sandbox using axios. The request looks like this:
const response = await axios.post(
'https://api-m.sandbox.paypal.com/v1/oauth2/token',
new URLSearchParams({
'grant_type': 'client_credentials'
}),
{
auth: {
username: process.env.PP_CLIENT,
password: process.env.PP_SECRET
},
headers: {
'Accept': 'application/x-www-form-urlencoded',
'Accept-Language': 'en_US'
}
}
);
console.log(`response: ${JSON.stringify(response.data)}`);
As far as I can tell this code used to work, because I used it before. However now I'm getting the error:
"Converting circular structure to JSON
--> starting at object with constructor 'ClientRequest'
| property 'socket' -> object with constructor 'TLSSocket'
--- property '_httpMessage' closes the circle"
I have confirmed that my credentials are correct by testing it with curl, which works and by copying it directly into the code. I've also tries fetch (which always yields an empty response)
I want to integrate Quasar with FeathersJS using Feathers-Vuex
Feathers-Vuex uses a pattern to:
promise to authenticate from localStorage/cookies
.then( /*start the new Vue() app */ )
I created my app with Quasar CLI 1.0.beta16-ish and looked through /src and couldn't find the main entry point for Quasar. I feel like I'm missing something.
What includes src/store/index.js?
quasar.conf.js includes this comment - where is the main.js
// app boot file (/src/boot)
// --> boot files are part of "main.js"
boot: ["axios"],
Feathers-Vuex includes a Nuxt integration guide that may solve the same problem. These packages are all new to me, and I'm excited to learn them!
Thank you!
The part of main.js is included in quasar app.js that you can find in .quasar folder. The src/store/index.js contains the Vuex Store definition. A "store" is basically a container that holds your application state.
For more detail visit - https://quasar-framework.org/guide/app-vuex-store.html https://quasar-framework.org/guide/app-plugins.html
I ended up with two things:
Adding Feathers-Vuex to my backend.
Adding this "boot file" in my Quasar project
The comments are a bread-crumb trail if I ever have to figure it out again :-)
/*
Context:
For 3rd-party API's, we us /src/boot/axios.js
For our own API's, we use FeathersClient (socket.io & REST)
https://docs.feathersjs.com/guides/basics/clients.html
https://docs.feathersjs.com/api/authentication/client.html#appconfigureauthoptions
Our FeathersClient is in `/src/lib/feathersClient.js`
and imported into `/src/store/index.js`
which is imported by Quasar's build system. /src/quasar.conf.js setting(?)
Feathers-vuex integrates Vuex with FeathersClient:
https://feathers-vuex.feathers-plus.com/auth-module.html
Feathers-Vuex proxies it's authentication/logout actions to FeathersClient
https://github.com/feathers-plus/feathers-vuex/blob/master/src/auth-module/actions.js
The parameters for these actions are here:
https://docs.feathersjs.com/api/authentication/client.html#appauthenticateoptions
In addition to this module, you can use FeathersVuex state in UI from here:
https://feathers-vuex.feathers-plus.com/auth-module.html
This module:
Create a Feathers Auth integration for Vue as a Quasar Boot Module.
// Use case: test if user is authenticated
if (Vue.$auth.currentUser()) { ... }
// Use case: get current user's email
name = Vue.$auth.currentUser("email") || "anonymous"
// Use case: Login
Vue.$auth.login({
strategy: 'local',
email: 'my#email.com',
password: 'my-password'
});
// Use case: Logout
// logs out and sends message
let p = Vue.$auth.logout();
// After logout, go home
p.then(() => {
// User data still in browser
router.push({ name: "home"});
// To clear user data, do a hard refresh/redirect - https://feathers-vuex.feathers-plus.com/common-patterns.html#clearing-data-upon-user-logout
location && location.reload(true)
});
*/
export default ({ app, router, store, Vue }) => {
// Create the API demonstrated above
const auth = {
currentUser(prop) {
let u = store.state.auth.user || false;
if (u && prop) return u[prop];
return u;
},
login(authData, quiet) {
return store
.dispatch("auth/authenticate", authData)
.then(() => {
Vue.prototype.$q.notify({
message: "Welcome back!",
type: "info"
});
})
.catch(err => {
if (!quiet) {
console.log(err);
Vue.prototype.$q.notify({
message: "There was a problem logging you in.",
type: "error"
});
}
});
},
logout(quiet) {
return store.dispatch("auth/logout").then(() => {
if (!quiet)
Vue.prototype.$q.notify({
message: "You've been logged out.",
type: "info"
});
});
},
register(authData) {}
};
// Auth from JWT stored in browser before loading the app. true => suppress token not found error
auth.login("jwt", true);
// Add API to Vue
Vue.prototype.$auth = auth;
// If you would like to play with it in the console, uncomment this line:
// console.log(auth);
// Then, in the console:
/*
temp1.login({
strategy: "local",
email: "feathers#example.com",
password: "secret"
})
*/
// If you haven't created this user, see here:
// https://docs.feathersjs.com/guides/chat/authentication.html
// For this REST api endpoint
/*
curl 'http://localhost:3001/users/' -H 'Content-Type: application/json' --data-binary '{ "email": "feathers#example.com", "password": "secret" }'
*/
};
I use MongoDB as the back-end database for my node/Express application. To summarize the problem I am facing, I don't know how to set up the body-parser configuration in my Express app, because the server side application is not receiving the full JSON posted by the client application (also a node.js app). For the most part, the client is sending JSON in the request body to RESTful endpoints. The exception being a single case where a file needs to be uploaded and since that is a multipart body, I am using request and form-data to build that type of request and using multer on the server side to process the multipart request, since body-parser does not process such requests.
On the server-side (Express), I have the following configuration of the Express app:
let app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
On the node client, I am using the following code to build up a JSON-style JavaScript object and post it to a RESTful endpoint:
I am having difficulty composing the request on the client side, with node-request:
// class is a JavaScript/JSON object within scope of this code
let req = request.post({
//headers: { 'Content-Type': 'application/json' },
url: 'http://localhost:3000/classes',
//form: class
form: JSON.stringify(class)
}, function (err, resp, body) {
if (err) {
throw err;
}
});
Note that I attempted multiple versions of the above code by explicitly specifying the content type as application/JSON, as well as using JSON.stringify to convert the JavaScript object into a JSON string. The MongoDB collection (class) stores the following type of document, which contains foreign keys to two other collections (subject and student):
{
"_id" : ObjectId("57758f15f68da08c254ebee1"),
"name" : "Grade 5 - Section A",
"scores" : [{
"studentId" : ObjectId("5776bd36ffc8227405d364d2"),
"performance": [{
"subjectId" : ObjectId("577694ecbf6f3a781759c54a"),
"score" : 86,
"maximum" : 100,
"grade" : "B+"
}]
}]
}
In the server logs, I see the following error:
Tue, 05 Jul 2016 04:34:46 GMT classReportApp:routes:classes class received from client: { _id: 577b38e65967097c25876764, scores: [] }
RangeError: Invalid status code: 0
at ServerResponse.writeHead (_http_server.js:192:11)
at ServerResponse.writeHead (C:\Development\classReportApp\node_modules\morgan\node_modules\on-headers\index.js:55:19)
at ServerResponse._implicitHeader (_http_server.js:157:8)
at ServerResponse.OutgoingMessage.end (_http_outgoing.js:566:10)
at ServerResponse.send (C:\Development\classReportApp\node_modules\express\lib\response.js:205:10)
at ServerResponse.json (C:\Development\classReportApp\node_modules\express\lib\response.js:250:15)
at C:\Development\classReportApp\server-process\app.js:80:26
at Layer.handle_error (C:\Development\classReportApp\node_modules\express\lib\router\layer.js:71:5)
at trim_prefix (C:\Development\classReportApp\node_modules\express\lib\router\index.js:310:13)
at C:\Development\classReportApp\node_modules\express\lib\router\index.js:280:7
at Function.process_params (C:\Development\classReportApp\node_modules\express\lib\router\index.js:330:12)
which is strange because scores array of sub-documents is empty (scores: []), whereas on the client side, I am sending a non-empty array with some students' performances elements in it.
Have I violated the right way to post JSON to Express apps? How do I fix this?
Edited: 7/5/2016
I changed the body parser middle-ware configuration to use extended: true instead.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
The node.js is still using the node-request module to compose and send the POST request, using the following code:
let req = request({
url: 'http://localhost:3000/classes',
method: 'POST',
json: class
}, function (err, resp, body) {
if (err) {
throw err;
}
else {
// process response
}
});
This works now, but what confuses me is that since the content type is application/json, how does the bodyParser.urlencoded({ extended: true }) (or false) matter?
Issue is with form: JSON.stringify(class) in your first request. form takes url encoded form input, stringified json won't work. check content-type header (application/x-www-form-urlencoded)
json: class in your 2nd snippet works as this handles json data and sets correct content type header correctly.
Try .toJSON() method over class before sending.
I follow the tutorial here https://developer.chrome.com/apps/app_identity and use the api here https://developer.chrome.com/apps/identity but with no luck. Could anyone point out is anything wrong in this code?
function onGoogleLibraryLoaded() {
var redirect_uri = chrome.identity.getRedirectURL("http://qqibrow.github.io");
var full_url = "https://stackexchange.com/oauth/dialog?client_id=4716&redirect_uri=" + redirect_uri;
console.log(redirect_uri);
console.log(full_url);
chrome.identity.launchWebAuthFlow({
'url': full_url,
'interactive': true
}, authorizationCallback);
}
var authorizationCallback = function (data) {
// should print out redirect_uri with auth_token if succeed.
console.log(data);
};
// manifest.json
// ...
"permissions": [
"activeTab",
"identity",
"https://ajax.googleapis.com/",
"https://stackexchange.com/*",
"https://stackexchange.com/oauth/*",
"http://qqibrow.github.io/*"
],
"web_accessible_resources": [
"http://qqibrow.github.io/*",
"https://stackexchange.com/*",
],
// ...
If i try https://stackexchange.com/oauth/dialog?client_id=4716&redirect_uri=http://qqibrow.github.io it does work. But with above code, I always got a error page from stackexchange, saying that:
Application Login Failure error description: Cannot return to provided redirect_uri.
This is a creative usage of chrome.identity.getRedirectURL().
It does not allow you to redirect to an arbitrary domain; you can provide a path, but the domain for chrome.identity will be https://<app-id>.chromiumapp.org.
So, your call returns https://<app-id>.chromiumapp.org/http://qqibrow.github.io which is not a valid URL, and your auth fails.
I recommend re-reading the launchWebAuthFlow documentation.
I am trying to register a user in my Hybrid Worklight application. For this I am using an external REST API. The API says:
Request Method: POST
Request URL: https://${domainservice}/service/plan/${planName}/user?key=${key}&tenantId=${tenantId}
Request Headers: Content-Type:application/json
Request Payload:
{
"uid": "patricia",
"firstName": "Patricia",
"lastName": "Mayo",
"pic": "BASE64_ENCODED_IMAGE"
}
Field description:
uid (required): user's uid
firstName (optional): user's first name
lastName (optional): user's last name
pic (optional): user's picture encoded as base64 string
So I created a HTTP Worklight adapter:
function RegisterUser(userid) {
var input = {
method : 'post',
path : '/service/plan/App/user',
returnedContentType : 'plain',
headers: {'Content-Type' : 'application/json'},
parameters: {
'key':'e634bc60-0c6eba577258',
'tenantId': 'd93b921d-a56c-a645924fd548'
},
body : {
'contentType' : 'application/json',
'content' : JSON.stringify({
"uid" : userid})
}
};
return WL.Server.invokeHttp(input);
}
And I get this error:
{ "errors": [
],
"info": [
],
"isSuccessful": true,
"responseHeaders": {
"$wsep": "",
"Connection": "Keep-Alive",
"Content-Language": "en-US",
"Content-Type": "text\/html;charset=ISO-8859-1",
"Date": "Wed, 30 Jul 2014 14:47:27 GMT",
"Transfer-Encoding": "chunked",
"X-Backside-Transport": "FAIL FAIL",
"X-Client-IP": "199.127.32.67",
"X-Global-Transaction-ID": "48515650",
"X-Powered-By": "Servlet\/3.0"
},
"responseTime": 357,
"statusCode": 500,
"statusReason": "Internal Server Error",
I think is very weird that I set up
headers: {'Content-Type' : 'application/json'}
but in the response it looks like
"Content-Type": "text/html;charset=ISO-8859-1"
Also I want to point out some things I have already tried:
returnedContentType : 'plain' --> It is set to plain because if I set it to json I would get a JSON parse error
body content I also tried
var payload = "{\'uid\': \'"+userid+"\'}";
payload = payload.toString();
and then 'content' : payload
Using the RESTClient of the browser everything works fine
I tried using http port 80, http port 2080, and also https 443
I also tried writting the Host in the headers
I would not be too concerned about the format of the payload in the case where the server has hit an error condition - ideally servers would give us nicely formed error response but that's not always possible. Typically, if the expected response type in case of success is JSON then that's what I specify, and I just have to have enough error handling for cases when JSON parsing fails.
As to why you're getting the 500 error ... best I can suggest is to use a network monitor of some sort to discern the difference between the request issued via Worklight and the request as issued by the Browser's REST client.
You can set this up in Eclipse, Preferences->Run->TCP/IP Monitor ...
You may want to add a Host header. I've seen numerous times where it solved similar issues. E.g. if you're trying to connect to http://www.server.com/a/b/c/d add below header to your headers property
headers: {
Host: 'www.server.com'
}
I finally found the error. There is a defect in Worklight, the query parameters are removed and put into request body so the REST SERVICE does not receive key & tenantId. This is the final working code solution. Thank you anyways for your quick answers :)
function RegisterUser(userid) {
var payload = '{"uid": \"'+userid+'\"}';
var input = {
method : 'post',
path : '/service/plan/App/user?key=e630-db87-4803-bc45-57725c&tenantId=d9348af-a56c-a645924fd54e',
returnedContentType : 'application/json',
headers: {'Content-Type' : 'application/json'},
body : {
'contentType' : 'application/json',
'content' : payload.toString()
}
};
return WL.Server.invokeHttp(input);
}