Compare commits

...

10 Commits

Author SHA1 Message Date
Mauro Mugnaini ee90e6e97f
Added JSON parsed instance of the refresh token to `JWTToken` class, the getter and methods to checks its presence and expiration 2024-06-05 10:31:55 +02:00
Mauro Mugnaini ccce7944ee
Moved from `maven-portal-bom` to `gcube-bom` 2024-05-08 13:56:23 +02:00
Mauro Mugnaini 83041827c4
Releasing `v1.3.2` 2024-04-24 10:59:46 +02:00
Mauro Mugnaini 098bfc9fef
Removed `permission` list from exchange method signature and improved it adding also `refresh-token` as requested token type and `offline_access` scope. Added also extra headers support for all the token methods. 2024-03-27 18:43:21 +01:00
Mauro Mugnaini 6da3a9f55a
Moved to `maven-portal-bom` `v3.7.0` and `openjdk11` 2024-03-26 11:40:54 +01:00
Mauro Mugnaini ed5ea15599 Merge pull request 'Porting `token_exchange` branch to `master`' (!1) from token_exchange into master
Reviewed-on: #1
2024-03-26 11:34:35 +01:00
Alfredo Oliviero 33a22f9448 rimosso header 2024-03-25 15:50:45 +01:00
Alfredo Oliviero 7c1597d479 Token exchange (#27099)
Header X-D4Science-Context in query exchange and refresh

- performQueryTokenWithPOST accepts also optional headers
- performURLEncodedPOSTSendData accepts also optional headers
- implemented queryExchangeToken
2024-03-22 15:43:59 +01:00
Mauro Mugnaini fccbedb299 Release of new version with addition of new `Catalogue-Manager` and `Catalogue-Moderator` roles to the enum of known roles (#23623) 2022-07-07 16:17:28 +02:00
Mauro Mugnaini fea71c720c Added new `Catalogue-Manager` and `Catalogue-Moderator` roles to the enum (#23623) 2022-07-06 12:54:44 +02:00
10 changed files with 270 additions and 48 deletions

View File

@ -3,23 +3,14 @@
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"> <classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
<attribute name="optional" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"> <classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/> <attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> <attribute name="optional" value="true"/>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="src" output="target/classes" path="src/main/java"> <classpathentry kind="src" output="target/classes" path="src/main/java">
@ -30,9 +21,20 @@
</classpathentry> </classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"> <classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes> <attributes>
<attribute name="test" value="true"/>
<attribute name="optional" value="true"/> <attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
<attribute name="test" value="true"/> </attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry kind="output" path="target/classes"/> <classpathentry kind="output" path="target/classes"/>

View File

@ -1,11 +1,11 @@
eclipse.preferences.version=1 eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=disabled org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8 org.eclipse.jdt.core.compiler.source=11

View File

@ -1,6 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0"> <?xml version="1.0" encoding="UTF-8"?><project-modules id="moduleCoreId" project-version="1.5.0">
<wb-module deploy-name="oidc-library"> <wb-module deploy-name="oidc-library">
<wb-resource deploy-path="/" source-path="/src/main/resources"/> <wb-resource deploy-path="/" source-path="/src/main/resources"/>
<wb-resource deploy-path="/" source-path="/src/main/java"/> <wb-resource deploy-path="/" source-path="/src/main/java"/>
</wb-module> </wb-module>
</project-modules> </project-modules>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<faceted-project> <faceted-project>
<installed facet="java" version="1.8"/>
<installed facet="jst.utility" version="1.0"/> <installed facet="jst.utility" version="1.0"/>
<installed facet="java" version="11"/>
</faceted-project> </faceted-project>

View File

@ -2,8 +2,21 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
# Changelog for "oidc-library" # Changelog for "oidc-library"
## [v1.3.3-SNAPSHOT]
Added JSON parsed instance of the refresh token to `JWTToken` class, the getter and methods to checks its presence and expiration
## [v1.3.2]
- Header X-D4Science-Context in query exchange and refresh
- Token exchange (#27099)
- PerformQueryTokenWithPOST accepts also optional headers
- PerformURLEncodedPOSTSendData accepts also optional headers
- Moved from `maven-portal-bom` to `gcube-bom`
## [v1.3.1]
- Added `Catalogue-Manager` and `Catalogue-Moderator` roles to the enum (#23623)
## [v1.3.0] ## [v1.3.0]
Added method to retrieve UMA token by using `clientId` and `clientSecret` in a specific `audience` (aka context) that can now be provided in both encoded and not encoded form (starts with "/" check is performed). - Added method to retrieve UMA token by using `clientId` and `clientSecret` in a specific `audience` (aka context) that can now be provided in both encoded and not encoded form (starts with "/" check is performed).
## [v1.2.1] ## [v1.2.1]
- "Data-Editor" role added (#20896), some logs changed to debug level - "Data-Editor" role added (#20896), some logs changed to debug level

18
pom.xml
View File

@ -7,20 +7,20 @@
<parent> <parent>
<artifactId>maven-parent</artifactId> <artifactId>maven-parent</artifactId>
<groupId>org.gcube.tools</groupId> <groupId>org.gcube.tools</groupId>
<version>1.1.0</version> <version>1.2.0</version>
<relativePath /> <relativePath />
</parent> </parent>
<groupId>org.gcube.common</groupId> <groupId>org.gcube.common</groupId>
<artifactId>oidc-library</artifactId> <artifactId>oidc-library</artifactId>
<version>1.3.0</version> <version>1.3.3-SNAPSHOT</version>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.gcube.distribution</groupId> <groupId>org.gcube.distribution</groupId>
<artifactId>maven-portal-bom</artifactId> <artifactId>gcube-bom</artifactId>
<version>3.6.3</version> <version>2.4.0</version>
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
@ -33,6 +33,14 @@
<url>https://code-repo.d4science.org/gCubeSystem/${project.artifactId}</url> <url>https://code-repo.d4science.org/gCubeSystem/${project.artifactId}</url>
</scm> </scm>
<properties>
<java.version>8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<slf4j-log4j12.version>1.6.4</slf4j-log4j12.version>
<log4j.version>1.2.16</log4j.version>
</properties>
<dependencies> <dependencies>
<dependency> <dependency>
@ -48,12 +56,14 @@
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId> <artifactId>slf4j-log4j12</artifactId>
<version>${slf4j-log4j12.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>log4j</groupId> <groupId>log4j</groupId>
<artifactId>log4j</artifactId> <artifactId>log4j</artifactId>
<version>${log4j.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>

View File

@ -12,6 +12,8 @@ public class D4ScienceMappings {
ACCOUNTING_MANAGER("Accounting-Manager"), ACCOUNTING_MANAGER("Accounting-Manager"),
CATALOGUE_ADMIN("Catalogue-Admin"), CATALOGUE_ADMIN("Catalogue-Admin"),
CATALOGUE_EDITOR("Catalogue-Editor"), CATALOGUE_EDITOR("Catalogue-Editor"),
CATALOGUE_MANAGER("Catalogue-Manager"),
CATALOGUE_MODERATOR("Catalogue-Moderator"),
DATA_EDITOR("Data-Editor"), DATA_EDITOR("Data-Editor"),
DATA_MANAGER("Data-Manager"), DATA_MANAGER("Data-Manager"),
DATAMINER_MANAGER("DataMiner-Manager"), DATAMINER_MANAGER("DataMiner-Manager"),
@ -49,23 +51,4 @@ public class D4ScienceMappings {
} }
public enum Scope {
BELONGS("belongs");
// TODO will be defined later
// LIST("list"),
// READ("read"),
// WRITE("write"),
// EXECUTE("execute");
private String str;
Scope(String str) {
this.str = str;
}
public String asString() {
return str;
}
}
} }

View File

@ -28,6 +28,7 @@ public class JWTToken implements Serializable {
private String raw; private String raw;
private JSONObject tokens; private JSONObject tokens;
private JSONObject payload; private JSONObject payload;
private JSONObject refreshToken;
public static JWTToken fromString(String tokenString) { public static JWTToken fromString(String tokenString) {
if (tokenString == null) { if (tokenString == null) {
@ -50,6 +51,11 @@ public class JWTToken implements Serializable {
tokens = (JSONObject) new JSONParser().parse(this.raw); tokens = (JSONObject) new JSONParser().parse(this.raw);
String[] parts = getAccessTokenString().split("\\."); String[] parts = getAccessTokenString().split("\\.");
payload = (JSONObject) new JSONParser().parse(new String(Base64.getDecoder().decode(parts[1]))); payload = (JSONObject) new JSONParser().parse(new String(Base64.getDecoder().decode(parts[1])));
String refreshTokenString = getRefreshTokenString();
if (refreshTokenString != null) {
refreshToken = (JSONObject) new JSONParser()
.parse(new String(Base64.getDecoder().decode(refreshTokenString.split("\\.")[1])));
}
} }
public String getRaw() { public String getRaw() {
@ -81,6 +87,14 @@ public class JWTToken implements Serializable {
return payload; return payload;
} }
public boolean hasRefreshToken() {
return refreshToken != null;
}
public JSONObject getRefreshToken() {
return refreshToken;
}
public String getAzp() { public String getAzp() {
return (String) getPayload().get("azp"); return (String) getPayload().get("azp");
} }
@ -89,10 +103,18 @@ public class JWTToken implements Serializable {
return (Long) getPayload().get("exp"); return (Long) getPayload().get("exp");
} }
public Long getRefreshTokenExp() {
return hasRefreshToken() ? (Long) getRefreshToken().get("exp") : 0;
}
public Date getExpAsDate() { public Date getExpAsDate() {
return new Date(getExp() * 1000); return new Date(getExp() * 1000);
} }
public Date getRefreshTokenExpAsDate() {
return new Date(getRefreshTokenExp() * 1000);
}
public Calendar getExpAsCalendar() { public Calendar getExpAsCalendar() {
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTime(getExpAsDate()); cal.setTime(getExpAsDate());
@ -103,6 +125,10 @@ public class JWTToken implements Serializable {
return new Date().after(getExpAsDate()); return new Date().after(getExpAsDate());
} }
public boolean isRefreshTokenExpired() {
return new Date().after(getRefreshTokenExpAsDate());
}
public List<String> getAud() { public List<String> getAud() {
List<String> audienceStrings = new ArrayList<>(); List<String> audienceStrings = new ArrayList<>();
Object audience = getPayload().get("aud"); Object audience = getPayload().get("aud");

View File

@ -71,6 +71,23 @@ public class OpenIdConnectRESTHelper {
public static JWTToken queryClientToken(String clientId, String clientSecret, URL tokenURL) public static JWTToken queryClientToken(String clientId, String clientSecret, URL tokenURL)
throws OpenIdConnectRESTHelperException { throws OpenIdConnectRESTHelperException {
return queryClientToken(clientId, clientSecret, tokenURL, null);
}
/**
* Queries from the OIDC server an OIDC access token, by using provided clientId and client secret.
*
* @param clientId the client id
* @param clientSecret the client secret
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param extraHeaders extra HTTP headers to add to the request (e.g. <code>X-D4Science-Context</code> custom header), may be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryClientToken(String clientId, String clientSecret, URL tokenURL,
Map<String, String> extraHeaders)
throws OpenIdConnectRESTHelperException {
Map<String, List<String>> params = new HashMap<>(); Map<String, List<String>> params = new HashMap<>();
params.put("grant_type", Arrays.asList("client_credentials")); params.put("grant_type", Arrays.asList("client_credentials"));
try { try {
@ -83,28 +100,41 @@ public class OpenIdConnectRESTHelper {
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
logger.error("Cannot URL encode 'client_secret'", e); logger.error("Cannot URL encode 'client_secret'", e);
} }
return performQueryTokenWithPOST(tokenURL, null, params); return performQueryTokenWithPOST(tokenURL, null, params, extraHeaders);
} }
public static JWTToken queryToken(String clientId, URL tokenURL, String code, String scope, public static JWTToken queryToken(String clientId, URL tokenURL, String code, String scope,
String redirectURI) throws Exception { String redirectURI) throws Exception {
return queryToken(clientId, tokenURL, code, scope, redirectURI, null);
}
public static JWTToken queryToken(String clientId, URL tokenURL, String code, String scope,
String redirectURI, Map<String, String> extraHeaders) throws Exception {
Map<String, List<String>> params = new HashMap<>(); Map<String, List<String>> params = new HashMap<>();
params.put("client_id", Arrays.asList(URLEncoder.encode(clientId, "UTF-8"))); params.put("client_id", Arrays.asList(URLEncoder.encode(clientId, "UTF-8")));
params.put("grant_type", Arrays.asList("authorization_code")); params.put("grant_type", Arrays.asList("authorization_code"));
params.put("scope", Arrays.asList(URLEncoder.encode(scope, "UTF-8"))); params.put("scope", Arrays.asList(URLEncoder.encode(scope, "UTF-8")));
params.put("code", Arrays.asList(URLEncoder.encode(code, "UTF-8"))); params.put("code", Arrays.asList(URLEncoder.encode(code, "UTF-8")));
params.put("redirect_uri", Arrays.asList(URLEncoder.encode(redirectURI, "UTF-8"))); params.put("redirect_uri", Arrays.asList(URLEncoder.encode(redirectURI, "UTF-8")));
return performQueryTokenWithPOST(tokenURL, null, params); return performQueryTokenWithPOST(tokenURL, null, params, extraHeaders);
} }
protected static JWTToken performQueryTokenWithPOST(URL tokenURL, String authorization, protected static JWTToken performQueryTokenWithPOST(URL tokenURL, String authorization,
Map<String, List<String>> params) throws OpenIdConnectRESTHelperException { Map<String, List<String>> params) throws OpenIdConnectRESTHelperException {
return performQueryTokenWithPOST(tokenURL, authorization, params, null);
}
protected static JWTToken performQueryTokenWithPOST(URL tokenURL, String authorization,
Map<String, List<String>> params, Map<String, String> headers) throws OpenIdConnectRESTHelperException {
logger.debug("Querying access token from OIDC server with URL: {}", tokenURL); logger.debug("Querying access token from OIDC server with URL: {}", tokenURL);
StringBuilder sb; StringBuilder sb;
try { try {
HttpURLConnection httpURLConnection = performURLEncodedPOSTSendData(tokenURL, params, authorization); HttpURLConnection httpURLConnection = performURLEncodedPOSTSendData(tokenURL, params, authorization,
headers);
sb = new StringBuilder(); sb = new StringBuilder();
int httpResultCode = httpURLConnection.getResponseCode(); int httpResultCode = httpURLConnection.getResponseCode();
@ -144,6 +174,12 @@ public class OpenIdConnectRESTHelper {
protected static HttpURLConnection performURLEncodedPOSTSendData(URL url, Map<String, List<String>> params, protected static HttpURLConnection performURLEncodedPOSTSendData(URL url, Map<String, List<String>> params,
String authorization) throws IOException, ProtocolException, UnsupportedEncodingException { String authorization) throws IOException, ProtocolException, UnsupportedEncodingException {
return performURLEncodedPOSTSendData(url, params, authorization, null);
}
protected static HttpURLConnection performURLEncodedPOSTSendData(URL url, Map<String, List<String>> params,
String authorization, Map<String, String> headers)
throws IOException, ProtocolException, UnsupportedEncodingException {
HttpURLConnection con = (HttpURLConnection) url.openConnection(); HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("POST"); con.setRequestMethod("POST");
@ -155,6 +191,12 @@ public class OpenIdConnectRESTHelper {
logger.debug("Adding authorization header as: {}", authorization); logger.debug("Adding authorization header as: {}", authorization);
con.setRequestProperty("Authorization", authorization); con.setRequestProperty("Authorization", authorization);
} }
if (headers != null) {
for (String key : headers.keySet()) {
con.setRequestProperty(key, headers.get(key));
}
}
OutputStream os = con.getOutputStream(); OutputStream os = con.getOutputStream();
String queryString = mapToQueryString(params); String queryString = mapToQueryString(params);
@ -171,7 +213,7 @@ public class OpenIdConnectRESTHelper {
* @param tokenUrl the token endpoint {@link URL} of the OIDC server * @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param clientId the client id * @param clientId the client id
* @param clientSecret the client secret * @param clientSecret the client secret
* @param audience the audience (context) where to request the issuing of the ticket * @param audience the audience (context) where to request the issuing of the token (URLEncoded or not)
* @param permissions a list of permissions, can be <code>null</code> * @param permissions a list of permissions, can be <code>null</code>
* @return the issued token * @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details * @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
@ -179,9 +221,28 @@ public class OpenIdConnectRESTHelper {
public static JWTToken queryUMAToken(URL tokenUrl, String clientId, String clientSecret, String audience, public static JWTToken queryUMAToken(URL tokenUrl, String clientId, String clientSecret, String audience,
List<String> permissions) throws OpenIdConnectRESTHelperException { List<String> permissions) throws OpenIdConnectRESTHelperException {
return queryUMAToken(tokenUrl, clientId, clientSecret, audience, permissions, null);
}
/**
* Queries from the OIDC server an UMA token, by using provided clientId and client secret for the given audience
* (context), in URLEncoded form or not, and optionally a list of permissions.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param clientId the client id
* @param clientSecret the client secret
* @param audience the audience (context) where to request the issuing of the token (URLEncoded or not)
* @param permissions a list of permissions, can be <code>null</code>
* @param extraHeaders extra HTTP headers to add to the request (e.g. <code>X-D4Science-Context</code> custom header), may be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryUMAToken(URL tokenUrl, String clientId, String clientSecret, String audience,
List<String> permissions, Map<String, String> extraHeaders) throws OpenIdConnectRESTHelperException {
return queryUMAToken(tokenUrl, return queryUMAToken(tokenUrl,
"Basic " + Base64.getEncoder().encodeToString((clientId + ":" + clientSecret).getBytes()), "Basic " + Base64.getEncoder().encodeToString((clientId + ":" + clientSecret).getBytes()),
audience, permissions); audience, permissions, extraHeaders);
} }
/** /**
@ -190,7 +251,7 @@ public class OpenIdConnectRESTHelper {
* *
* @param tokenUrl the token endpoint {@link URL} of the OIDC server * @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param authorization the auth token (the access token URLEncoded by the "Bearer " string) * @param authorization the auth token (the access token URLEncoded by the "Bearer " string)
* @param audience the audience (context) where to request the issuing of the ticket (URLEncoded) * @param audience the audience (context) where to request the issuing of the token (URLEncoded or not)
* @param permissions a list of permissions, can be <code>null</code> * @param permissions a list of permissions, can be <code>null</code>
* @return the issued token * @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details * @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
@ -198,6 +259,24 @@ public class OpenIdConnectRESTHelper {
public static JWTToken queryUMAToken(URL tokenUrl, String authorization, String audience, public static JWTToken queryUMAToken(URL tokenUrl, String authorization, String audience,
List<String> permissions) throws OpenIdConnectRESTHelperException { List<String> permissions) throws OpenIdConnectRESTHelperException {
return queryUMAToken(tokenUrl, authorization, audience, permissions, null);
}
/**
* Queries from the OIDC server an UMA token, by using provided access token, for the given audience (context),
* in URLEncoded form or not, and optionally a list of permissions.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param authorization the auth token (the access token URLEncoded by the "Bearer " string)
* @param audience the audience (context) where to request the issuing of the token (URLEncoded or not)
* @param permissions a list of permissions, can be <code>null</code>
* @param extraHeaders extra HTTP headers to add to the request (e.g. <code>X-D4Science-Context</code> custom header), may be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryUMAToken(URL tokenUrl, String authorization, String audience,
List<String> permissions, Map<String, String> extraHeaders) throws OpenIdConnectRESTHelperException {
Map<String, List<String>> params = new HashMap<>(); Map<String, List<String>> params = new HashMap<>();
params.put("grant_type", Arrays.asList("urn:ietf:params:oauth:grant-type:uma-ticket")); params.put("grant_type", Arrays.asList("urn:ietf:params:oauth:grant-type:uma-ticket"));
if (audience.startsWith("/")) { if (audience.startsWith("/")) {
@ -223,7 +302,104 @@ public class OpenIdConnectRESTHelper {
} }
}).collect(Collectors.toList())); }).collect(Collectors.toList()));
} }
return performQueryTokenWithPOST(tokenUrl, authorization, params); return performQueryTokenWithPOST(tokenUrl, authorization, params, extraHeaders);
}
/**
* Queries from the OIDC server an exchanged token by using provided access token, optionally for the given audience (context)
* in URLEncoded form or not.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param authorization the auth token (the access token URLEncoded by the "Bearer " string)
* @param audience the audience (context) where to request the issuing of the token (URLEncoded or not), may be <code>null</code>
* @param clientId the client id
* @param clientSecret the client secret
* @param extraHeaders extra HTTP headers to add to the request (e.g. <code>X-D4Science-Context</code> custom header), may be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryExchangeToken(URL tokenUrl, String authorization, String audience, String client_id,
String client_secret, Map<String, String> extraHeaders) throws OpenIdConnectRESTHelperException {
return queryExchangeToken(tokenUrl, authorization, audience, client_id, client_secret,
"urn:ietf:params:oauth:token-type:access_token", null, extraHeaders);
}
/**
* Queries from the OIDC server an exchanged token by using provided access token, optionally for the given audience (context)
* in URLEncoded form or not.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param authorization the auth token (the access token URLEncoded by the "Bearer " string)
* @param audience the audience (context) where to request the issuing of the token (URLEncoded or not), may be <code>null</code>
* @param clientId the client id
* @param clientSecret the client secret
* @param withRefreshToken request also the refresh token (forced to <code>true</code> for offline requests)
* @param offline request a refresh token of offline type (TYP claim)
* @param extraHeaders extra HTTP headers to add to the request (e.g. <code>X-D4Science-Context</code> custom header), may be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryExchangeToken(URL tokenUrl, String authorization, String audience, String clientId,
String clientSecret, boolean withRefreshToken, boolean offline, Map<String, String> extraHeaders)
throws OpenIdConnectRESTHelperException {
return queryExchangeToken(tokenUrl, authorization, audience, clientId, clientSecret,
withRefreshToken || offline ? "urn:ietf:params:oauth:token-type:refresh_token"
: "urn:ietf:params:oauth:token-type:access_token",
offline ? "offline_access" : null, extraHeaders);
}
/**
* Queries from the OIDC server an exchanged token by using provided access token, optionally for the given audience (context)
* in URLEncoded form or not.
*
* @param tokenUrl the token endpoint {@link URL} of the OIDC server
* @param authorization the auth token (the access token URLEncoded by the "Bearer " string)
* @param audience the audience (context) where to request the issuing of the token (URLEncoded or not), may be <code>null</code>
* @param clientId the client id
* @param clientSecret the client secret
* @param requestedTokenType the requested token type (e.g. <code>urn:ietf:params:oauth:token-type:refresh_token</code> for refresh token)
* @param scope the optional scope to request (e.g. <code>offline_access</code> for an offline token)
* @param extraHeaders extra HTTP headers to add to the request (e.g. <code>X-D4Science-Context</code> custom header), may be <code>null</code>
* @return the issued token
* @throws OpenIdConnectRESTHelperException if an error occurs (also an unauthorized call), inspect the exception for details
*/
public static JWTToken queryExchangeToken(URL tokenUrl, String authorization, String audience, String clientId,
String clientSecret, String requestedTokenType, String scope, Map<String, String> extraHeaders)
throws OpenIdConnectRESTHelperException {
logger.info("Querying exchange token for context: " + audience);
Map<String, List<String>> params = new HashMap<>();
params.put("subject_token", Arrays.asList(authorization));
params.put("client_id", Arrays.asList(clientId));
params.put("client_secret", Arrays.asList(clientSecret));
params.put("grant_type", Arrays.asList("urn:ietf:params:oauth:grant-type:token-exchange"));
params.put("subject_token_type", Arrays.asList("urn:ietf:params:oauth:token-type:access_token"));
params.put("requested_token_type", Arrays.asList(requestedTokenType));
if (scope != null) {
params.put("scope", Arrays.asList(scope));
}
if (audience != null) {
if (audience.startsWith("/")) {
try {
logger.trace("Audience was provided in non URL encoded form, encoding it");
audience = URLEncoder.encode(audience, "UTF-8");
} catch (UnsupportedEncodingException e) {
logger.error("Cannot URL encode 'audience'", e);
}
}
try {
params.put("audience", Arrays.asList(URLEncoder.encode(audience, "UTF-8")));
} catch (UnsupportedEncodingException e) {
logger.error("Cannot URL encode 'audience'", e);
}
}
return performQueryTokenWithPOST(tokenUrl, null, params, extraHeaders);
} }
/** /**
@ -249,6 +425,7 @@ public class OpenIdConnectRESTHelper {
*/ */
public static JWTToken refreshToken(URL tokenURL, String clientId, JWTToken token) public static JWTToken refreshToken(URL tokenURL, String clientId, JWTToken token)
throws OpenIdConnectRESTHelperException { throws OpenIdConnectRESTHelperException {
return refreshToken(tokenURL, clientId, null, token); return refreshToken(tokenURL, clientId, null, token);
} }

View File

@ -18,6 +18,9 @@ public class JWTTokenTest {
Assert.assertNotNull(token.getAccessTokenString()); Assert.assertNotNull(token.getAccessTokenString());
Assert.assertNotNull(token.getSub()); Assert.assertNotNull(token.getSub());
Assert.assertNotNull(token.getExp()); Assert.assertNotNull(token.getExp());
Assert.assertTrue(token.isExpired());
Assert.assertNotNull(token.getRefreshToken());
Assert.assertTrue(token.isRefreshTokenExpired());
} }
} }