-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for CSRF tokens to pass-data-client
- Loading branch information
1 parent
65592b8
commit 14c15f0
Showing
4 changed files
with
281 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
114 changes: 114 additions & 0 deletions
114
pass-data-client/src/main/java/org/eclipse/pass/support/client/OkHttpCsrfInterceptor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package org.eclipse.pass.support.client; | ||
|
||
import java.io.IOException; | ||
import java.util.function.Supplier; | ||
|
||
import okhttp3.Call; | ||
import okhttp3.Interceptor; | ||
import okhttp3.Request; | ||
import okhttp3.Response; | ||
|
||
/** | ||
* Add CSRF token as a header to protected requests. The token is provided as a | ||
* cookie. Refresh the token as needed and attempt to handle access by multiple | ||
* threads. | ||
*/ | ||
public class OkHttpCsrfInterceptor implements Interceptor { | ||
private volatile String cached_csrf_token; | ||
private Supplier<Call> csrf_token_call; | ||
|
||
/** | ||
* @param csrf_token_call Return a new call which will generate a CSRF token | ||
*/ | ||
public OkHttpCsrfInterceptor(Supplier<Call> csrf_token_call) { | ||
this.csrf_token_call = csrf_token_call; | ||
} | ||
|
||
private String get_csrf_token() throws IOException { | ||
if (cached_csrf_token == null) { | ||
refresh_csrf_token(); | ||
} | ||
|
||
return cached_csrf_token; | ||
} | ||
|
||
private void update_csrf_token(Response response) throws IOException { | ||
String token = parse_csrf_token(response); | ||
|
||
if (token != null) { | ||
cached_csrf_token = token; | ||
} | ||
} | ||
|
||
private String parse_csrf_token(Response response) { | ||
String prefix = "XSRF-TOKEN="; | ||
int start = prefix.length(); | ||
|
||
for (String c : response.headers("Set-Cookie")) { | ||
if (c.startsWith(prefix)) { | ||
int end = c.indexOf(';'); | ||
if (end == -1) { | ||
end = c.length() - 1; | ||
} | ||
|
||
return c.substring(start, end); | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
// Make a GET request to trigger a CSRF returned as a cookie | ||
private void refresh_csrf_token() throws IOException { | ||
try (Response response = csrf_token_call.get().execute()) { | ||
if (!response.isSuccessful()) { | ||
throw new IOException("Failed to make CSRF token request"); | ||
} | ||
|
||
update_csrf_token(response); | ||
} | ||
} | ||
|
||
private Response make_csrf_request(Chain chain) throws IOException { | ||
// Make sure that the CSRF header and cookie value match | ||
String token = get_csrf_token(); | ||
|
||
Request request = chain.request().newBuilder().header("X-XSRF-TOKEN", token) | ||
.header("Cookie", "XSRF-TOKEN=" + token).build(); | ||
|
||
Response response = chain.proceed(request); | ||
update_csrf_token(response); | ||
|
||
return response; | ||
} | ||
|
||
@Override | ||
public Response intercept(Chain chain) throws IOException { | ||
// GET is not protected | ||
if (chain.request().method().equals("GET")) { | ||
Response response = chain.proceed(chain.request()); | ||
update_csrf_token(response); | ||
return response; | ||
} | ||
|
||
Response response = make_csrf_request(chain); | ||
|
||
// If there is a 403, refresh the token and try again | ||
if (response.code() == 403) { | ||
response.close(); | ||
refresh_csrf_token(); | ||
response = make_csrf_request(chain); | ||
} | ||
|
||
return response; | ||
} | ||
|
||
/** | ||
* Set the cached CSRF token. | ||
* | ||
* @param token new token value | ||
*/ | ||
protected void setCsrfToken(String token) { | ||
cached_csrf_token = token; | ||
} | ||
} |
Oops, something went wrong.