nuxt auth-module "User Data response does not contain field XXX" - javascript

I'm trying to use nuxt-auth module, my settings for this module is
auth: {
cookie: false,
plugins: ['~/plugins/api.js'],
redirect: {
logout: '/login',
login: '/',
home: false
},
strategies: {
local: {
scheme: 'refresh',
token: {
property: 'token',
maxAge: 3600
},
refreshToken: {
property: 'refresh_token',
data: 'refresh_token',
maxAge: 60 * 60 * 24 * 30
},
user: {
property: 'userDetail'
},
endpoints: {
login: { url: 'http://localhost:8085/api/login_check', method: 'post', propertyName: 'token' },
refresh: { url: 'http://localhost:8085/api/token/refresh', method: 'post', propertyName: 'refresh_token' },
logout: false,
user: { url: 'http://localhost:8085/api/user/fetchactive', method: 'get' }
},
tokenRequired: true
}
}
}
My "fetchactive" API returns a JSON containing a property "userDetail" which is a string containing the email address, (I also tried to make userDetail an object but with no luck).
e.g.
{"userDetail":{"email":"my#email.test"}}
Nuxt auth keeps telling me that "User Data response does not contain field userDetail".
I also tried to set "property" to false, but Nuxt auth in that cases looks for a field named "false"...
I just can't get it to work.
Anyone can help?

using this configuration in the nuxt.config.js file worked for me
auth: {
strategies: {
local: {
user: {
property: ''
},
//other configs
}
}
//other configs
}

It's happened due to the fact that auth.user endpoint search for 'data' object by default in the response but I does not exist there, as You have Your own response object key, not contained in 'data:{...}
try to add propertyName as in example in auth.user
endpoints: {
user: { url: 'http://localhost:8085/api/user/fetchactive', method: 'get', propertyName: '' }
}
}

Related

Nuxt Auth Module - how to get a user by id/username

I'm attempting to integrate the 'Nuxt Auth Module' into my Nuxt App.
https://auth.nuxtjs.org/
I have configured my Proxy & Auth Modules and have setup the 'Local Strategy'.
https://auth.nuxtjs.org/schemes/local.html
My 'Login' endpoint works fine, and I set the 'propertyName' to 'access_token' as that is where the value for my token lives. I see 'Vuex' update my 'LoggedIn' status to true and I can also see the Token Response in the 'Network' tab of Chrome.
However I'm really struggling to understand how the 'User' endpoint works.
The example given:
auth: {
strategies: {
local: {
endpoints: {
login: { url: '/api/auth/login', method: 'post', propertyName: 'token' },
logout: { url: '/api/auth/logout', method: 'post' },
user: { url: '/api/auth/user', method: 'get', propertyName: 'user' }
},
tokenRequired: true,
tokenType: 'bearer'
}
}
}
The above is pretty much identical to mine, how does the 'User' endpoint, know which user is logged in?
I am using a third-party system for my authentication as I'm integrating an application into the third-party system. Their 'User' endpoint for REST requires an 'ID' or 'UserName' to return details about a particular user.
My 'Login' response contains 'UserName' which I could use to call the subsequent User endpoint (If I knew how).
Does anyone know how the User endpoint works? Essentially I need to call something like this:
user: {
url: '/users/${userId}',
method: 'get',
propertyName: 'data'
}
Faced to this problem, too.
My solution:
Set user property of auth endpoint to false
auth: {
strategies: {
local: {
endpoints: {
// login api
login: {
url: '/api/v1/users/login',
method: 'post',
propertyName: 'token'
},
// logout api
logout: {
url: '/api/v1/users/logout',
method: 'post'
},
user: false // setting user fetch api to false
},
redirect: {
login: '/login',
logout: '/login',
callback: '/login',
home: '/'
},
}
}
},
After loginWith() You can use function setUniversal(key, value, isJson) to save fetched user and get it with function getUniversal(key)
async login(user) {
await this.$auth.loginWith('local', {
data: user
}).then(res => {
let user = res.data.data.user // getting user (yours can be different)
this.$auth.$storage.setUniversal('user', user, true) // setting user in Vuex, cookies and localstorage
user = this.$auth.$storage.getUniversal('user') // getting user (you can use it anywhere in your app)
console.log(user) // checking user
this.$router.push('/') // redirecting after login
}).catch(err => {
console.log(err.response)
})
}
That's all, you have your user in vuex, cookies, and localstorage you can get it in computed like this:
computed: {
user() {
return this.$auth.$storage.getUniversal('user');
}
}
P.S: to logout, use logout() function, and in the callback use this.$auth.$storage.removeUniversal('user') to remove it from everywhere

Nuxt Auth Module - save JWT in Vuex store

The login/logout/middleware etc themselves work, but I don't seem to have control over the token. I'm trying to save JWT in Vuex store after logging in, but the token is only saved in a cookie and localStorage. From documentation I understand that support for auth in Vuex is added automatically. I didn't define tokenRequired and tokenType in config as according to documentation they are needed for cookie based flow (also adding them did not change anything).
nuxt.config.js
modules: [
'#nuxtjs/axios',
'#nuxtjs/auth'
],
axios: {
baseURL: 'https://api.example.com/'
},
router: {
middleware: ['auth']
},
auth: {
strategies: {
local: {
endpoints: {
login: { url: 'login', method: 'post', propertyName: 'token' },
logout: { url: 'logout', method: 'post' },
user: false
}
}
},
redirect: {
login: '/login',
logout: '/',
callback: '/login',
home: '/'
}
},
login function
await this.$axios.post('authenticate', {
email: this.email,
password: this.password
}).then(response => {
if (response.success === 'true') {
this.$auth.setUserToken(response.token)
} else {
//alert invalid login
}
}).catch(error => {
//alert server error
});
Now when I successfully log in and look at $auth.$state it returns
{ "user": {}, "loggedIn": true, "strategy": "local" }
I expect the token to also be saved in $auth.
I also looked at a question with similar title, but their solution does not work for me, as I am using user: false.
I looked at auth-module's default.js file and tried the default values in my nuxt.config.js. After adding the default into my configuration it started working. So now I was able to disable the cookies and localStorage, while saving JWT into store only.
auth: {
strategies: {
local: {
endpoints: {
login: { url: 'login', method: 'post', propertyName: 'token' },
logout: { url: 'logout', method: 'post' },
user: false
}
}
},
redirect: {
login: '/login',
logout: '/',
callback: '/login',
home: '/'
},
cookie: false,
localStorage: false,
token: {
prefix: 'token.'
},
},
And $auth.$state returns
{ "user": {}, "loggedIn": true, "strategy": "local", "token.local": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" }
If anyone has an explanation why the default value did not work and why I had to include it in my configuration, please let me know. I assume for some reason they have disabled the Vuex saving by default? Even though the documentation states what can be interpreted as by default token is saved in three locations.
Auth tokens are stored in various storage providers (cookie, localStorage, vuex)

Pact: Write different interactions with same endpoint

I have a situation where I wrote 2 interactions with same endpoint.
Even though I am passing different query in with_request option, i am getting below error -
Error: Multiple interaction found for GET /a1/configurations?includeDeleted=true&
First interaction:
withRequest: {
method: "GET",
path: `/a1/configurations`,
query: {
includeDeleted: "false",
}
}
Second interaction:
withRequest: {
method: "GET",
path: `/a1/configurations`,
query: {
includeDeleted: "true",
}
}
Can anyone help me in getting a way to fulfil this requirement ?
Thanking you !!
I suspect that both of your requests have the same name, set by uponReceiving.
The error message suggests that your code is something like:
uponReceiving: 'GET /a1/configurations?includeDeleted=true&'
withRequest: { ... }
uponReceiving: 'GET /a1/configurations?includeDeleted=true&'
withRequest: { ... }
The value for uponReceiving needs to be unique if the withRequest details are different.
For best practice, I would recommend using a human-readable string (this helps with reporting):
uponReceiving: 'a request for configurations that are not deleted',
withRequest: { method: "GET", path: /a1/configurations, query: { includeDeleted: "false", } }
and later:
uponReceiving: 'a request for all configurations',
withRequest: { method: "GET", path: /a1/configurations, query: { includeDeleted: "true", } }

ngResource does not pass parameters - how to debug?

I have the following resource definition:
myservices.factory('User', ['$resource',
function($resource){
return $resource('/user/:screenName', {}, {
whoami: {
url: '/user',
method: 'GET',
isArray: false,
params: {
op: 'whoami'
}
},
query: {
url: '/user',
method: 'GET',
isArray: true
},
get: {
method: 'GET',
isArray: false
},
update: {
method:'GET',
params: {
op: 'update'
},
isArray: false,
},
delete: {
method:'GET',
params: {
op: 'delete'
},
isArray: false,
}
});
}]);
and I was thinking it will pass screenName as the part of URL. Unfortunately, this does not happen.
The controller code is follows:
var user = new User();
user.firstname = $scope.selectedUser.firstName;
user.lastname = $scope.selectedUser.lastName;
user.screenName = $scope.selectedUser.screenName;
user.password1 = $scope.selectedUser.password1;
user.password2 = $scope.selectedUser.password2;
user.roles = $scope.selectedUser.roles;
user.maximalCalories = $scope.selectedUser.maximalCalories;
user.$update();
Actually it passes:
GET http://localhost:8080/user?op=update HTTP/1.1
Host: localhost:8080
...
Accept: application/json, text/plain, */*
...
Referer: http://localhost:8080/app/index.html
i.e. it passes neither parameters except explicit one.
UDPATE
If I do
User.$update(
{
firstname : $scope.selectedUser.firstName,
lastname : $scope.selectedUser.lastName,
screenName : $scope.selectedUser.screenName,
password1 : $scope.selectedUser.password1,
password2 : $scope.selectedUser.password2,
roles : $scope.selectedUser.roles,
maximalCalories : $scope.selectedUser.maximalCalories,
}
);
I get an exception TypeError: User.$update is not a function
UPDATE 2
Apparently, Angular adds function $update to the object and function update to the class, and if I have object at LHS, it will pass only by POST...
You are not passing screenName to resource. Should be:
user.$update({screenName: $scope.selectedUser.screenName});
The problem is you have wrong request type for you $resource's udpate object, It should have method PUT instead of GET
update: {
method: 'PUT', //<--change it 'PUT' from 'GET'
params: {
op: 'update'
},
isArray: false,
},
Also you had wrong type for delete method, it should have method: 'DELETE' instead of method: 'GET'

ExtJS: Using remotely loaded singleton values for store definition

I'm having some trouble trying to figure out how to do this (if it's even possible).
I have an app which uses parse.com to store it's data, the thing is I want each user to have a different parse.com account so their data sets don't intersect whatsoever. So I created a singleton (Settings) which stores the user's appId and apiKey, which are loaded from a general parse.com account which is managed by me and contains each user's email, appId and apiKey, so when they log into the app it gets the user's appId and apiKey.
The thing is I need to use those settings, appId and apiKey, in the definitions of my stores, as I need to send them in the headers. I've done some testing trying to set my singleton's globals when the app launchs, but at the time of the stores definition both of those "globals" are null, as the app hasn't launched yet.
Here's some of my code so I can make myself a little clearer as I know this isn't the easiest thing to understand.
Application.js
Ext.define('Settings', {
singleton: true,
appId: null,
apiKey: null
});
Ext.define('MyApp.Application', {
extend: 'Ext.app.Application',
name: 'MyApp',
stores: [],
launch: function () {
Ext.create('MyApp.store.Settings').load({
params: {
'where': '{"email": "useremail#gmail.com"}' //email is supposed to be a user input but for the sakes of testing I just made it static
},
callback: function(records){
var s = records[0];
Settings.appId = s.get('appId');
Settings.apiKey = s.get('apiKey');
Parse.initialize(Settings.appId, Settings.apiKey);
}
});
},
onAppUpdate: function () {
Ext.Msg.confirm('Application Update', 'This application has an update, reload?',
function (choice) {
if (choice === 'yes') {
window.location.reload();
}
}
);
}
});
Store
Ext.define('MyApp.store.Things', {
extend: 'Ext.data.Store',
model: 'MyApp.model.Thing',
proxy: {
type: 'rest',
api: {
read: 'https://api.parse.com/1/classes/Thing',
create: 'https://api.parse.com/1/classes/Thing'
},
reader: {
type: 'json',
rootProperty: 'results'
},
useDefaultXhrHeader: false,
withCredentials: false,
headers: {
'X-Parse-Application-Id': Settings.appId, //this is null at the time of definition, but I want it to be the newly fetched value at the time of app launch
'X-Parse-REST-API-Key': Settings.apiKey, //this is obviously null as well
'Content-Type': 'application/json'
}
},
autoLoad: true,
autoSync: true
});
What's the way around this?
By the way.. if someone can think of a proper name for this thread please feel free to change it or suggest.
Try something like:
Ext.define('Settings', {
singleton: true,
appId: null,
apiKey: null
});
Ext.define('MyApp.store.Things', {
extend: 'Ext.data.Store',
model: 'MyApp.model.Thing',
proxy: {
type: 'rest',
api: {
read: 'https://api.parse.com/1/classes/Thing',
create: 'https://api.parse.com/1/classes/Thing'
},
reader: {
type: 'json',
rootProperty: 'results'
},
useDefaultXhrHeader: false,
withCredentials: false,
},
//autoLoad: true,
autoSync: true
});
Ext.define('MyApp.Application', {
extend: 'Ext.app.Application',
name: 'MyApp',
stores: ['Things'],
launch: function() {
var settings = Ext.create('MyApp.store.Settings');
settings.on('load', function() {
var things = Ext.getStore('Things');
things.getProxy().setHeaders({
'X-Parse-Application-Id': Settings.appId,
'X-Parse-REST-API-Key': Settings.apiKey,
'Content-Type': 'application/json'
});
things.load();
});
settings.load({
params: {
'where': '{"email": "useremail#gmail.com"}' //email is supposed to be a user input but for the sakes of testing I just made it static
},
callback: function(records) {
var s = records[0];
Settings.appId = s.get('appId');
Settings.apiKey = s.get('apiKey');
Parse.initialize(Settings.appId, Settings.apiKey);
}
});
},
onAppUpdate: function() {
Ext.Msg.confirm('Application Update', 'This application has an update, reload?',
function(choice) {
if (choice === 'yes') {
window.location.reload();
}
}
);
}
});
I would suggest using routes to accomplish this, since you are using ExtJs 6. It is completely out of the box, but I thing it would be ideal for your situation. In this way you can simply be sure that when a route is called and a part of your application is loaded, you always can do some checks. This can be very useful for checking user credentials for example. More information about routes can be found here. And this is a great post when you want to handling user sessions through routes.
The singleton:
Ext.define('Settings', {
singleton: true,
appId: null,
apiKey: null
});
The Base store:
Ext.define('Base', {
extend: 'Ext.data.Store',
alias: 'store.base',
storeId: 'base',
autoLoad: false,
proxy: {
type: 'rest',
useDefaultXhrHeader: false,
withCredentials: false
},
listeners: {
beforeload: function(store, operation, eOpts) {
store.getProxy().headers = {
'X-Parse-Application-Id': Settings.appId,
'X-Parse-REST-API-Key': Settings.apiKey,
'Content-Type': 'application/json'
}
}
}
});
The Things store:
Ext.define('MyApp.store.Things', {
extend: 'MyApp.store.Base',
alias: 'store.things',
model: 'MyApp.model.Thing',
storeId: 'things',
requires: [
'Settings'
],
proxy: {
api: {
read: 'https://api.parse.com/1/classes/Thing',
create: 'https://api.parse.com/1/classes/Thing'
},
reader: {
type: 'json',
rootProperty: 'results'
}
},
autoLoad: false, // --> set to false
autoSync: true
});
Your MainController:
Ext.define('MyApp.view.main.MainController', {
extend : 'Ext.app.ViewController',
requires: [
'Settings'
],
stores: [
'Things'
],
routes : {
'user/:id' : {
before : 'onBeforeUser',
action : 'onUser'
}
},
onBeforeUser : function(id, action) {
Ext.create('MyApp.store.Settings').load({
params: {
'where': '{"email": "useremail#gmail.com"}' //email is supposed to be a user input but for the sakes of testing I just made it static
},
callback: function(records){
var s = records[0];
Settings.appId = s.get('appId');
Settings.apiKey = s.get('apiKey');
Parse.initialize(Settings.appId, Settings.apiKey);
action.resume();
}
});
// or even better
Ext.Ajax.request({
url: 'url/to/the/api',
params: {
'where': '{"email": "useremail#gmail.com"}' //email is supposed to be a user input but for the sakes of testing I just made it static
},
success: function(response, opts) {
var obj = Ext.decode(response.responseText);
Settings.appId = obj.appId;
Settings.apiKey = obj.apiKey;
Parse.initialize(Settings.appId, Settings.apiKey);
action.resume();
},
failure: function(response, opts) {
action.stop(true);
}
});
},
onUser : function(id) {
Ext.getStore('things').load();
}
});
I think the issue can be solved by moving proxy definition to constructor of 'Things' store as given below.
Ext.define('MyApp.store.Things', {
extend: 'Ext.data.Store',
model: 'MyApp.model.Thing',
autoLoad: true,
autoSync: true,
constructor: function(config) {
config = Ext.apply({
proxy: {
type: 'rest',
api: {
read: 'https://api.parse.com/1/classes/Thing',
create: 'https://api.parse.com/1/classes/Thing'
},
reader: {
type: 'json',
rootProperty: 'results'
},
useDefaultXhrHeader: false,
withCredentials: false,
headers: {
'X-Parse-Application-Id': Settings.appId,
'X-Parse-REST-API-Key': Settings.appId,
'Content-Type': 'application/json'
}
}
}, config);
this.callParent([config]);
}
});
When proxy definition is inside the constructor, Settings.appId and Settings.apiKey are resolved only at the time of instance creation of 'MyApp.store.Things'.

Categories