How can I compose handler off Hapijs? - hapi.js

I need to do some additional authentication in a few of my handlers. Is there a way of doing that way in a composable way?
export async function handler(request) {
const user = request.auth.credentials;
const customer = FindCustomer(user);
if (!customer) {
throw Boom.forbidden('user is not a customer');
}
if (!customer.verified) {
throw Boom.forbidden('customer not validated');
}
// actual handler logic
}
Is there a way to wrap this so that some routes already provide the customer in the request object ?

You can make use of the extension points in the request life cycle. In your case, the 'onPostAuth' extension point would be ideal.
server.ext('onPostAuth', function (request, reply){
const user = request.auth.credentials;
const customer = FindCustomer(user);
if (!customer) {
return reply (Boom.forbidden('user is not a customer'));
}
if (!customer.verified) {
return reply(Boom.forbidden('customer not validated'));
}
reply.continue();
});

Complementing ZeMoon's answer, you can implement the onPostAuth like this:
server.ext('onPostAuth', function (request, reply) {
if(request.route.settings.plugins.verifyCustomer) {
const user = request.auth.credentials;
const customer = FindCustomer(user);
if (!customer) {
return reply (Boom.forbidden('user is not a customer'));
}
if (!customer.verified) {
return reply(Boom.forbidden('customer not validated'));
}
}
reply.continue();
});
And then add a configuration plugins.verifyCustomer to the route:
server.route({
method: 'get',
path: '/test1',
handler: function(request, reply) {
// Handler logic here
},
config: {
plugins: {
verifyCustomer: true
}
}
});

i think a more robust way would be to assign scope to the credentials when the customer is authenticated, and to require the scope in the routes you want it.

Related

Vuejs a function is triggering an infinite loop

I've created a function to get a user's IP address and save to a database, but instead of saving to the database, my home page is quickly and infinitely refreshing. I've tried removing watchers that may be causing this, but this did not solve the problem.
All the relevant parts that are triggering this loop:
1 - When home is mounted:
mounted() {
document.documentElement.scrollTop = 0;
IpService.getIpAddress().then((data) => {
const ip = localStorage.getItem("ip");
this.ip = data.data.ip;
if (!ip) {
VisitorsService.add(this.ip).then(()=> {
localStorage.setItem("ip",this.ip);
})
}
});
},
2 - ip service to get user's ip:
export default class ipService {
static getIpAddress() {
return axios.get("https://www.cloudflare.com/cdn-cgi/trace", {
responseType: "text",
transformResponse: data =>
Object.fromEntries(data.trim().split("\n").map(line => line.split("=")))
})
}
}
3 - This is the part where is happening the issue, if I remove this the problem stops, but it's necessary that this method works
static add(ip) {
return axios({
url: APIConstants.baseURL + "visitors/add",
method: "POST",
data: {
ip: ip
}
});
}
Conclusion: I have no idea why adding a simple axios function is causing an infinite loop on my home page. If I remove that function the issue stop. I would like to know if anyone has an idea on how can I fix this terrible loop?
Trying first stackover solution:
A user suggested to use everything async it might solve the issue:
1 - mounted at home
async mounted() {
document.documentElement.scrollTop = 0;
let data = await IpService.getIpAddress();
this.ip = data.data.ip;
//The issue happens only after this add()
await VisitorsService.add(this.ip);
},
2 - ipService class
const axios = require('axios');
export default class ipService {
static async getIpAddress() {
return await axios.get("https://www.cloudflare.com/cdn-cgi/trace", {
responseType: "text",
transformResponse: data =>
Object.fromEntries(data.trim().split("\n").map(line => line.split("=")))
})
}
}
3 - Visitor service where the is the issue:
static async add(ip) {
return axios({
url: APIConstants.baseURL + "visitors/add",
method: "POST",
data: {
ip: ip
}
});
}
New conclusion changing everything to async still keep the loop problem.
It's a re-rendering issue ,the user who posted this topic had a similar issue
Infinite loop on axios call, React
You should probably always use an async function and await request calls
export default class ipService = async () => {
static getIpAddress() {
return await axios.get("https://www.cloudflare.com/cdn-cgi/trace", {
responseType: "text",
transformResponse: data =>
Object.fromEntries(data.trim().split("\n").map(line => line.split("=")))
})
}
}

How to use Nuxt Context to call Axios request with param

so I'm trying to get my Axios to do a get request with a param that'll end the url in
'/?user= {id}'
the id is passed in by my loggedInUser.id from Vuex. I know that async functions won't accept 'this' inside the call so I included store as a parameter. Something's still off with how I passed the data around thought I think. Would appreciate any help, thanks!
import { mapGetters } from "vuex";
export default {
computed: {
...mapGetters(["loggedInUser"])
},
head() {
return {
title: "Actors list"
};
},
components: {
EditProfile
},
async asyncData({ store }) {
try {
const body = { data: store.getters.loggedInUser.id };
const { actors } = await $axios.$get(`/api/v1/actors/`, {
params: {
user: body
}
});
return { actors };
} catch (e) {
return { actors: [] };
}
},
data() {
return {
actors: []
};
Edit
I got it to work when I removed the data: from 'const body' and removed the brackets as well around 'actor'
try {
const body = store.getters.loggedInUser.id;
const actors = await $axios.$get(`/api/v1/actors/`, {
params: {
user: body
}
});
You can access your params from Context.
Context is available in special nuxt lifecycle areas like asyncData, fetch, plugins, middleware and nuxtServerInit.
In Nuxt, with asyncData hook you can get query parameters from the route context key.
Please read the Nuxt.js Context documentation. The context provides additional objects/params from Nuxt to Vue components
With your-domain/?user=wonderman
asyncData({ route: { query: queryParams} }) {},
variable queryParams is an object:
{ user: "wonderman" }

vue-authenticate: Where is the $auth variable defined?

I am trying to implement vue-authenticate, but I'm having an issue with the following example code taken from their documentation:
new Vue({
methods: {
login: function () {
this.$auth.login({ email, password }).then(function () {
// Execute application logic after successful login
})
},
register: function () {
this.$auth.register({ name, email, password }).then(function () {
// Execute application logic after successful registration
})
}
}
})
Where is the $auth property coming from? I can't see it defined anywhere. I've looked through the documentation, and the example code in the repo, but neither provide any help.
As you know vue-authenticate is a Vue-plugin.
And when you use this plugin using the line.
Vue.use(VueAuthenticate, ...data)
And this is where it gets defined in this file
Object.defineProperties(Vue.prototype, {
$auth: {
get() {
if (!vueAuthInstance) {
// Request handler library not found, throw error
if (!this.$http) {
throw new Error('Request handler instance not found')
}
vueAuthInstance = new VueAuthenticate(this.$http, options)
}
return vueAuthInstance
}
}
})
Also you may want to go through this documentation on Adding Instance Properties.

Get item from AsyncStorage in React Native

I have a list of companies in React Native.
When I click on one of those companies I get the url of the API that is used for selected company. Then I store it to AsyncStorage and then I show the login screen. The function is as follows:
selectCompany(data_url, e) {
AsyncStorage.setItem("data_url", JSON.stringify(data_url), () => this.props.login());
}
Then on login page if I click on sign in button I go to the onLogin function, the function is as follows:
onLogin: function() {
fetch(data.url + '/manager/api/v1/obtain-auth-token/', })
.then(function(body) {
return body.json();
}).then(function(json) {
.....
}).catch(function() {
....
});
},
And data.url comes from data.js file, and I try to get url from the data.js file as follows:
let data_url = AsyncStorage.getItem("data_url").then(json => JSON.parse(json));
module.exports = {
url: data_url,
.....
}
But it doesn't work. Any advice?
AsyncStorage is async, therefore data_url will not be defined until it's retrieved what its looking for, you would need to move the fetch into the promise thats returned from the get so it will run it once it's done getting the data. This might be one way you tackle it:
const data_url = () => AsyncStorage.getItem("data_url"); //change this into a function
module.exports = {
url: data_url,
.....
}
now inside your component...
onLogin: function() {
data.url().then((url) => {
fetch(JSON.parse(url) + '/manager/api/v1/obtain-auth-token/', })
.then(function(body) {
return body.json();
}).then(function(json) {
.....
}).catch(function() {
....
});
});
},
AsyncStorage.getItem is a promise and needs to await for response rather than accessing direct and the function calling it should be defined as async. Here is an example to retrieve from AsyncStorage..
export async function getAccessKey(){
let accessToken = await AsyncStorage.getItem(ACCESS_TOKEN);
return accessToken;
}

VueRouter - Fetching Before Navigation - Multiple AJAX

I would like to use the beforeRouteEnter guard so I can be sure my data is loaded before going to a page. I read the example you can find here in the vue-router documentation.
Current situation
I'm more or executing two AJAX calls to get some data in the created lifecycle event.
export default {
created() {
const _this = this;
axios.get('/getCompanyDetails').then((response) => {
_this.private.company_details = response.data
});
axios.get('/getusers').then((response) => {
if(response.data){
_this.private.company_users = response.data;
}
});
}
}
What I try
beforeRouteEnter (to, from, next) {
function getCompanyDetails() {
return axios.get('/getCompanyDetails')
}
function getUsers() {
return axios.get('/getusers');
}
axios.all([getCompanyDetails(), getUsers()])
.then(axios.spread(function (company_details, company_users) {
next(vm => vm.setData(err, company_details, company_users))
}));
},
Am I on the right track ? The only thing I see here is I fell I'm required to call only one function setData in the next with all the parameters received from the different AJAX calls.
Is there a way to call several functions like setUsers(), setDetails() in the next ?
Is there a better way than what I'm doing ?
As #thanksd stated :
next((vm) => { vm.setUser(err, company_users); vm.setDetails(err, company_details); })
The final answer is then
beforeRouteEnter (to, from, next) {
function getCompanyDetails() {
return axios.get('/getCompanyDetails')
}
function getUsers() {
return axios.get('/getusers');
}
axios.all([getCompanyDetails(), getUsers()])
.then(axios.spread(function (company_details, company_users) {
next((vm) => { vm.setUser(err, company_users); vm.setDetails(err, company_details); })
}));
},