Hi,
Would be nice if was possible to configure on every http 401 the user be redirect to login page.
Any plan for this or how to do this?
Thanks for the suggestion! However, I think that would be a little bit out of scope for this library as we want to keep it as a thin wrapper for HTTP requests.
You can certainly wrap AuthHttp with another class that handles redirection. Check out @escardin's code sample in #31 to see how to get a start at it :)
I just thought I'd post what I have done to achieve this...
boot.ts:
import {provide} from 'angular2/core';
import {ConnectionBackend, HTTP_PROVIDERS, Http} from 'angular2/http';
import {bootstrap} from 'angular2/platform/browser';
import {Router, ROUTER_PROVIDERS} from 'angular2/router';
import {AuthConfig} from 'angular2-jwt';
import {AppComponent} from './app';
import {AuthHttp} from './config/http';
bootstrap(AppComponent, [
HTTP_PROVIDERS,
ROUTER_PROVIDERS,
provide(AuthConfig, {
useFactory: () => {
return new AuthConfig({
noJwtError: true
});
}
}),
provide(AuthHttp, {
useFactory: (http, router) => {
return new AuthHttp(new AuthConfig(), http, router);
},
deps: [Http, Router]
}),
AuthHttp,
ConnectionBackend
]);
config/http.ts:
import {Injectable} from 'angular2/core';
import {Http, Request, Response, RequestOptionsArgs} from 'angular2/http';
import {Router} from 'angular2/router';
import {AuthHttp as JwtAuthHttp, AuthConfig} from 'angular2-jwt';
import {Observable} from 'rxjs/Observable';
@Injectable()
export class AuthHttp extends JwtAuthHttp {
constructor(options: AuthConfig, http: Http, private _router: Router) {
super(options, http);
}
_isUnauthorized(status: number): boolean {
return status === 401;
}
_request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
var response = super._request(url, options);
response.subscribe(null, err => {
if (this._isUnauthorized(err.status)) {
this._router.navigate(['Login']);
}
});
return response;
}
}
Hi thanks for that code. Worked fine except every request was made twice to server.
Had to change response.subscribe(null, err => { to response = response.do(null, err => {
For me the problem is that angular2 always return status 200 when the backend return the response error. There is a open issue at angular2 github for this https://github.com/angular/http/issues/54
Anyone have this same problem?
@wanton7 Can you show the entire code, I'm with this duplication problem that you said but I could no figure out your changes, I'm receiving response.do is not a function
@wanton7 Thanks for the update, though I am also getting response.do is not a function. I'm using rxjs 5.0.0-beta.0, angular2-jwt 0.1.6 and angular2 2.0.0-beta.3. Any clarity on this would be a great help.
@jasonroyle I'm using following from npm: rsjx 5.0.0-beta.2 and angular 2.0.0-beta.14.
rxjs 5.0.0-beta.2 folder has Observable.d.ts with do: (next?: (x: T) => void, error?: (e: any) => void, complete?: () => void) => Observable
@jasonroyle you need to add the operator https://github.com/escardin/angular2-community-faq/blob/master/rxjs.md#adding-operatorsobservables-map-is-not-defined
Thanks @escardin, sorted! 馃槃
anyone is receiving 401 error status?
I always receive status 200 with error instead of another status sent by backend. This issue angular/http#54 is taking too long to be resolve =(
I'm using Asp.Net Core with Angular 2. It's returning 401 just fine. I'm using that jwt library but not actually using jwt, so i'm not returning any signed stuff from server side.
Are you getting something on the error path at all? if you're getting something on the error path with status 200 then it was something like a connection error or similar, and you can use that to work. If your server is returning a non 2XX status, it's supposed to route through the error path.
I'm returning status 401, I receive this status at postman, rest client, swagger and so on, but angular 2 does not respect this and show me status 200 =(
I'm using spring boot and wildfly server
what do you see in your network console in chrome for the failed request? can you copy paste it for us?
Hi,
I know this is not the best place for this question, but anyone have any advice?
I still have this issue. In the console, I see this (I was testing error handling while our test environment was unwell).
XMLHttpRequest cannot load https://mycompany.com/mobile-gateway/signin. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 400.
But what my code receives is this:
{"_body":{"isTrusted":true},"status":200,"ok":true,"statusText":"Ok","headers":{},"type":3,"url":null}
The calling code looks like this:
return this.http.request(new Request(options))
.map((res: Response) => <OauthResponse> res.json())
.do((res: OauthResponse) => {
console.log('do: ' + JSON.stringify(res));
localStorage.setItem('access_token', res.access_token);
this.setStandardHeaders();
})
.catch(MobileGatewayService.handleSigninError);
Here you see how the logging above was done.
private static handleSigninError(error: Response) {
let response: any;
try {
console.log(JSON.stringify(error));
// response = error.json();
response = {
status: 401,
message: 'Sign-in Error.'
};
// If the body wasn't JSON, something else went wrong -
// provide a normalized default error body.
} catch ( jsonError ) {
response = {
status: 500,
message: 'The system is unavailable at this time. Please try again later.'
};
}
console.log('handleSigninError ' + response);
return( Rx.Observable.throw( response ) );
}
Is something that I'm doing, or not doing, causing Angular 2 to swallow the 400 error I see in the log?
the same problem as describe @smitchell and @gustavolira. Request to nonexisting page (in network tab 404) returns
Response {_body: XMLHttpRequestProgressEvent, status: 200, ok: true, statusText: "Ok", headers: Headers鈥
Any idea?
That's what http does. Nothing to do with angular2-jwt. As I understand it, if you get a network error (cors fails, no internet connection, etc...) you'll get a status of 200, and the body will be the XHR object instead of the actual response. It's a bug, but it's detectable.
@escardin - Is there any way for us to get to the XHR object? It is not part of the Response passed into .map
return this.http.request(new Request(options))
.map((res: Response) => <OauthResponse> res.json())
.do((res: OauthResponse) => {
console.log('do: ' + JSON.stringify(res));
localStorage.setItem('access_token', res.access_token);
this.setStandardHeaders();
})
.catch(MobileGatewayService.handleSigninError);
try getting it from res._body or doing res.text()
@escardin - In our case, which is not limited to just sign-in, _both "isTrusted: true," and text is "OK," and the headers are empty, despite having seen an error emitted in the console immediately prior to receiving the 200 response.
{"_body":{"isTrusted":true},"status":200,"ok":true,"statusText":"Ok","headers":{},"type":3,"url":null}
@smitchell I'm not sure I understand. You got an xhr error, but got the response you want?
The error is swallowed and dumped to the console log. Our error handlers never got invoked. We only get a success response of 200. It is my understanding that if you don't provide an error handler that is what happens. It is as if our error handler is not being recognized. Here is a typical example.
return this.http.request(new Request(options))
.map((res: Response) => <MobileResponse> res.json())
.catch(this.handleError);
@escardin - OH! I think I just set it up wrong. Let me test this
return this.http.request(new Request(options))
.map((res: Response) => <MobileResponse> res.json())
.do(() => {
this.logCall();
})
.catch(e => this.handleError(e));
I handle it by type=3 It means error
Nope. The syntax change made no difference. I tried unplugging my network cable and attempting to sign in. The error details get swallowed and we get a 200, although, it does say type=3 as @ramilexe pointed out.

Is this the browser's preflight OPTIONS request to check for CORS headers by any chance?
@jasonroyle - I'm testing with Chrome, but ultimately this goes through PhoneGap to iPhone and Android apps. These are our standard headers:
private _standardHeaders = new Headers({
'Authorization': 'Bearer ' + localStorage.getItem('access_token'),
'Content-Type': 'application/json'
});
and this is a typical call.
getUserEmails(): Rx.Observable<Email[]> {
let options = new RequestOptions({
method: RequestMethod.Get,
url: this._emailUrl,
headers: this._standardHeaders
});
return this.http.request(new Request(options))
.map((res: Response) => <Email[]> res.json().data)
.do(() => {
this.logCall();
})
.catch((res: Response) => this.handleError(res));
}
I misspoke earlier. The error handler function does indeed get invoked, but we always get a status of 200 and don't know if it was 403, 400, 500, etc...
If it is the browser's preflight OPTIONS request you need to allow OPTIONS requests to your server. Angular won't handle these requests as it wasn't Angular that made them. When a request from a client is made most browsers do their own preflight OPTIONS request to the same URI that Angular is about to request. These preflight requests should tell the browser wether or not it is ok to send the request based on the CORS headers returned. See here for more information.
@smitchell So it looks like you're doing a GET request from Angular but your console shows that an OPTIONS request fails. From my experience this looks very much like the browser's preflight request and Chrome always performs preflight checks.
@jasonroyle - We do allow options:
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
}
I think we should create issue in angular2 repo. Is is not jwt problem.
k
Changing the syntax of my call from this;
.catch(this.handleSigninError);
To this:
.catch((res: Response) => this.handleSigninError(res));
Fxed my problem. For the first time I've seen the actual error code returned to my error handler:
{"_body": "{\"timestamp\":1465920358459,\"status\":500,\"error\":\"Internal Server Error\",\"exception\":\"com.netflix.zuul.exception.ZuulException\",\"message\":\"GENERAL\"}",
"status": 500,
"ok": false,
"statusText": "Ok",
"headers": {
"Content-Type": ["application/json;charset=UTF-8"]
},
"type": 2,
"url": "https://test.ioubenefits.com/mobile-gateway/signin"
}
Most helpful comment
I just thought I'd post what I have done to achieve this...
boot.ts:
config/http.ts: