简体   繁体   中英

XSRF - how to set cross-origin cookie

I have developed REST API, and two JavaScript clients (Single Page App & native app - based on electron). In both clients my users are authenticating via OAuth2 flow:

  1. sends user-password to server
  2. gets access_token (in plain text) and refresh_token (in httponly cookie)
  3. when token expires they are refreshing it sending request to /refresh endpoint (server reads refresh_token from cookie)

Now I would like to implement csrf protection. So I implemented it on my back-end side (Spring):

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()).and()
                .authorizeRequests().antMatchers("/", "/index.html", "/token/**").permitAll()
                .anyRequest().authenticated();
    }

My SPA works perfectly, angular reads XSRF-TOKEN from cookie and sends it in X-XSRF-TOKEN header. I got problem with electron app. It doesn't have access to cookie (because of different origin - electron is running on file:// url), so it is unable to set X-XSRF-TOKEN header.

How can I deal with such problem? Is there any way to instatiate "cross-origin" cookie? Or maybe I can somehow takie the cookie value via electron magic electron API (if it has access to file-system, maybe it have access to any cookie which is created on the machine)?

I had the same problem and eventually managed to fix it by implementing an angular http interceptor like this:

@Injectable()
export class HttpXsrfInterceptor implements HttpInterceptor {

  constructor(private tokenExtractor: HttpXsrfTokenExtractor, //from angular
              private nativeAuthService: NativeAuthService) { //my native service
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    //this works in web
    const headerName = 'X-XSRF-TOKEN';
    const token = this.tokenExtractor.getToken() as string;
    if (token !== null && !req.headers.has(headerName)) {
      req = req.clone({ headers: req.headers.set(headerName, token) });
      return next.handle(req);
    }

    //this works in electron
    return this.nativeAuthService.getCookieValue('XSRF-TOKEN').pipe(
      mergeMap((xsrfToken: string) => {
        if (xsrfToken && !req.headers.has(headerName)) {
          req = req.clone({ headers: req.headers.set(headerName, xsrfToken) });
        }
        return next.handle(req);
      }),
    );
  }
}

And my nativeAuthService basically just calls this method in the electron main process:

export function getCookieValue(cookieName: string): Observable<string> {
  return from(session.defaultSession.cookies.get({ name: cookieName })
    .then((cookies: Electron.Cookie[]) => {
      if (cookies.length == 1) {
        return cookies[0].value;
      } else {
        if (cookies.length > 1) {
          throw Error(`There is more than one cookie with the name: ${ cookieName }.`);
        }
        return '';
      }
    }));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM