Skip to content

Instantly share code, notes, and snippets.

@kilgarenone
Last active October 24, 2017 06:32
Show Gist options
  • Save kilgarenone/e59e300084b58b4e33e87d16ef973087 to your computer and use it in GitHub Desktop.
Save kilgarenone/e59e300084b58b4e33e87d16ef973087 to your computer and use it in GitHub Desktop.
Angular2 + RxJS: Call refresh token api to get a new access token and then automatically retry the previous http request with the new access token
export interface RebuildRequest {
type: string;
args: {
path: string;
body?: string;
opt: RequestOptions;
}
};
// a http request method
get(): Observable<any> {
// build your headers and url here
return this.processObs(
this.http.get(path, opt),
{
type: 'get',
args: { path, opt }
});
}
/**
* Post-Process on Observable object
*
* @private
* @param {Observable<any>} obv
* @param {object} obj
* @returns {Observable<any>}
* @memberof ApiService
*/
private processObs(obv: Observable<any>, obj: object): Observable<any> {
return obv
.catch((err: Response) => this.errorHandler.refreshToken(err, obj)) // catch your 401 Unauthorized error
.map((res) => { // receives the original request's response
const data = res.json();
return data ? data.data : data || {};
})
.catch(this.errorHandler.processApiError.bind(this.errorHandler)); // catch original or retried request's non-401 errors
}
/**
* Call refresh token api to get and store the new access token,
* then return it as an observable back to the original stream
*
* @param {Response} err
* @param {{ type: string, args: { path: string, body?: string, opt: RequestOptions } }} obj
* @returns
* @memberof ErrorHandlerService
*/
refreshToken(err: Response, obj: RebuildRequest) {
if (err.status === 401) {
// construct your new request here
// call refresh token api and return the new access token
return this.sessionService.renewAccessToken()
.switchMap((newToken) => { // switch to your new request observable
// set with the new access token
obj.args.opt.headers.set('Authorization', 'bearer ' + newToken)
return this.http[obj.type](...Object.values(obj.args));
});
} else {
// throw other kind of errors to be caught by the second
// catch method in the processObs method.
return Observable.throw(err);
}
}
// handle your non-401 errors here
processApiError(response: Response) {
if (response.status === 500) {
// do stuff
return Observable.throw(response.statusText);
} else if (response.status >= 400 && response.status < 500) {
// do stuff
return Observable.throw(response.error_message)
} else {
// do stuff
return Observable.throw('Unknown Error');
}
}
renewAccessToken(): Observable<any> {
// if no refresh token is found in session storage,
// clean up and route to login page
if (!this.refreshToken) {
this.clear();
this.router.navigate([environment.loginRoute]);
return Observable.throw('No Refresh Token');
}
// construct the refresh token query parameter
const queries = new URLSearchParams();
queries.append('refreshToken', this.refreshToken);
const options = new RequestOptions({ params: queries });
// call the refresh token api
return this.http.get(
environment.apiUrl + OAuth.token,
options
).map((response) => {
// set the new access token and refresh token
this.token = response.json()['data'].access_token;
this.refreshToken = response.json()['data'].refresh_token;
return this.token;
}).catch((err) => {
// clear session and redirect to login if failed to get refresh token
this.clear();
this.router.navigate([environment.loginRoute]);
return Observable.throw(err);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment