I have created an app using Google API and I'm using Google fusion tables for the backend so I have enabled the fusion table API as well. I'm making an hybrid app using ionic 2. GET works perfectly for reading the table and POST given an
error 401
.
function submitAnswer(button) {
var accessToken = document.getElementById("accessToken").value;
var query = "https://www.googleapis.com/fusiontables/v2/query?sql=INSERT INTO " + answerTableId + "(Answers,QuestionId,UserID) VALUES ('" + button.value + "','" + currentQueNo + "','" + userId + "')"+key+"&access_token="+accessToken;
var xhttp2 = new XMLHttpRequest();
xhttp2.onreadystatechange = function() {
//alert(this.readyState + " " + this.status);
if(this.readyState == 4) {
alert(this.responseText);
}
};
xhttp2.open("POST", query, true);
xhttp2.setRequestHeader('Authorization',accessToken);
xhttp2.send();
}
Maybe you've only forgotten "Bearer " in your Authorization value :
xhr.setRequestHeader('Authorization', 'Bearer ' + oauthToken.access_token);
Or maybe you've just badly encoded your accessToken in your query (you need to use encodeURIComponent(accessToken))
If this is called from a browser and not from NodeJS, you may be blocked by CORS issue.
Also, not related to your question : your way of creating the request is very sensible to SQL injection. Some random user could just delete your whole database without knowing any password.
Sincw you are using ionic 2 while don't create like an interceptor that extends the main angular http like this code sample below should do every trick for you and i suggest you adhere to it. Since it is ionic 2 angular 2+
Firstly, create a class to extend the http class like so api-handler.ts:
import { Storage } from '#ionic/storage';
import { environment } from './environment';
import { Injectable } from '#angular/core';
import { Headers, Http, ConnectionBackend, RequestOptions, RequestMethod, RequestOptionsArgs } from '#angular/http';
import 'rxjs/add/operator/map';
import { Observable } from "rxjs/Observable";
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/finally';
import 'rxjs/add/observable/throw';
import 'rxjs/add/observable/fromPromise';
import 'rxjs/add/operator/mergeMap';
/*
Generated class for the ApiHandler provider.
this is used to make communication with our endpoint where we pass endpoint
header information and any form of manipulation
*/
#Injectable()
export class ApiHandler extends Http {
private bearer: string = 'Plutus';
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, private _storage: Storage) {
super(backend, defaultOptions);
}
/**
* This is used to call our api service by inputing the service url
* #param service_url
* #param method
* #param params
* #param options
*
* #return Observable<any>
*/
callService(service_url: string, method: RequestMethod, params?: any, options?: RequestOptionsArgs): Observable<any> {
if (params == null) {
params = {};
}
options = this.requestOptions(method, params, options);
let token_promise: Promise<any> = this._storage.get('token');
return Observable.fromPromise(token_promise)
.mergeMap(token => {
console.log("token from storage", token);
if (options.headers == null && token != null) {
options.headers = new Headers({
'Authorization': `${this.bearer} ${token}`
});
}
return super.request(this.getFullUrl(service_url), options)
.catch(this.onCatch);
});
}
/**
* Request options is used to manipulate and handle needed information before
* it is sent to server
* #param options
* #returns {RequestOptionsArgs}
*/
private requestOptions(method: RequestMethod, params: any, options?: RequestOptionsArgs): RequestOptionsArgs {
if (options == null) {
options = new RequestOptions();
}
options.method = method;
if (options.method === RequestMethod.Post || options.method === RequestMethod.Put) {
options.body = params;
} else {
options.params = params;
}
return options;
}
/**
* Build API url.
* and we remove any leading / from the service calls since
* we are not needing then in making request calls
* e.g localhost:1337//base... to localhost:1337/base..
* #param url
* #returns {string}
*/
private getFullUrl(url: string): string {
if (url.charAt(0) == "/") {
url = url.substring(1);
}
return environment.endpoint + url;
}
/**
* Error handler.
* #param error
* #param caught
* #returns {ErrorObservable}
*/
private onCatch(error: any, caught: Observable<any>): Observable<any> {
return Observable.throw(x);
}
}
if you observe the way i added header information on the above code and using the request method which allows for any form of http methods like Request.Get, Request.Post, Request.Put, Request.Delete, etc.
Secondly, in your app.module.ts provide the class as your default http call for any backend communication by adding the below to your providers:[]
{
provide: ApiHandler,
useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, _storage: Storage) => new ApiHandler(backend, defaultOptions, _storage),
deps: [XHRBackend, RequestOptions, Storage]
}
Then finally, to use it in your case just add this to your constructor, then use straight away like this
import { IFeedBack } from './../interfaces/ifeedback';
import { Observable } from 'rxjs/Observable';
import { ApiHandler } from './../util/api-handler';
import { Injectable } from '#angular/core';
import { RequestMethod } from '#angular/http';
import 'rxjs/add/operator/map';
/*
Generated class for the FeedBackServiceProvider provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular 2 DI.
*/
#Injectable()
export class FeedBackService {
constructor(private _apiHandler: ApiHandler) {
}
/**
* this is used to create new feedback
* #param feedback
*/
create(feedback: IFeedBack): Observable<IFeedBack> {
return this._apiHandler.callService('/feedback', RequestMethod.Post, feedback)
.map(res => <IFeedBack>res.json());
}
}
then you can call the create with the new param to send and then subscribe to it.
Think this should serve you better.
Related
I have completed steps of authorization and obtained the access token and refresh token by Laravel Passport.
My Angular frontend and Laravel backend work fine.
My main questions are:
How and when should I use the refresh token to make new access token?
Should this be done in the background or should the user have to click on a button to refresh the token?
Should the Angular page be reloaded when creating the new token?
I am using JWT authentication in my angular project where the token is set by the API.
The approach I'm taking when the token is expired in explained below -
Expose a new API which will take an expired token and return newly created token.
The API should check the token expiry in every REST API call.
In case, the token is expired, the API should return a status (as per the standards, 498 - expired/invalid).
In angular,create a service layer (token refresher) which delegates every API calls to the server (internally using the http service).
The job of this service is to check the status of API response (if it is 498) and internally make an additional call to refresh the token.
The service can then re-initiate the original call with newly created token to get the response.
All the api services will call the token refresher to get the response.
On a broader level, token refresher is a wrapper over the default http service which performs the additional check.
This will avoid the annoying page loads and make the application faster.
EDIT - Example of HTTP Interceptor
import { Injectable } from "#angular/core";
import { XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Http, Headers } from "#angular/http";
import { Store } from "#ngrx/store";
import { Observable } from "rxjs/Rx";
import { Observer } from "rxjs/Observer";
import { Response as ApiResponse } from "../../models/base/response.model";
import { ToastModel } from "../../redux/app-reducers";
import { ReducerActions } from "../../redux/reducer-actions";
#Injectable()
export class HttpInterceptor extends Http {
constructor(private _XHRBackend: XHRBackend,
private _RequestOptions: RequestOptions,
private _ToastStore: Store<ToastModel>,
private _LoaderStore: Store<boolean>) {
super(_XHRBackend, _RequestOptions);
}
public request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
return this.handleResponse(super.request(url, options));
}
public get(url: string, options?: RequestOptionsArgs): Observable<Response> {
this.beforeRequest(url);
return super.get(url, this.getRequestOptionArgs(options));
}
public post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
this.beforeRequest(url, body);
return super.post(url, body, this.getRequestOptionArgs(options));
}
public put(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
this.beforeRequest(url, body);
return super.put(url, body, this.getRequestOptionArgs(options));
}
public delete(url: string, options?: RequestOptionsArgs): Observable<Response> {
this.beforeRequest(url);
return super.delete(url, this.getRequestOptionArgs(options));
}
private getRequestOptionArgs(options?: RequestOptionsArgs): RequestOptionsArgs {
if (options == null) {
options = new RequestOptions();
}
if (options.headers == null) {
options.headers = new Headers();
}
options.headers.append('Content-Type', 'application/json');
return options;
}
private handleResponse(response: Observable<Response>): Observable<Response> {
return response
.catch(this.onCatch)
.do(this.onSuccess.bind(this), this.onError.bind(this))
.finally(this.afterResponse.bind(this));
}
private beforeRequest(url: string, body?: string): void {
this._LoaderStore.dispatch({ type: ReducerActions.Loader.Set, payload: true });
}
private afterResponse(): void {
this._LoaderStore.dispatch({ type: ReducerActions.Loader.Set, payload: false });
}
private onCatch(error: any, caught: Observable<Response>): Observable<Response> {
console.log("interceptor catch called");
return Observable.throw(error);
}
private onSuccess(res: Response): void {
let response: ApiResponse<any> = res.json();
if (!response.message) {
return;
}
let toast: ToastModel = {
text: response.message,
duration: 5000,
type: "success"
};
this._ToastStore.dispatch({ type: ReducerActions.Toast.Update, payload: toast });
}
private onError(error: any): void {
let toast: ToastModel = {
text: "Error occurred!",
duration: 5000,
type: "failure"
};
this._ToastStore.dispatch({ type: ReducerActions.Toast.Update, payload: toast });
}
}
In the above example, handleResponse callback is the hook to do anything you want. (In this case, token refresh API call).
I hope this helps. :)
I am using Angular 2.0 to write a custom HTTP Provider which allows me to attach a bearer token to each HTTP Request to the API. This is essentially what ADAL JS does, but I can not use that library in my application.
The problem is this - before I make a call to my HTTP API, I need to wait unit both tokens are present in session storage. Once I have both, I can then send the request.
My HTTP Client class looks like this:
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
function getWindow(): any {
return window;
}
#Injectable()
export class HttpClient {
private _window: Window;
constructor(private http: Http) { }
createAuthorizationHeader(headers: Headers) {
let keys = sessionStorage.getItem("adal.token.keys").split("|");
let key1 = keys[0]; // web
let key2 = keys[1]; // api
if (!key1 || !key2) {
// I NEED TO WAIT FOR BOTH KEYS!
}
let accessToken = sessionStorage.getItem("adal.access.token.key" + key2);
headers.append('Authorization', 'Bearer ' + accessToken);
}
get(url) {
let headers = new Headers();
this.createAuthorizationHeader(headers);
return this.http.get(url, {
headers: headers
});
}
post(url, data) {
let headers = new Headers();
this.createAuthorizationHeader(headers);
return this.http.post(url, data, {
headers: headers
});
}
}
I would like to avoid using a Timer (setTimeout) for obvious reasons. I would like to use an ES6 promise type of thing.
This is going to be a long question.
For windows authentication to work with angular I have wrapper for the http calls as shown below
import { Injectable } from '#angular/core';
import { Http, Response, RequestOptions, Headers, RequestOptionsArgs } from '#angular/http';
import { Config } from '../_helpers/config';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class HTTPUtility {
public baseApiUrl: string;
constructor(private http: Http, private config: Config) {
this.baseApiUrl = this.config.getByKey('baseApiUrl') || '';
}
public getApiUrl(url) {
return this.baseApiUrl + url;
}
public get(url: string, options?: RequestOptions) {
if (!options) {
options = this.getDefaultHeaders();
}
return this.http.get(this.getApiUrl(url), options)
.catch(this.handleError);
}
private getDefaultHeaders() {
let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
headers.append('Accept', 'application/json');
return new RequestOptions({ headers, withCredentials: true });
}
public handleError(response: Response) {
return Observable.throw(JSON.parse(response.json().Message) || 'Server error');
}
}
If you observe new RequestOptions({ headers, withCredentials: true }); is allowing browser to send credentials to server for windows authentication.
And it's working great for everything.
Now coming to the issue, I have sampleComponent in which i'm using ServerDataSource as shown below:
import { Component, OnInit, NgZone } from '#angular/core';
import { Http, RequestOptions } from '#angular/http';
import { Ng2SmartTableModule, ServerDataSource } from 'ng2-smart-table';
#Component({
selector: 'criteria',
templateUrl: './criteria.component.html',
styles: [require('./criteria.scss')],
})
export class SampleComponent implements OnInit {
Source: ServerDataSource;
settings: any;
constructor(
private http: Http) {
this.Source = new ServerDataSource(http, { endPoint: 'https://xxxxxx.org/yyy/GetCriterias'});
}
ngOnInit(): void {
// this.settings = {}// assigning the settings.
}
}
As you can see ServerDataSource is accepting Http instance and I have checked there documentation and haven't found any way to pass to RequestOptions. So the web api call made by ng2-smart-table fails with 401 status as credentials is not passed.
To resolve this issue I have made changes directly to ng2-smart-table source file to be specific 'server.data-source.js' and here is the change
ServerDataSource.prototype.createRequestOptions = function () {
var requestOptions = {withCredentials : true}; // this where I have added the withCredntials flag
requestOptions.params = new URLSearchParams();
requestOptions = this.addSortRequestOptions(requestOptions);
requestOptions = this.addFilterRequestOptions(requestOptions);
return this.addPagerRequestOptions(requestOptions);
};
With this change everything is working fine as of now.
But I could have issue in future, if we upgrade the package in that case I have to again make changes.
So if any one can help me to fix the issue in some other way please let me know.
Links: https://github.com/akveo/ng2-smart-table/blob/master/src/app/pages/examples/server/advanced-example-server.component.ts
Thanks.
i'm working on an Angular 2 Apllication where i have in my login feature this service.
import { Http, Response } from '#angular/http';
import {Injectable} from '#angular/core';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch';
import { Observable } from 'rxjs/Observable';
import { contentHeaders, apiUrl} from '../shared/headers';
#Injectable()
export class LoginService extends BaseService{
constructor(private http: Http){
super();
}
/**
* send the user login data (email, password) and the token back to be stored on the client side.
* #param user
* #returns {any|Promise}
*/
login(user: any): Observable<any>{
let body = JSON.stringify(user);
return this.http.post(apiUrl + '/login', body, { headers: contentHeaders })
.map(this.extractData)
.catch(this.handleError);
}
/**
* extract response data and return it to the component
* #param res
* #returns {*}
*/
public extractData(res: Response) {
let body = res.json();
console.log(body);
return body;
}
/**
* handle service error
* #param error
* #returns {ErrorObservable}
*/
public handleError(res: Response) {
return Observable.throw(res);
}
}
and i use it in my LoginComponent in this way
this.loginService.login(userObj)
.subscribe(
(response: any) => {
// success call that is Ok
},
(errorRes: any)=> {
console.log('res in err is', error);
}
-the result of console.log in my component is
TypeError: Observable_1.Observable.throw is not a function
i tried to search stackoverflow or in github if an issue solves this but i couldn't find soething that helps me, so if someone can help me handle the error in LoginComponent as a response from handleError method of the service and get the error message of the server in my component it will be great.
Note: the success part is working fine the problem is in the case of error when i make
return Observable.throw(res);
thanks in advance
You need to import Observable.throw().
Add this import statement:
import 'rxjs/add/observable/throw';
When ever throw is raised you should handle using catch operator as below
getTasks(): Observable<any[]> {
return this._http.get(this._url)
.map((response: Response) => <any[]>response.json())
.do(data => console.log("data we got is " + JSON.stringify(data)))
.catch(this.handleError);
}
private handleError(error: Response) {
console.log(error);
}
Also you are throwing again in your error Handler method which should not be as such
this.loginService.login(userObj)
.subscribe(
(response: any) => {
// success call that is Ok
},
(error)=> {
////////////////////////error message is available in this object
console.log('res in err is', error);
})
I would like to ensure that my JSON Web tokens are revoked/expire after a configurable ammount of time and i have the following set up:
Security Filter:
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import yourwebproject2.service.UserService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
/**
* #author: kameshr
*/
public class JWTTokenAuthFilter extends OncePerRequestFilter {
private static List<Pattern> AUTH_ROUTES = new ArrayList<>();
private static List<String> NO_AUTH_ROUTES = new ArrayList<>();
public static final String JWT_KEY = "JWT-TOKEN-SECRET";
static {
AUTH_ROUTES.add(Pattern.compile("/api/*"));
NO_AUTH_ROUTES.add("/api/user/authenticate");
NO_AUTH_ROUTES.add("/api/user/register");
}
private Logger LOG = LoggerFactory.getLogger(JWTTokenAuthFilter.class);
#Autowired
private UserService userService;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String authorizationHeader = request.getHeader("authorization");
String authenticationHeader = request.getHeader("authentication");
String route = request.getRequestURI();
// no auth route matching
boolean needsAuthentication = false;
for (Pattern p : AUTH_ROUTES) {
if (p.matcher(route).matches()) {
needsAuthentication = true;
break;
}
}
if(route.startsWith("/api/")) {
needsAuthentication = true;
}
if (NO_AUTH_ROUTES.contains(route)) {
needsAuthentication = false;
}
// Checking whether the current route needs to be authenticated
if (needsAuthentication) {
// Check for authorization header presence
String authHeader = null;
if (authorizationHeader == null || authorizationHeader.equalsIgnoreCase("")) {
if (authenticationHeader == null || authenticationHeader.equalsIgnoreCase("")) {
authHeader = null;
} else {
authHeader = authenticationHeader;
LOG.info("authentication: " + authenticationHeader);
}
} else {
authHeader = authorizationHeader;
LOG.info("authorization: " + authorizationHeader);
}
if (StringUtils.isBlank(authHeader) || !authHeader.startsWith("Bearer ")) {
throw new AuthCredentialsMissingException("Missing or invalid Authorization header.");
}
final String token = authHeader.substring(7); // The part after "Bearer "
try {
final Claims claims = Jwts.parser().setSigningKey(JWT_KEY)
.parseClaimsJws(token).getBody();
request.setAttribute("claims", claims);
// Now since the authentication process if finished
// move the request forward
filterChain.doFilter(request, response);
} catch (final Exception e) {
throw new AuthenticationFailedException("Invalid token. Cause:"+e.getMessage());
}
} else {
filterChain.doFilter(request, response);
}
}
}
Method that creates the Authentication token:
String token = Jwts.builder().setSubject(user.getEmail())
.claim("role", user.getRole().name()).setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, JWTTokenAuthFilter.JWT_KEY).compact();
authResp.put("token", token);
authResp.put("user", user);
Above i have all the claims that i am using on the JWT , i would like to request that the token is revoked after x ammount of time(of inactivity if possible).
How could i achieve this using JWT / Spring MVC / Angular JS / Spring Security
Set expiration for token
String token = Jwts.builder()
.setSubject(user.getEmail())
.claim("role", user.getRole().name())
.setIssuedAt(new Date())
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS256, WTTokenAuthFilter.JWT_KEY)
.compact();
Then, parseClaimsJws will raise ExpiredJwtException if currentTime>expirationDate
To revoke a valid token is a hard technique with no easy solutions:
1) Maintain a blacklist in server and compare for each request
2) Set a small expiration time and issue new token
3) Insert the login time in the token and compare if acomplish your criteria
4) remove the jwt in client side
Ser Invalidating client side JWT session