Working with HTTP-responses in angular2 - javascript

I'm working on web-app with authorization via JWT and Angular 2. I've Nodejs/express server with API and client-side on Angular2.
So, my server answers GET request correctly and gives data like this:
{
"success": true,
"user": {
"_id": "5a6ef70edb04dd29e24bb03b",
"email": "danko",
"username": "ivan"
}
}
Next, here is my auth.service.ts. Functions createAuthenticationHeaders() and getProfile() takes part in handling HTTP responses:
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders, HttpRequest, HttpResponse, HttpParams} from '#angular/common/http';
import { Http, Headers, RequestOptions} from '#angular/http'; // Http, Headers, RequestOptions
import 'rxjs/add/operator/map';
#Injectable()
export class AuthService {
domain = "http://localhost:8080";
authToken;
user;
options;
constructor(
private http: HttpClient,
private httplegacy: Http) { }
createAuthenticationHeaders() {
this.loadToken();
this.options = new RequestOptions({
headers : new Headers({
'Content-Type' : 'application/json',
'authorization' : this.authToken
})
});
}
loadToken() {
this.authToken = localStorage.getItem('token');
}
registerUser(user) {
return this.http.post(this.domain + '/authentication/register', user);
}
loginUser(user) {
return this.http.post(this.domain + '/authentication/login', user);
}
storeUserData(token, user) {
localStorage.setItem('token', token);
localStorage.setItem('user', JSON.stringify(user));
this.authToken = token;
this.user = user;
}
getProfile() {
this.createAuthenticationHeaders();
return this.httplegacy.get(this.domain + '/authentication/profile', this.options);
}
}
Also, here is my profile.component.ts:
import { Component, OnInit } from '#angular/core';
import { AuthService} from '../../services/auth.service';
#Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
username;
email;
constructor(
private authService: AuthService
) { }
ngOnInit() {
this.authService.getProfile().subscribe(profile => {
console.log(profile);
this.username = profile.user.username;
this.email = profile.user.email;
})
}
}
Expected behavior of these lines of code: after handling server's response with user's data with auth.service.ts(mainly createAuthenticationHeaders() and getProfile() functions), user's data is transmitted to profile.component.ts to show it on web-page using next code:
<h2 class="page-header">Profile Page</h2>
<ul class="list-group">
<li class="list-group-item">Username: {{ username }} </li>
<li class="list-group-item">Email: {{ email }}</li>
</ul>
But, while compiling I got an error: property 'user', doesn't exist on type 'Response'. Would You like to explain why i got such error, and how to fix it?
P.S.: yep, console.log(profile) gives me such info:
Response {_body: "{"success":true,"user":{"_id":"5a6ef70edb04dd29e24bb03b","email":"danko","username":"ivan"}}", status: 200, ok: true, statusText: "OK", headers: Headers, …}
headers:Headers {_headers: Map(1), _normalizedNames: Map(1)}
ok : true
status : 200
statusText : "OK"
type : 2
url : "http://localhost:8080/authentication/profile"
_body : "{"success":true,"user":{"_id":"5a6ef70edb04dd29e24bb03b","email":"danko","username":"ivan"}}"
__proto__ : Body
constructor : ƒ Response(responseOptions)
toString : ƒ ()
__proto__ :
Object
But how can I get data from _body field of response?
P.S.: code for router from server side:
router.get('/profile', (req, res) => {
User.findOne({ _id: req.decoded.userId }).select('username email').exec((err, user) => {
if (err) {
res.json({ success: false, message: err });
} else {
if(!user) {
res.json({ success: false, message: 'User not found'});
} else{
res.json({ success: true, user: user });
}
}
});
});

you try to read your data directly from the Response Object of express. You need smth like:
this.authService.getProfile().subscribe(profile => {
console.log(profile);
let p = JSON.parse(profile._body)
this.username = p.user.username;
this.email = p.user.email;
})
This will take the JSON string from the body of your HTTP Response and make it an accessible object.
NOTE:
It would be much better to tell the server to answer with a standard json due to this is web standard nowadays.

Update: #messerbill's was 50/50 correct. Such construction works:
this.authService.getProfile().subscribe(profile => {
console.log(profile);
let p = JSON.parse(profile._body)
this.username = p.user.username;
this.email = p.user.email;
})
My web-page got user's info and show it in correctly, but an error left and i've to comment these lines of code to compile and run my application, and uncomment after to see user's info on webpage.
An error message: property '_body' does not exists on type 'Response'.
So, at this moment i've no idea how it works with an error and how to create really correct structure.

Try this
ngOnInit() {
this.authService.getProfile().subscribe(profile => {
console.log(profile);
let p = profile.json();
this.username = p.user.username;
this.email = p.user.email;
})
}

Related

How to get status code in angular observable

I have services below that I'd like to get status code and handle if statements in it but so far I couldn't figure it out
import { Injectable } from '#angular/core';
import { EnvService } from './env.service';
import { tap } from 'rxjs/operators';
import { Observable, from } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { NativeStorage } from '#ionic-native/native-storage/ngx';
import { Plugins } from '#capacitor/core';
const { Storage } = Plugins;
#Injectable({
providedIn: 'root'
})
export class InvoicesServiceService {
token: any;
constructor(
private env: EnvService,
private http: HttpClient,
private nativeStorage: NativeStorage
) {
Storage.get({ key: 'token' }).then((token: any) => {
this.token = JSON.parse(token.value)
}).catch(error => console.error(error));
}
// All
getInvoices(): Observable<any> {
const tokenPromise =
this.token === undefined
? Storage.get({ key: 'token' })
: Promise.resolve(this.token);
return from(tokenPromise).pipe(
switchMap((token) => {
this.token = this.token;
const httpOptions = {
headers: new HttpHeaders({
Accept: 'application/json, text/plain',
'Content-Type': 'application/json',
Authorization: this.token.access_token,
}),
};
return this.http
.get(`${this.env.Dashboard}` + '/invoices', httpOptions)
.pipe(map((data) => data));
})
);
}
What I try to do is that if, status code is 403 redirect user to specific route other than that just return data.
any idea?
In component where you subscribe this service you can handle error
this.service
.getInvoices()
.subscribe((response) => {
// This is success
},
(error: HttpErrorResponse) => {
// Handle error
// Use if conditions to check error code, this depends on your api, how it sends error messages
});
Another way to handle in service itself.
return this.http
.get(`${this.env.Dashboard}` + '/invoices', httpOptions)
.pipe(map((data) => data))
.toPromise()
.then((response) => {
//Success
})
.catch((error: HttpErrorResponse) => {
// Handle error
});
Hope this helps.
The error is not always sent in the headers.
Sometimes the erros comes via HTML message, like when NGINX tells you someting before you even get to the backend:
<html>
<head><title>413 Request Entity Too Large</title></head>
<body>
<center><h1>413 Request Entity Too Large</h1></center>
<hr><center>nginx</center>
</body>
</html>
In these cases you should use if (error.includes('413 Request Entity Too Large')) {...}

Ionic angular won't send headers

I am trying to store data in database with API and I'm getting Trying to get property 'id' of non-object error.
The reason that I'm getting that error is because Ionic won't send my user token to server
0: Closure (HttpHeaders)
headers:
Accept: "application/json, text/plain"
Authorization: "undefined undefined" <--- here
Content-Type: "application/json"
Code
store.page.ts
This data will send to store services and from there to the server.
create() {
const addToko = this.addToko.value;
this.storeService.store(
addToko.name,
addToko.description,
addToko.phone,
addToko.province_id,
addToko.kota_id,
addToko.address,
addToko.logo = this.logoURI,
addToko.banner = this.bannerURI,
).subscribe(
data => {
this.alertService.presentToast(data);
console.log('done ', data);
},
error => {
this.alertService.presentToast(error);
console.log('error ', error);
}
);
}
store.service.ts
Data from here will send to server
export class StoreService {
token: any; //setting user token
constructor(
private http: HttpClient,
private env: EnvService,
private storage: NativeStorage,
) {
//gettinguser token
this.storage.getItem('token').then(
data => {
this.token = data;
console.log('token data', data);
},
error => {
this.token = null;
}
);
}
store(
name: String,
description: String,
phone: String,
province_id: String,
kota_id: String,
address: String,
logo: String,
banner: String,
) {
// adding headers to request
const headers = new HttpHeaders({
'Accept': 'application/json, text/plain',
'Content-Type': 'application/json',
'Authorization': this.token["token_type"] + " " + this.token["access_token"]
});
return this.http.post(this.env.STORES_URL,
{ name: name, description: description, phone: phone, province_id: province_id, kota_id: kota_id, address: address, logo: logo, banner: banner, headers: headers } // defining headers to request
)
}
}
Might be a bit of help:
I have Auth.service.ts which takes care of user loggin status and route guards etc. there I have function below which gets users token and in there it's working just fine, I implemented same function in my store.service.ts (code above) but it doesn't work.
getToken() {
return this.storage.getItem('token').then(
data => {
this.token = data;
if (this.token != null) {
this.isLoggedIn = true;
} else {
this.isLoggedIn = false;
}
},
error => {
this.token = null;
this.isLoggedIn = false;
}
);
}
Any idea?
your implemenation of store service needs a correction.
You've included the headers in object body, take it out from there.
return this.http.post(this.env.STORES_URL,
{ name: name, description: description, phone: phone, province_id:
province_id, kota_id: kota_id, address: address, logo: logo,
banner: banner, headers: headers /* headers should not be embedded in object body*/
});
return this.http.post(this.env.STORES_URL,
{ name: name, description: description, phone: phone, province_id:
province_id, kota_id: kota_id, address: address, logo: logo,
banner: banner
}, {headers: headers});
This is probably a race condition, the token has not been set before making the request. You could make a http-interceptor to take care of setting the auth header to your requests:
import { Injectable } from '#angular/core';
import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '#angular/common/http';
import { Observable } from 'rxjs';
import { of, from} from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
#Injectable()
export class NoopInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
// convert promise to observable
// fetch token and switch to inner observable where we add headers
return from(this.storage.getItem('token')).pipe(
switchMap((token) => {
// clone request and add headers
const authReq = req.clone({
headers: new HttpHeaders({
'Authorization': token["token_type"] + " " + token["access_token"]
})
})
return next.handle(authReq);
})
)
}
Add the interceptor to providers array in app.module:
{ provide: HTTP_INTERCEPTORS, useClass: NoopInterceptor, multi: true },
Then just remove the httpOptions from your request (which was like in other answer, in the wrong place):
return this.http.post(this.env.STORES_URL,
{ name: name, description: description, phone: phone, province_id:
province_id, kota_id: kota_id, address: address, logo: logo,
banner: banner
});

