Nre refresh token policy applied. Request for new token, invalidates the old ones.
This commit is contained in:
parent
94dee47ab3
commit
5574d602b9
|
@ -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>();
|
|
||||||
params.add(new BasicNameValuePair("token", authentication.getAccessTokenValue()));
|
|
||||||
params.add(new BasicNameValuePair("token_type_hint", "access_token"));
|
|
||||||
httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
|
|
||||||
|
|
||||||
HttpResponse resp = httpclient.execute(httppost);
|
|
||||||
System.out.println("status " + resp.getStatusLine().getStatusCode());
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Error deleting old refresh tokens.", e);
|
||||||
|
//TODO should I let user know?
|
||||||
}
|
}
|
||||||
|
request.setAttribute("display_refresh_token", "display:block");
|
||||||
|
response.sendRedirect("./personalToken");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteOldRefreshTokens(List<String> oldRefreshTokens, String accessToken) throws IOException {
|
||||||
|
HttpDelete httpDelete;
|
||||||
|
CloseableHttpClient httpclient = HttpClients.createDefault();
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
</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>
|
||||||
|
|
||||||
<!-- END OF CENTER SIDE -->
|
<!-- END OF CENTER SIDE -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue