Nre refresh token policy applied. Request for new token, invalidates the old ones.

This commit is contained in:
Katerina Iatropoulou 2020-10-19 12:49:19 +00:00
parent 94dee47ab3
commit 5574d602b9
3 changed files with 101 additions and 58 deletions

View File

@ -1,13 +1,18 @@
package eu.dnetlib.openaire.usermanagement; package eu.dnetlib.openaire.usermanagement;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders; import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair; import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair; import org.apache.http.message.BasicNameValuePair;
@ -16,7 +21,6 @@ import org.mitre.openid.connect.client.service.impl.StaticClientConfigurationSer
import org.mitre.openid.connect.model.OIDCAuthenticationToken; import org.mitre.openid.connect.model.OIDCAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.support.SpringBeanAutowiringSupport; import org.springframework.web.context.support.SpringBeanAutowiringSupport;
@ -26,11 +30,13 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.*;
import java.util.Base64;
import java.util.List;
public class PersonalTokenServlet extends HttpServlet { public class PersonalTokenServlet extends HttpServlet {
@ -41,6 +47,9 @@ public class PersonalTokenServlet extends HttpServlet {
@Value("${oidc.id}") @Value("${oidc.id}")
private String id; private String id;
@Value("${oidc.issuer}")
private String issuer;
@Autowired @Autowired
private StaticClientConfigurationService staticClientConfigurationService; private StaticClientConfigurationService staticClientConfigurationService;
@ -54,54 +63,65 @@ public class PersonalTokenServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { throws ServletException, IOException {
System.out.println("IN GET");
response.setContentType("text/html"); response.setContentType("text/html");
OIDCAuthenticationToken authentication = (OIDCAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); OIDCAuthenticationToken authentication = (OIDCAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
request.getSession().setAttribute("accessToken", authentication.getAccessTokenValue()); request.getSession().setAttribute("accessToken", authentication.getAccessTokenValue());
request.getSession().setAttribute("refreshToken", authentication.getRefreshTokenValue()); request.getSession().setAttribute("refreshToken", authentication.getRefreshTokenValue());
request.getRequestDispatcher("./personal.jsp").include(request, response); request.getRequestDispatcher("./personal.jsp").include(request, response);
} }
public void doPost(HttpServletRequest request, HttpServletResponse response) { public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("IN POST");
System.out.println(id);
System.out.println(secret);
OIDCAuthenticationToken authentication = (OIDCAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); OIDCAuthenticationToken authentication = (OIDCAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
String refreshToken = authentication.getRefreshTokenValue();
List<String> oldRefreshTokens = null;
try { try {
CloseableHttpClient httpclient = HttpClients.createDefault(); oldRefreshTokens = getOldRefreshTokens(authentication.getRefreshTokenValue(), authentication.getAccessTokenValue());
HttpPost httppost = new HttpPost("https://openaire-dev.aai-dev.grnet.gr/oidc/revoke"); deleteOldRefreshTokens(oldRefreshTokens, authentication.getAccessTokenValue());
httppost.setHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
String encoding = Base64.getEncoder().encodeToString((id.concat(":").concat(secret)).getBytes("UTF-8"));
httppost.setHeader(HttpHeaders.AUTHORIZATION, "Basic " + encoding);
List<NameValuePair> params = new ArrayList<NameValuePair>(); } catch (IOException e) {
params.add(new BasicNameValuePair("token", authentication.getAccessTokenValue())); logger.error("Error deleting old refresh tokens.", e);
params.add(new BasicNameValuePair("token_type_hint", "access_token")); //TODO should I let user know?
httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8")); }
request.setAttribute("display_refresh_token", "display:block");
response.sendRedirect("./personalToken");
}
HttpResponse resp = httpclient.execute(httppost); private void deleteOldRefreshTokens(List<String> oldRefreshTokens, String accessToken) throws IOException {
System.out.println("status " + resp.getStatusLine().getStatusCode()); HttpDelete httpDelete;
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpEntity entity = resp.getEntity();
System.out.println("REVOKE " + IOUtils.toString(entity.getContent(), StandardCharsets.UTF_8.name()));
response.sendRedirect("./personalToken");
} catch (UnsupportedEncodingException uee) {
logger.error("Error in Base64 encoding.", uee);
request.getSession().setAttribute("message", "Unable to revoke your token. Please try again later");
System.out.println("ERROR >>>> " + uee.getMessage());
} catch (IOException ioe) {
logger.error("Error in Base64 encoding.", ioe);
request.getSession().setAttribute("message", "Unable to revoke your token. Please try again later");
System.out.println("ERROR >>>> " + ioe.getMessage());
for (String refreshTokenId:oldRefreshTokens) {
httpDelete = new HttpDelete(issuer + "/api/tokens/refresh/" + refreshTokenId);
httpDelete.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
HttpResponse response = httpclient.execute(httpDelete);
if (response.getStatusLine().getStatusCode()!=200) {
logger.warn("Could not delete old refresh tokens." + response.getStatusLine().getStatusCode());
System.out.println("Could not delete old refresh tokens." + response.getStatusLine().getStatusCode());//TODO should I throw exception?
}
} }
} }
private List<String> getOldRefreshTokens(String currentRefreshToken, String accessToken) throws IOException {
HttpGet httpGet = new HttpGet(issuer + "/api/tokens/refresh");
httpGet.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken);
CloseableHttpClient httpclient = HttpClients.createDefault();
String jsonResponse = IOUtils.toString(httpclient.execute(httpGet).getEntity().getContent(), StandardCharsets.UTF_8.name());
Gson gson = new Gson();
List<String> oldRefreshTokens = null;
for(RefreshToken refreshToken:gson.fromJson(jsonResponse, RefreshToken[].class)){
if (oldRefreshTokens == null) {
oldRefreshTokens = new ArrayList<>();
}
if (!refreshToken.getValue().equals(currentRefreshToken)) {
oldRefreshTokens.add(refreshToken.getId()+"");
}
}
System.out.println("Old refresh tokens... " + oldRefreshTokens);
return oldRefreshTokens;
}
} }

View File

@ -88,14 +88,15 @@
<h2 class="uk-h2 uk-margin-small-bottom">OpenAIRE APIs Authentication</h2> <h2 class="uk-h2 uk-margin-small-bottom">OpenAIRE APIs Authentication</h2>
<div class="uk-margin-top"> <div class="uk-margin-top">
The OpenAIRE APIs can be accessed over HTTPS both by authenticated and unauthenticated requests. The OpenAIRE APIs can be accessed over HTTPS both by authenticated and unauthenticated requests.
To achieve better rate limits you need to make authenticated requests. We support personal access tokens and service registration. To achieve <b>better rate limits</b> you need to make <b>authenticated requests</b>.
For more information please read the <a href="">documentation.</a> <p><span uk-icon="icon:info"></span> For more information please read the <a href="">documentation</a>.</p>
</div> </div>
<div class="uk-grid uk-child-width-1-2@m uk-child-width-1-1@s uk-margin-top uk-text-left uk-container uk-container-small uk-margin-auto"> <div class="uk-grid uk-child-width-1-2@m uk-child-width-1-1@s uk-margin-top uk-text-left uk-container uk-container-small uk-margin-auto">
<div class="uk-padding-small"> <div class="uk-padding-small">
<div class="uk-card uk-card-default uk-card-body"> <div class="uk-card uk-card-default uk-card-body">
<div class=""> <a class="uk-link uk-text-large" href="./personalToken"> Personal token</a></div> <div class=""> <a class="uk-link uk-text-large" href="./personalToken"> Personal token</a></div>
<div>mpla mpla mpla</div> <div>Get access to the OpenAIRE APIs with your personal access and refresh token.</div>
</div> </div>
@ -103,7 +104,7 @@
<div class="uk-padding-small"> <div class="uk-padding-small">
<div class="uk-card uk-card-default uk-card-body "> <div class="uk-card uk-card-default uk-card-body ">
<div class=""> <a class="uk-link uk-text-large" href="./registeredServices"> Registered Services</a></div> <div class=""> <a class="uk-link uk-text-large" href="./registeredServices"> Registered Services</a></div>
<div>mpla mpla mpla</div> <div>Register your services to get access to the OpenAIRE APIs.</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -124,32 +124,54 @@
<span id="server_error" class="uk-text-danger uk-text-small uk-float-left">${message}</span> <span id="server_error" class="uk-text-danger uk-text-small uk-float-left">${message}</span>
<c:remove var="message" scope="session" /> <c:remove var="message" scope="session" />
<form id="revoke" name="revoke" action="./personalToken" method="post"> <form id="revoke" name="revoke" action="./personalToken" method="post">
<a class=" uk-text-danger uk-float-right" title="Revoke access token" onClick="document.revoke.submit();"><span uk-icon="refresh" ></span></a> <!-- <a class=" uk-text-danger uk-float-right" title="Revoke access token" onClick="document.revoke.submit();"><span uk-icon="refresh" ></span></a> -->
<a class=" uk-float-right uk-margin-small-left" onclick="copy('accessToken')" title="Copy access token"><span uk-icon="icon:copy"></span></a> <a class=" uk-float-right uk-margin-small-left" onclick="copy('accessToken')" title="Copy access token"><span uk-icon="icon:copy"></span></a>
<div class="uk-h5">Your personal access token is</div> <div class="uk-h5">Your personal access token is</div>
<pre><code id="accessToken">${accessToken}</code></pre> <pre><code id="accessToken">${accessToken}</code></pre>
<span uk-icon="icon:info"></span> Your access token is <b>valid for an hour</b>.
</form> </form>
</div> </div>
<div>
<a class=" uk-text-danger uk-float-right" title="Revoke refresh token"><span uk-icon="refresh"></span></a>
<a class=" uk-float-right uk-margin-small-left" onclick="copy('refreshToken')" title="Copy refresh token"><span uk-icon="icon: copy"></span></a>
<div class="uk-h5">Your refresh token is</div>
<pre><code id="refreshToken">${refreshToken}</code></pre>
</div>
<div class="uk-alert-danger uk-alert uk-margin-large-top" > <div class="uk-alert-danger uk-alert uk-margin-large-top" >
<p><b>Do not share your personal access token. Send your personal access token only over HTTPS.</b></p> <p>Do not share your personal access token. Send your personal access token only over HTTPS.</p>
</div>
<div class="uk-alert-primary uk-alert" >
<span uk-icon="icon:info"></span> For further information on how to use the tokens please visit the <a href="">OpenAIRE API Authentication documentation</a>.
</div> </div>
<div class="uk-alert-primary uk-alert" >
For further information on how to use the tokens please visit the <a href="">OpenAIRE API Authentication documentation</a>.
</div>
<div> <div>
<div class="uk-h5">Revoke tokens</div> <!--<a class=" uk-text-danger uk-float-right" title="Revoke refresh token"><span uk-icon="refresh"></span></a>-->
<div>Mpla mpla mpla</div> <div class="uk-h5">Do you need a refresh token?</div>
<a class="uk-button uk-button-danger uk-margin-top"><span uk-icon="refresh"></span>Revoke</a> <p>OpenAIRE refresh token <b>expires after 1 month</b> and allows you to programmatically get a new access token. </p>
<button type="submit" class="uk-button uk-button-primary" uk-toggle="target: #refreshWarning">Get a refresh token</button>
<div id="refreshTokenDiv" style="${display_refresh_token}">
<c:remove var="display_refresh_token" scope="session"/>
<div class="uk-alert-warning uk-alert" >
Please copy your refresh token and store it confidentially. You will not be able to retrieve it.
</div>
<div class="uk-alert-danger uk-alert uk-margin-large-top" >
<p>Do not share your refresh token. Send your personal access token only over HTTPS.</p>
</div>
<a class="uk-float-right uk-margin-small-left" onclick="copy('refreshToken')" title="Copy refresh token"><span uk-icon="icon: copy"></span></a>
<pre><code id="refreshToken">${refreshToken}</code></pre>
</div>
</div> </div>
<!-- This is the modal -->
<div id="refreshWarning" uk-modal>
<div class="uk-modal-dialog uk-modal-body">
<form id="refreshForm" action="./personalToken" method="POST">
<h2 class="uk-modal-title">Get refresh token</h2>
<p>In case you already have a refresh token, it will no longer be valid. Do you want to proceed?</p>
<p class="uk-text-right">
<button class="uk-button uk-button-default uk-modal-close" type="button">Cancel</button>
<button class="uk-button uk-button-primary" type="button" onclick="submit();">Get refresh token</button>
</p>
</form>
</div>
</div>
</div> </div>
<!-- END OF CENTER SIDE --> <!-- END OF CENTER SIDE -->
</div> </div>
</div> </div>