Angular 2: Getting undefined when return data from service

I'm using service to make http call and then return the response back to component where I get "undefined" when I console it. I have set timeout to make sure that the http request gets completed before console prints it but without any luck. I'm new to Angular 2, would really appreciate if someone could help me out.
My serivce code:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { LoginComponent } from './login/login.component';
#Injectable({
providedIn: 'root'
})
export class LoginService {
rootUrl = 'https://dev-510009.oktapreview.com/'
constructor(public _http: HttpClient){
}
primaryVerify1(userData) {
let data = {
"username": userData.username,
"password": userData.pass,
"options": {
"multiOptionalFactorEnroll": true,
"warnBeforePasswordExpired": true
}
};
this._http.post(this.rootUrl + "api/v1/authn", data, {
headers: {
'Content-type': 'application/json'
}
}).subscribe(response => {
if(response.status == 'SUCCESS'){
let primaryverifydata = response
console.log("primaryverifydata", primaryverifydata)
let data1 = {
"factorType": "token:software:totp",
"provider": "GOOGLE"
}
this._http.post(this.rootUrl + "api/v1/users/"+ primaryverifydata._embedded.user.id + "/factors", data1,
{
headers: {
'Content-type': "application/json",
'Authorization' :'SSWS 00e1Wq_tDwvikJt2ZufC0DgW58JX61R6BEQriGsvtl',
'Accept': "application/json"
}
}).subscribe(response => {
console.log(response)
let enrollResponse = response;
if(response.status = 'PENDING_ACTIVATION'){
window.open(enrollResponse._embedded.activation._links.qrcode.href, '_blank')
return response;
}
})
}
})
}
}
My component code:
import { Component, OnInit } from '#angular/core';
import { LoginService } from '../login-service.service';
#Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css'],
providers: [LoginService],
})
export class LoginComponent implements OnInit {
userData: object;
pass: any;
enrollResponse: object;
constructor(private loginservice: LoginService) {
this.userData = {};
this.pass = "";
this.enrollResponse = {}
}
ngOnInit(){
/* this.enrollResponse = this.loginservice.primaryVerify;
console.log(" this.enrollResponse", this.enrollResponse)*/
}
primaryVerify(){
let some = this.loginservice.primaryVerify1(this.userData);
setTimeout(() => {
console.log("this.enrollResponse", some)
},5000)
}
}
Kindly note: primaryVerify() gets fired when user clicks on submit button.
Use switchMap in your service primaryVerify1() since you are calling an http requests one after another. That will kill the previous observable.
Do not subscribe in your service, instead subscribe in your component and just map the results from service (i.e return resp from service). Make note of 3 return key words in the below code.
setTimeout() not required in component, instead subscribe to primaryVerify1()
Service code :
primaryVerify1(userData) {
...........
return this._http.post(this.rootUrl + "api/v1/authn", data, {
headers: {
'Content-type': 'application/json'
}
}).pipe(switchMap(response => {
if(response.status == 'SUCCESS'){
...............
return this._http.post(this.rootUrl + "api/v1/users/"+ primaryverifydata._embedded.user.id + "/factors", data1,
{
headers: {
'Content-type': "application/json",
'Authorization' :'SSWS 00e1Wq_tDwvikJt2ZufC0DgW58JX61R6BEQriGsvtl',
'Accept': "application/json"
}
}).pipe(map(response => {
...........
return response;
}))
})
}
}))
}
component
primaryVerify(){
let some = this.loginservice.primaryVerify1(this.userData).subscribe(data => console.log(data));
}
primaryVerify1 does not return anything, so undefined is expected.
What you should do is using a callback or a Promise. Something along the lines of:
primaryVerify1(userData, callback) {
...
let enrollResponse = response;
if(response.status = 'PENDING_ACTIVATION'){
window.open(enrollResponse._embedded.activation._links.qrcode.href, '_blank')
callback(response);
}
and use it like that:
let some = this.loginservice.primaryVerify1(this.userData, function(res) {
console.log("this.enrollResponse", res);
});
Of course this does not handle errors very well (you may want to return early at each error possibility), but it gives you a starting point.

Angular 2 authenticate state

I've implemented a login page using Angular 2. After login, I get jsonwebtoken, userId, userRole, userName from server. I'm storing this info in localstorage so that I can access it any time and maintain login state if user refreshes page.
AuthService.ts
import {Injectable} from "#angular/core";
#Injectable()
export class AuthService {
redirectUrl: string;
logout() {
localStorage.clear();
}
isLoggedIn() {
return localStorage.getItem('token') !== null;
}
isAdmin() {
return localStorage.getItem('role') === 'admin';
}
isUser() {
return localStorage.getItem('role') === 'user';
}
}
To check the login status, I'm just checking if token exists in localstorage. As localstorage is editable so just adding any token in localstorage would bypass login page. Similarly, if client edit user role in localstorage, client can easily access admin or user pages.
How do I solve these problems?
This is more like a general problem, I want to know how websites maintain login status?
P.S.
NodeJS Server side login code to generate jsonwebtoken
const jwt = require('jsonwebtoken');
const User = require('../models/User');
/**
* POST /login
* Sign in using username and password
*/
exports.postLogin = (req, res, next) => {
User.findOne({username: req.body.username})
.then(user=> {
if (!user) {
res.status(401);
throw new Error('Invalid username');
}
return user.comparePassword(req.body.password)
.then(isMatch=> {
if (isMatch != true) {
res.status(401);
throw new Error('Invalid password');
}
let token = jwt.sign({user: user}, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_TIMEOUT
});
return res.status(200).json({
success: true,
token: token,
userId: user._id,
role:user.role,
name:user.name
});
});
})
.catch(err=>next(err));
};
-Thanks
Tokens are supposed to be unique and hard to type (as of a big length). Also, they should be refreshed with some frequency. Better to read oAuth docs on this
Roles should not be stored on client side. Only checking on server.
Also, when using oAuth consider using Scopes.
You digitally sign the authentication token on the server side:
jwt.sign({user: user}, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_TIMEOUT
})
This signature then should be verified by the server side on subsequent requests. It becomes invalid when client changes the content of the token.
store token in localStorage/sessionStorage and validate token with server whenever required. I am having following implementation to validate token
UserProfileService.ts
#Injectable()
export class UserProfileService {
private isLoggedIn: boolean = false;
private apiEndPoint: string;
constructor(private http: Http) {
this.apiEndPoint = environment.apiEndpoint;
}
login(token: string) {
localStorage.setItem('auth_token', token);
this.isLoggedIn = true;
}
logout(){
localStorage.removeItem('auth_token');
this.isLoggedIn = false;
}
isAuthorized(): Observable<boolean> {
if (!this.isLoggedIn) {
let authToken = localStorage.getItem('auth_token');
if(authToken){
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Accept', 'application/json');
headers.append('Authorization', `Bearer ${authToken}`);
return this.http.get(`${this.apiEndPoint}/validate-token`, { headers: headers })
.map(res => {
let serverResponse = res.json();
this.isLoggedIn = serverResponse['valid'];
if (!this.isLoggedIn) {
localStorage.removeItem('auth_token');
}
return this.isLoggedIn;
})
.catch(this._serverError);
}
}
return Observable.of(this.isLoggedIn);
}
private _serverError(err: any) {
localStorage.removeItem('auth_token');
if(err instanceof Response) {
console.log(err.json());
return Observable.of(false);
}
return Observable.of(false);
}
}
AuthService.ts
#Injectable()
export class CanActivateAuthGuard implements CanActivate, CanActivateChild, CanLoad {
constructor(private userProfileService: UserProfileService, private router: Router) { }
canLoad(route: Route) {
return this.userProfileService.isAuthorized().map(authorized => {
if(authorized) {
return authorized;
} else {
let url = `/${route.path}`;
this.router.navigate(['/login'], { queryParams: { redirectTo: url } });
return authorized;
}
});
}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
) {
return this.userProfileService.isAuthorized().map(authorized => {
if(authorized) {
return authorized;
} else {
this.router.navigate(['/login'], { queryParams: { redirectTo: state.url } });
return authorized;
}
});
}
canActivateChild(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
) {
return this.canActivate(route, state);
}
}

Value become null for this in Promise callback

I am using the following code value of this become null when i call it inside the then function here is the code. Am i doing something wrong or it is like this or there is any work around to resolve this issue
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { CanActivate, Router } from '#angular/router';
import { AuthService } from '../services/auth.service';
import { WebAPISettings } from '../services/webapisettings.service';
#Injectable()
export class LoginService {
//_ngWEBAPISettings: WebAPISettings;
//_authService: AuthService;
constructor(private http: Http, private ngWEBAPISettings: WebAPISettings, private authService: AuthService) {
//this._ngWEBAPISettings = ngWEBAPISettings;
//this._authService = authService;
}
public login(username: string, password: string): Promise<any> {
let data = "grant_type=password&username=" + username + "&password=" + password;
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
let options = new RequestOptions({ headers: headers });
try {
debugger;
return this.http.post(this.ngWEBAPISettings.apiServiceBaseUri + "token", data, options)
.toPromise()
.then(function (res: Response) {
debugger;
let body = res.json();
//let _authService: AuthService = new AuthService();
this.authService.fillAuthDataFromLogin(body);
//this.router.navigate(['/Home']);
return body.data || {};
})
.catch(this.handleError);
}
catch (error) {
console.log(error);
}
}
private extractData() {
}
private handleError(error: any) {
debugger;
let errMsg = (error.message) ? error.message :
error.status ? `${error.status} - ${error.statusText}` : 'Server error';
console.error(errMsg); // log to console instead
return Observable.throw(errMsg);
}
}
and i am debugging it in the chrome here is the screenshot please help me in fixing it.
after using the arrow function same thing check the screen shot
one thing to mention i am using Angular2 RC4.
You could use an arrow function to be able to use the lexical this:
return this.http.post(this.ngWEBAPISettings.apiServiceBaseUri + "token", data, options)
.toPromise()
.then((res: Response) => { // <-----
(...)
});
This way, this will correspond to the instance of the LoginService service.
See this doc for more details:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Categories

Resources