Authentication in Angular 2, handling the observables - angularjs

I just started with a Angular 2 project and am trying to get authentication up and running. Inspired by this tutorial I decided to do the following:
Create a custom RouterOutlet class (extending it) to handle the authentication logic whenever a url is called.
I succeeded in this custom class, but am still not sure how to check if a user is authenticated. My situation is as follows, I need to query a get call to a external API, for my development proces it is as follows:
getAdmin() {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get('http://localhost:3000/admin/is_admin.json', options)
.map(res => res)
.catch(this.handleError)
}
This API call returns true or false. I was wondering what would be the best option to use this information? Should I for example call the following function each time a URL should be checked?:
isAdmin() {
this.getAdmin().subscribe(
data => this.authenticationResult = data,
error => console.log("Error: ", error),
() => return JSON.parse(this.authenticationResult._data);
}
I can't get this up and running because my observable is undefined when using the function I gave as example.

The "problem" is that your method is asynchronous so you need to be careful the way and when you use it.
If you want to use within the activate method of your custom RouterOutlet, you need to leverage observables and reactive programming.
I don't know exactly the way you want to check admin roles:
activate(instruction: ComponentInstruction) {
return this.userService.getAdmin().flatMap((isAdmin) => {
if (this.userService.isLoggIn()) {
if (this._canActivate(instruction.urlPath, isAdmin) {
return Observable.fromPromise(super.activate(instruction));
} else {
this.router.navigate(['Forbidden']);
return Observable.throw('Forbidden');
}
} else {
this.router.navigate(['Login']);
return Observable.throw('Not authenticated');
}
}).toPromise();
}
_canActivate(url, admin) {
return this.publicRoutes.indexOf(url) !== -1
|| this.userService.isLoggedIn();
}
In order to optimize the request, you could lazily (and only once) call the request to check if the user is admin or not:
isAdmin:boolean;
getAdmin() {
if (this.isAdmin) {
return Observable.of(this.isAdmin);
} else {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
return this.http.get('http://localhost:3000/admin/is_admin.json', options)
.map(res => res)
.catch(this.handleError);
}
}
Another approach will be also to load this hint when authenticating the user. This way, the implementation of the activate method would be simplier:
activate(instruction: ComponentInstruction) {
if (this.userService.isLoggIn()) {
if (this.userService.isAdmin()) {
return super.activate(instruction);
} else if (this._canActivate(instruction.urlPath, isAdmin) {
return super.activate(instruction);
} else {
this.router.navigate(['Forbidden']);
}
} else {
this.router.navigate(['Login']);
}
}
_canActivate(url, admin) {
return this.publicRoutes.indexOf(url) !== -1
|| this.userService.isLoggedIn();
}

I would consider to call getAdmin() somehow as first Step of your app, store the result in a SessionService object which you move around using Dependency Injection. This way any time you need to check the result of getAdmin you can ask the SessionService instance.
I hope this helps

Related

sveltekit TypeError: immutable

I'm using sveltekit 1.0.0-next.483 running with npm run dev -- --host
connecting to an endpoint with a mobile device i get this error:
typeError: immutable
at Headers.append ([..]node_modules/undici/lib/fetch/headers.js:227:13)
This error only occurs on mobile device, connecting to the local net ip address.
my endpoint: src/routes/gqlendpoint/+server.ts
const base = 'http://localhost:4000/graphql';
export async function POST( opts: { request: Request} ): Promise<Response> {
const { request } = opts;
const body = await request.json();
const response = await fetch(base, {
//credentials:"include",
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
});
return response;
}
the only way I found to unlock this situation, is by commenting a line of code inside node_modules/undici/lib/fetch/headers.js
// 3. If headers’s guard is "immutable", then throw a TypeError.
// 4. Otherwise, if headers’s guard is "request" and name is a
// forbidden header name, return.
// Note: undici does not implement forbidden header names
if (this[kGuard] === 'immutable') {
**//throw new TypeError('immutable')**
} else if (this[kGuard] === 'request-no-cors') {
// 5. Otherwise, if headers’s guard is "request-no-cors":
// TODO
}
which is certainly not a good solution.
You have to return new Response body to avoid this issue , see example code below.
return new Response('test example')
in place of return response;

Unable to perform POST operation in React with fetch

I am attempting to perform POST request using ReactJS and update in a Mongo-database using Spring-boot. During the compilation of the code I did not get any errors, yet when trying to perform a POST operation I do not get an output. When I refresh the page I get an entry in the database with an ID but with no name.
This is the code for the react application:
async handleSubmit(event){
event.preventDefault();
const {item} = this.state;
await fetch(`/person/new`, {
method : 'POST',
headers : {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body : JSON.stringify({item}),
});
this.props.history.push("/change");
}
The code for POST in Spring-boot is :
public Integer Counter(){
List<Person> p = pr.findAll();
c = 1;
while(pr.existsById(c)) {
c++;
}
return c;
}
#PostMapping("/new")
public void insertPerson(#RequestBody Person person){
Integer id = Counter();
String name = person.getName();
pr.save(new Person(id,name));
c++;
}
This is my site looks when the code is executed ↓
How my site looks
Please help me with this.
PS: DELETE operation is working though.
I think it's a matter of how you call this function. I'd suggest invoking it like so: onClick = { async (e) => await { this.handleSubmit(e); } }

async inside a for loop nodejs

I am trying to access an api and I will have to run the api calls several times based on the page numbers I need to iterate, the following is the code which I am using and how can I get the all the response pushed into an array.
as nodeJs is single threaded It is not waiting for the responses from the api.
How can I can tackle this and ensure all the response values are being pushed into an array
Inside the for loop I want the final array which has all the values of the api response. So, I check the total page value and response page Number if that matches which means that will be the last page and I push the array to another function but when I do that it does not have all the values because nodejs does not wait for the api response.
const fs = require('fs');
var pepKey = 'asdfasdfasd';
var pepResponse;
var pepTimecards = [];
pep();
function pep(){
var options = {
headers: {
"content-type": "application/json",
},
agentOptions: {
pfx: fs.readFileSync('./certificate/asdfsdaf.p12'),
passphrase: 'asdasdsda'
}
};
request.get('https://source.asdfasdf.io/api/organisations/asdfasdf/timecard_keys?timecard_type=Flex',options, (err, res, body) => {
if (err) { return console.log(err); }
pepResponse = JSON.parse(body)
pepTimecards = pepResponse.data;
if(pepResponse.pages > 1){
for(let i=2;i<=pepResponse.pages;i++){
var url = 'https://source.`pepme`.io/api/organisations/sdfsadf/timecard_keys?timecard_type=Flex&page='+pageNo;
request.get(url,options, (err, res, body) => {
if (err) { return console.log(err); }
body = JSON.parse(body)
pepTimecards = pepTimecards.concat(body.data)
if(pepResponse.pages == body.page){
console.log(pepResponse.pages)
console.log(body.page +"body page")
console.log(pepTimecards)
}
});
}
}else{
}
});
}
Use the request-promise library which supplies promisified versions of the request library. Then, you can use async/await in your for loop to serialize your operations:
Newer answer to go with the edited code in the OP's question
const fs = require('fs');
const rp = require('request-promise');
const pepKey = 'asdfasdfasd';
pep().then(pepTimecards => {
// the timecard data is valid in here
console.log(pepTimecards);
}).catch(err => {
console.log(err);
});
async function pep() {
let timecards = [];
const options = {
headers: {
"content-type": "application/json",
},
agentOptions: {
pfx: fs.readFileSync('./certificate/asdfsdaf.p12'),
passphrase: 'asdasdsda'
},
json: true,
uri: 'https://source.asdfasdf.io/api/organisations/asdfasdf/timecard_keys?timecard_type=Flex'
};
let pepResponse = await rp(options);
timecards = pepResponse.data;
if (pepResponse.pages > 1) {
for (let i = 2; i <= pepResponse.pages; i++) {
options.uri = 'https://source.`pepme`.io/api/organisations/sdfsadf/timecard_keys?timecard_type=Flex&page='+pageNo;
let body = await rp(url, options);
// add body.data onto the existing array
timecards.push(...body.data);
}
} else {
}
console.log(pepResponse.pages)
console.log(timecards)
return timecards;
}
Prior Answer before OP edited the code in their question:
const rp = require('request-promise');
// I'm assuming this is some sort of method definition on a class, otherwise it needs the function keyword
async pageno(pageNo) {
for (let i=2;i<=pepResponse.pages;i++){
try {
options.uri = 'https://test/timecard_keys?timecard_type=asdas&page='+pageNo;
// let request-promise parse the json for you automatically
options.json = true;
let body = await rp(options);
pepTimecards = pepTimecards.concat(body.data)
if (pepResponse.pages == body.page){
console.log(pepResponse.pages)
console.log(body.page +"body page")
console.log(pepTimecards)
}
} catch(e) {
// decide what to do for error handling
// this will log and rethrow so the caller will get a rejected promise
console.log(e);
throw e;
}
}
// return some value here to be the resolved value of the returned promise
return pepTimecards;
}
In your code, it is not clear where the options, pepTimecards, pepResponse variables are declared. They should probably be declared as local variables here or passed in to the function and/or returned from your function.
Summary of modifications:
Add async to method declaration so we can use await.
Load request-promise library into rp variable
Add options.json = true to the let the request-promise library parse the JSON result for us automatically
Change rp() to just use the options structure (add URL to that)
Add try/catch to catch any errors from the await, log them, then rethrow so pageno() will return a promise that rejects if there is an error (you can customize the behavior when there's an error if desired)
Add a return value so there is meaningful resolved value to the promise (you should not be using side-effect programming as it is now (modifying variables that are not passed in, declared locally or returned).
Things for you still to fix:
Stop using side-effect programming where you modify free variables that aren't passed in, aren't declared locally and aren't returned. This is a bad way to design code. You don't show enough overall context from the calling code or where these other variables are defined to make a concrete recommendation on how it should be done.
Decide what your error handling strategy is if there's an error on one of the requests and implement that strategy and proper handling.

NativeScript Firebase plugin execution order

I'm learning NativeScript/Angular 2 and would need to get help with this issue.
In order to implement a multi-role login system within the Firebase platform I thought about this solution
Login the user through Firebase authentication
Query the /stores/ path for a store which has a merchantEmail field same as the e-mail that has just logged in
If I find it, I set the store ID inside a BackendService service which uses getString/setString to store tokens, then route to a MerchantDashboardComponent
If I don't find it, just route to a BuyerDashboardComponent
This is part of my code in the login.service:
login (email: string, password: string) {
return firebase.login({
type: firebase.LoginType.PASSWORD,
email: email,
password: password
}).then(
(result: any) => {
firebase.query(
(_result) => { // Here I set BackendService.storeID
Inside the .query() callback I am assigning the tokens I need in the application.
This is the method I'm using in my login.component:
doLogin () {
this.isAuthenticating = true;
if (!this.validateEmail()) {
alert("Please insert a valid email");
return false;
}
this.loginService.login(this.email, this.password).then(
() => {
this.isAuthenticating = false;
if (BackendService.loginError)
alert(BackendService.loginError)
else if (BackendService.storeID != '') {
this.router.navigate(['/merchant-dashboard'], {clearHistory: true});
}
else {
this.router.navigate(['/home/categories'], {clearHistory: true});
}
}
);
}
Everything works except for the fact that the Merchant gets routed to the Buyer dashboard. I've managed to discover that the execution order is not what I expected to be, in fact:
firebase.login() gets executed and returns a Promise
.then() handler is executed inside the doLogin() method
Only after this, the firebase.query() method completes the callback and my tokens are available, but doLogin() has already navigated the user because storeID is still empty when I need it
I hope I've been clear as much as possible.
Thanks for your attention.
Greetings,
Davide
So, the problem was in the login service method.
I now return the Promise generated by firebase.query(), which causes then() calls to chain in the correct order.
Yep that was exactly was I was going to propose to wrap it in a promise and create a chain.
example code
return new Promise<any>((resolve, reject) => {
firebase.login({ loginArguments })
.then((result: any) => {
var onQueryEvent = function (result) {
};
return firebase.query(
onQueryEvent,
"/owner",
{
// query arguments follows here
}
).then(res => {
return res;
})
})
.then(finalResult => {
console.log(finalResult);
try {
resolve(finalResult);
} catch (e) {
reject(e);
}
})
});

How to make synchronous API call request in react js

i am beginner to react js i am working on a small application which makes api requests frequently. So the problem i am facing is there is a page with form fields prefilled from the db, if the user makes changes to those fields i am posting the new filed values to db. when submit button is clicked saveAndConttinue() is called,from there addNewAddress() is invoked based on the condition. But the problem is the response that i get from addNewAddress has to be used for the next api call in the queue, but it is taking time to get response, and the address_id is having null values for it's post call. is there any way to make synchronous call in react with out using flux/redux for now?
saveAndContinue(e) {
e.preventDefault();
if(this.props.params.delivery === 'home_delivery' && this.state.counter) {
this.addNewAddress();
}
console.log('add id is '+this.state.address_id);
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
let fd = new FormData();
fd.append('token', this.props.params.token);
fd.append('dish_id', this.props.params.dish_id);
fd.append('address_type', this.props.params.delivery);
fd.append('address_id', this.state.address_id);
fd.append('ordered_units', this.props.params.quantity);
fd.append('total_cost', this.props.params.total_cost);
fd.append('total_service_charge', this.props.params.service_charge);
fd.append('net_amount', this.props.params.net_cost);
fd.append('hub_id', this.props.params.hub_id);
fd.append('delivery_charge', this.props.params.delivery_charge);
fd.append('payment_type', this.state.payment_type);
fd.append('device_type', 'web');
axios.post(myConfig.apiUrl + '/api/foody/orders/purchase' , fd, config)
.then(function(response){
if(response.data.success) {
console.log(response);
browserHistory.push('/order_confirmed/');
} else {
console.log(response);
//alert(response.data.message)
}
});
}
addNewAddress() {
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
let fd = new FormData();
if(this.props.params.user_type === 'new') {
fd.append('type', 'PRIMARY');
}
fd.append('token', this.props.params.token);
fd.append('block', this.state.block);
fd.append('door_num', this.state.door_num);
fd.append('address', this.props.params.address);
fd.append('locality', this.props.params.locality);
fd.append('landmark', this.props.params.landmark);
fd.append('hub_id', this.props.params.hub_id);
axios.post(myConfig.apiUrl + '/api/user/add-address' , fd, config)
.then(function(response){
this.setState({address_id: response.data.data['id']});
console.log(this.state.address_id);
}.bind(this));
}
You're going to have to call the next request in the queue after addNewAddress() in the returned promise of addNewAddress():
addNewAddress() {
axios.post()...
.then(function (response) {
// Set state
this.setState({address_id: response.data.data['id']});
// [Call next API request here]
// ...
})
}
Doing synchronous calls are always a bad idea, I personally would advise against it and just do the next call in the returned promise as shown above.

Resources