Angular interceptor: refresh token (Observable) before handling source request - javascript

In my Angular application to avoid having to much request with invalid token to the backend, I wanted to check for every request first if the token is valid. If not refresh first the token and then emit the source request.
As the method to refresh a token is also a call to the backend (an observable), I have to concat those two observables.
As I know in the frontend when a token is expired, I don't want to send first the request, and wait that it fails and then handle the error.. I would like to refresh it before, and send this request just once, as it will be valid for the following request too...
I tried several ways to concat those two request, but I have always again issues with the return type.
This is what I could build for now:
Some logic to send the authentication request in case if the token is invalid. (Don't do that in case if the authentication is the request itself!
Also some logic to wait for an already ongoing refreshAuthentication request by storing it static in the class.
export class AuthHeaderInterceptor implements HttpInterceptor {
constructor() {}
static authenticationOngoing = false;
static authenticationObservable: Observable<boolean>;
public intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
/**
* check if the token is valid.
* If valid send request
* If invalid call "refreshAuthentication()", wait for result, and send request based on result...
*/
if (!this._isAuthenticationRequest(request) && !this.checkToken()) {
if (!AuthHeaderInterceptor.authenticationOngoing) {
// send request and wait for response
AuthHeaderInterceptor.authenticationOngoing = true;
AuthHeaderInterceptor.authenticationObservable = this.refreshAuthentication();
AuthHeaderInterceptor.authenticationObservable.subscribe(() => {
AuthHeaderInterceptor.authenticationOngoing = false;
// submit source request or reroute if invalid
});
} else {
AuthHeaderInterceptor.authenticationObservable.subscribe(() => {
// submit source request or reroute if invalid
});
}
}
return next.handle(request);
}
private checkToken() {
return this._configStore.getToken().getEndtime() > new Date().getTime() + 10000;
}
private _isAuthenticationRequest(request): boolean {
return request.url.endsWith('/api/authentication');
}
private refreshAuthentication(): Observable<boolean> {
// returns Observable (as it's doing another request). Value represents if its successfully.
}

Related

Nestjs What is the right order for #Body(), #Params(), #Req(), #Res()

I want to access the res object to send httpOnly cookies and need to validate body with DTO.
but every time i try to do it something go wrong what is the right order for these params?
There are no order.
Also, they are parameter decorator factories, not parameters.
There is no strict order that needs to be followed. Each controller method may use decorators for retrieving different things (see controller docs: https://docs.nestjs.com/controllers)
Example
Let's imagine you are building an endpoint for handling some kind of search using a POST request and a payload. Nest returns some results and sets cookies with the latest performed search timestamp.
That sounds like your requirements, right?
Add cookie parser to nest application
Make sure you followed cookies documentation and installed all dependencies together and configured cookie parser middleware: https://docs.nestjs.com/techniques/cookies
Search payload data transfer object (DTO)
import { IsInt, IsNotEmpty } from 'class-validator';
export class SearchBodyDto {
#IsNotEmpty()
searchPhrase: string;
#IsInt()
page = 1;
}
Controller method
import { Request, Response } from 'express';
import { Body, Controller, Post, Req, Res } from '#nestjs/common';
#Controller()
export class AppController {
#Post('/search')
async search(
#Body() body: SearchBodyDto,
#Req() req: Request,
// passthrough:true here leaves response handling to framework.
// Otherwise you would need to send response manually, like: `res.json({data})`
#Res({ passthrough: true }) res: Response,
) {
const currentTimestamp = new Date().toISOString();
// Save to cookies current timestamp of search.
res.cookie('lastSearch', currentTimestamp, { httpOnly: true });
return {
// Return last search timestamp and fallback to current timestamp.
lastSearch: req.cookies.lastSearch ?? currentTimestamp,
// Validated search phrase from DTO.
searchPhrase: body.searchPhrase,
// Page by default 1.
page: body.page,
// Some example search results.
searchResults: ['water', 'wind', 'earth'],
};
}
}
Result
Now when you do a request to the endpoint, you will see the latest search time in a response: postman example, and also that value will be set to 'lastSearch' cookie.
The payload still will be validated using decorators on DTO.

how to get the values that were sent from axios.post in the backend

I am using react for the frontend and springboot for the backend. I am not able to retrieve the data that I sent using axios in the backend. The first code below is the frontend where I make post and send 3 objects that I want to use in the backend. And the second code segment is the backend where I have post mapping but really confused on how to get those 3 objects that I sent from frontend. Also the user is where I have the getter and setters for name, message, and email so I want to set the data from frontend into those variables in User. I am somewhat new to springboot but I have some experience in connecting database to springboot but in this case I dont need to use database to store anything. The overall goal is for me to achieve a working contact form where users can submit comments/complaints about a webpage and it will direct those emails to me.
const info = {
name: "Test"
message: "This is comment for test",
email: "test#test.com
};
axios.post("http://localhost:8080/postgressApp/signup-success", info)
.then(response => {
if(response.data != null) {
this.setState({show:true});
setTimeout(() => this.setState({show:false}), 3000);
window.location.reload();
} else {
this.setState({show:false});
}
});
#RestController
#RequestMapping("/postgressApp")
#CrossOrigin(origins="http://localhost:3000")
public class RegistrationController {
private Logger logger = LoggerFactory.getLogger(RegistrationController.class);
#Autowired
private NotificationService notificationService;
#PostMapping("/signup-success")
public String signupSuccess(){
// create user
User user = new User();
// send a notification
try {
notificationService.sendNotificaitoin(user);
}catch( MailException e ){
// catch error
logger.info("Error Sending Email: " + e.getMessage());
}
return "Thank you for registering with us.";
}
}
Change your method signature like this:
...
#PostMapping("/signup-success")
public String signupSuccess(#RequestBody User user) {
...
}
#RequestBodyannotation tells Spring to bind the incoming http request's body to your type. It will check your request parameter keys and values, check the type you provided after the annotation, try to match parameter keys with the fields in your User class and then copy the values from request to your User instance.

How should I handle JWT token refresh in my Redux app?

Our Redux application use JWT tokens for authentication. The access_token expires every 15 minutes and the refresh_token expires every 30 days. Both of them are provided by our API every time you log in and stored in the browser's local storage. If a secure endpoint receives a request with an expired token, it returns a 401 HTTP error.
Unfortunately, I don't know how to proceed to handle the refresh process without having a negative impact on the user. From a technical point of view, here is what I would like to achieve:
Action creator calls the API with an expired token
Client receives a 401 HTTP error
Client triggers a function that calls the API to obtain a new token (by providing the refresh token).
If the call fails (refresh_token is expired), prompt the user the re-enter its credentials to re-obtain both tokens then re-attempt the original request.
If the call succeeds, re-attempt the original request.
I would like to have a function that would handle the refreshing process and that would be called in the error handling portion of the action creator.
Here is what I have tried so far:
export function handleError(dispatch, current_func, error, handling) {
if(error.response) {
if(error.response.status === 401 && readToken("token") !== null) {
return attemptTokenRefresh(dispatch, current_func)
}
if(error.response.status === 422 && readToken("token") === null) {
return attemptTokenRefresh(dispatch, current_func)
}
}
return(handling())
}
export function attemptTokenRefresh(dispatch, on_success) {
let token = readToken("refresh_token");
let instance = axios.create({
headers: {"Authorization": token}
});
instance.post("api/refresh").then(response => {
if (response.data["token"]) {
storeToken("token", response.data["token"]);
on_success();
}
}).catch(error => {
//TODO: Allow user to sign back (prevent wiping the state)
});
}
dispatch refers to the dispatch function provided by Redux
current_func refers to the action creator
error refers to the error returned by the API
handling refers to the error handling function for other types of errors
Any help would be greatly appreciated :)

Javascript using native browser authentication

I am currently developing a web-application using Angular2 with TypeScript (if that matters).
This application has to communicate with a webserver, which is asking for a digest authentication.
Until now i was using the native login prompt of the browsers, which is automatically showing, when the server returns a "401 unauthorized". The browser only asks for authentication once and automatically uses this username and password for future requests. So I don't have to take care about the authentication, the browser does everything for me.
Unfortunately now i have to create a custom login screen, as i have to implement some default actions, such as "register" or "reset passwort", which are ususally accessible from that screen.
As digest authentication is quite complex and the browser would allready do all the complex things for me I would like to continue using the browsers functionality, but without using it's login prompt.
So is it possible to use the browsers authentication functionality?
If it is possible, how can I set the username and the password it should use?
EDIT:
As someone wanted to close this question as "to broad", i'll try to add some more detail.
The web-application gets data from a restful webservice. This webservice requires digest authentication and responds with a 401, if you are using a wrong username or password.
As mentioned above, the browser automatically shows a login prompt, if he gets a 401 error. If you enter a valid login, the browser caches those values somewhere and automatically sets them for every future request.
Now i basicly want to replace the login prompt and programatically set the values the browser should use for the login.
I hope this helps to make the question clear.
Basically you have to write a HTTP decorator to intercept response code 401. Afterwards you add the Authentication header and replay the request.
import { Injectable } from '#angular/core';
import { Http, ConnectionBackend, RequestOptions, RequestOptionsArgs, Response, Headers } from '#angular/http';
import { Observable } from 'rxjs/Rx';
#Injectable()
export class CustomHttp extends Http {
/**
* intercept request
* #param {Observable<Response>} observable to use
* #param {string} url to request
* #returns {Observable<Response>} return value
* #private
*/
private _intercept(observable: Observable<Response>, url: string): Observable<Response> {
return observable.catch(error => {
if (error.status === 401) {
// add custom header
let headers = new Headers();
headers.append('Authentication', '<HEADER>');
// replay request with modified header
return this.get(url, new RequestOptions({
headers: headers
});
} else {
return Observable.throw(error);
}
});
};
/**
* constructor
* #param {ConnectionBackend} backend to use
* #param {RequestOptions} defaultOptions to use
* #returns {void} nothing
*/
constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
super(backend, defaultOptions);
};
/**
* get request
* #param {string} url to request
* #param {RequestOptionsArgs} options to use
* #returns {Observable<Response>} return value
*/
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
return this._intercept(super.get(url, options), url);
};
}
What didn’t worked for me yet is determining correct responses as there was a bug filed against Angular2 (https://github.com/angular/angular/pull/9355), which was merged just a few days before.
You have to increment the request counter for each valid request following the first successful one.
Maybe somebody else can show up with a working solution.

Protecting against CSRF attacks in Aurelia

In Aurelia, there doesn't seem to be any support for CSRF protection yet, as opposed to AngularJS's XSRF-TOKEN header which is set automatically on all XHR requests by the AngularJS framework.
How should I go about protecting an Aurelia app from CSRF attacks? Should I roll my own support based on the OWASP CSRF Prevention Cheat Sheet, or are there any alternatives out there for Aurelia already?
You should be able to do this yourself fairly easily by using Aurelia's HTTP interceptors (see examples in the docs). Before every request, you can send your token. This can be done with both the conventional aurelia-http-client and the new standard aurelia-fetch-client.
Your code might look like this:
export class MyRestAPI {
static inject () { return [HttpClient]; } // This could easily be fetch-client
constructor (http) {
this.http = http.configure(x => {
x.withBaseUrl(myBaseUrl);
x.useStandardConfiguration();
x.withInterceptor({
request: function (request) {
request.headers.set('XSRF-TOKEN', myAwesomeToken);
return request;
}
});
});
}
...
}
On every request, your token would be sent. You'd have to handle the validation on the server side. You could easily set up your code so that your initial request could grab a token, or you could pass a token back as part of your authentication payload, or if you wanted to you could even store a token in the browser's localstorage and use it that way.
You could even go a step further and implement JWT authentication. If you're using node.js, I have a small blog post that describes how I implemented JWT in Express. There's a plugin on Github called aurelia-auth that handles JWT, and there's a blog post on its implementation on the Aurelia blog as well.
Here is a sample interceptor that reads the token from the response header if it exists and sets it automatically on every request that needs it.
import {Interceptor, HttpResponseMessage, RequestMessage} from "aurelia-http-client";
class CsrfHeaderInterceptor implements Interceptor {
private static readonly TOKEN_HEADER = 'X-CSRF-Token';
private latestCsrfToken: string;
response(response: HttpResponseMessage): HttpResponseMessage {
if (response.headers.has(CsrfHeaderInterceptor.TOKEN_HEADER)) {
this.latestCsrfToken = response.headers.get(CsrfHeaderInterceptor.TOKEN_HEADER);
}
return response;
}
request(request: RequestMessage): RequestMessage {
if (this.latestCsrfToken) {
if (['POST', 'PUT', 'PATCH'].indexOf(request.method) >= 0) {
request.headers.add(CsrfHeaderInterceptor.TOKEN_HEADER, this.latestCsrfToken);
}
}
return request;
}
}
You register it in your http/fetch client with for example:
httpClient.configure((config) => {
config
.withBaseUrl("/api/") // adjust to your needs
.withHeader('Accept', 'application/json') // adjust to your needs
.withHeader('X-Requested-With', 'XMLHttpRequest') // adjust to your needs
.withInterceptor(new CsrfHeaderInterceptor());
});

Categories

Resources