diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..a9ae158608
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,14 @@
+# EditorConfig is awesome: http://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file
+[*]
+end_of_line = lf
+insert_final_newline = true
+
+# 4 space - Tab indentation
+[*.{java,xml,js,html}]
+indent_style = tab
+indent_size = 4
diff --git a/.gitignore b/.gitignore
index 016a3b8f82..e663c16b8f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
-local-values.conf
target
*~
bin
@@ -10,3 +9,4 @@ bin
.classpath
/target
.springBeans
+nb-configuration.xml
diff --git a/.travis.yml b/.travis.yml
index dff5f3a5d0..3d619c8263 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1 +1,11 @@
language: java
+jdk:
+ - oraclejdk11
+sudo: false
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
+
+cache:
+ directories:
+ - $HOME/.m2
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000000..96c6356a0c
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,43 @@
+Unreleased:
+
+*1.3.3*:
+- Authorization codes are now longer
+- Client/RS can parse the "sub" and "user_id" claims in introspection response
+- Database-direct queries for fetching tokens by user (optimization)
+- Device flow supports verification_uri_complete (must be turned on)
+- Long scopes display properly and are still checkable
+- Language system remebers when it can't find a file and stops throwing so many errors
+- Index added for refresh tokens
+- Updated to Spring Security 4.2.11
+- Updated Spring to 4.3.22
+- Change approve pages to use issuer instead of page context
+- Updated oracle database scripts
+
+*1.3.2*:
+- Added changelog
+- Set default redirect URI resolver strict matching to true
+- Fixed XSS vulnerability on redirect URI display on approval page
+- Removed MITRE from copyright
+- Disallow unsigned JWTs on client authentication
+- Upgraded Nimbus revision
+- Added French translation
+- Added hooks for custom JWT claims
+- Removed "Not Yet Implemented" tag from post-logout redirect URI
+
+*1.3.1*:
+- Added End Session endpoint
+- Fixed discovery endpoint
+- Downgrade MySQL connector dependency version from developer preview to GA release
+
+*1.3.0*:
+- Added device flow support
+- Added PKCE support
+- Modularized UI to allow better overlay and extensions
+- Modularized data import/export API
+- Added software statements to dynamic client registration
+- Added assertion processing framework
+- Removed ID tokens from storage
+- Removed structured scopes
+
+*1.2.6*:
+- Added strict HEART compliance mode
diff --git a/LICENSE.txt b/LICENSE.txt
index 1a04bce423..0e640e493b 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,8 +1,9 @@
-Copyright 2014 The MITRE Corporation
- and the MIT Kerberos and Internet Trust Consortium
+Copyright 2018 The MIT Internet Trust Consortium
+
+Portions copyright 2011-2013 The MITRE Corporation
Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
+you may not use this project except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
diff --git a/README.md b/README.md
index b9da9e863d..610579f550 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,11 @@
# MITREid Connect
---
-[](https://maven-badges.herokuapp.com/maven-central/org.mitre/openid-connect-parent) [](https://travis-ci.org/mitreid-connect/OpenID-Connect-Java-Spring-Server)
+[](https://maven-badges.herokuapp.com/maven-central/org.mitre/openid-connect-parent) [](https://travis-ci.org/mitreid-connect/OpenID-Connect-Java-Spring-Server) [](https://codecov.io/github/mitreid-connect/OpenID-Connect-Java-Spring-Server)
-This project contains an OpenID Connect reference implementation in Java on the Spring platform, including a functioning [server library](openid-connect-server), [deployable server package](openid-connect-server-webapp), [client (RP) library](openid-connect-client), and general [utility libraries](openid-connect-common). The server can be used as an OpenID Connect Identity Provider as well as a general-purpose OAuth 2.0 Authorization Server.
+This project contains a certified OpenID Connect reference implementation in Java on the Spring platform, including a functioning [server library](openid-connect-server), [deployable server package](openid-connect-server-webapp), [client (RP) library](openid-connect-client), and general [utility libraries](openid-connect-common). The server can be used as an OpenID Connect Identity Provider as well as a general-purpose OAuth 2.0 Authorization Server.
+
+[](https://openid.net/certification/)
More information about the project can be found:
@@ -23,9 +25,7 @@ The authors and key contributors of the project include:
* [Steve Moore](https://github.com/srmoore)
* [Mike Derryberry](https://github.com/mtderryberry)
* [William Kim](https://github.com/wikkim)
+* [Mark Janssen](https://github.com/praseodym)
-
-
-Copyright ©2014, [The MITRE Corporation](http://www.mitre.org/)
- and the [MIT Kerberos and Internet Trust Consortium](http://kit.mit.edu/). Licensed under the Apache 2.0 license, for details see `LICENSE.txt`.
+Licensed under the Apache 2.0 license, for details see `LICENSE.txt`.
diff --git a/README_zh_CN.md b/README_zh_CN.md
new file mode 100644
index 0000000000..4933b36836
--- /dev/null
+++ b/README_zh_CN.md
@@ -0,0 +1,38 @@
+# MITREid Connect
+---
+
+[](https://maven-badges.herokuapp.com/maven-central/org.mitre/openid-connect-parent) [](https://travis-ci.org/mitreid-connect/OpenID-Connect-Java-Spring-Server)
+
+此项目提供了一个业经认证的、用Java语言构筑于Spring平台之上的OpenID Connect参考实现,包括 [服务器端的实现库](openid-connect-server), [可部署的服务器包](openid-connect-server-webapp), [客户端 (RP) 的库](openid-connect-client), 以及 [工具类库](openid-connect-common)。该服务器可以用做OpenID Connect身份提供者,也可以用做一般意义上的OAuth 2.0授权服务器。
+
+[](https://openid.net/certification/)
+
+有关项目的更多信息参见:
+
+* [项目在GitHub上的主页 (及相关项目)](https://github.com/mitreid-connect/)
+* [完整的文档](https://github.com/mitreid-connect/OpenID-Connect-Java-Spring-Server/wiki)
+* [Maven文档及Java API](http://mitreid-connect.github.com/)
+* [问题(Issue)追踪系统 (用于报告bug及提交支持请求)](https://github.com/mitreid-connect/OpenID-Connect-Java-Spring-Server/issues)
+* 项目的邮件列表: `mitreid-connect@mit.edu`, 及其 [在线存档](https://mailman.mit.edu/mailman/listinfo/mitreid-connect).
+
+
+项目的作者及主要贡献者有:
+
+* [Justin Richer](https://github.com/jricher/)
+* [Amanda Anganes](https://github.com/aanganes/)
+* [Michael Jett](https://github.com/jumbojett/)
+* [Michael Walsh](https://github.com/nemonik/)
+* [Steve Moore](https://github.com/srmoore)
+* [Mike Derryberry](https://github.com/mtderryberry)
+* [William Kim](https://github.com/wikkim)
+* [Mark Janssen](https://github.com/praseodym)
+
+
+项目的中文译者:
+
+* [刘晓曦](https://github.com/liouxiao/)
+
+
+
+
+版权所有 ©2018 [MIT因特网信任联盟](http://www.mit-trust.org/). 采用Apache 2.0许可证, 详见 `LICENSE.txt`.
diff --git a/checkstyle.xml b/checkstyle.xml
index 7b96bdff67..06129daddb 100644
--- a/checkstyle.xml
+++ b/checkstyle.xml
@@ -1,20 +1,21 @@
+ Copyright 2018 The MIT Internet Trust Consortium
+
+ Portions copyright 2011-2013 The MITRE Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
diff --git a/docs/OAuth2.0_Diagrams.pdf b/docs/OAuth2.0_Diagrams.pdf
deleted file mode 100755
index 55b525e92b..0000000000
Binary files a/docs/OAuth2.0_Diagrams.pdf and /dev/null differ
diff --git a/docs/OpenID_Connect_Diagrams.pdf b/docs/OpenID_Connect_Diagrams.pdf
deleted file mode 100644
index a8da67ec49..0000000000
Binary files a/docs/OpenID_Connect_Diagrams.pdf and /dev/null differ
diff --git a/openid-connect-client/README.md b/openid-connect-client/README.md
index ba97fe29c1..5bddcdb6ed 100644
--- a/openid-connect-client/README.md
+++ b/openid-connect-client/README.md
@@ -2,7 +2,7 @@
## Overview ##
-This project contains an OpenID Connect Client implemented as a Spring Security AuthenticationFilter. The client facilitates a user's authentication into the secured application to an OpenID Connect Java Spring Server following the OpenID Connect Standard protocol.
+This project contains an OpenID Connect Client implemented as a Spring Security AuthenticationFilter. The client facilitates a user's authentication into the secured application to an OpenID Connect Server following the OpenID Connect standard protocol.
## Configuring ##
diff --git a/openid-connect-client/pom.xml b/openid-connect-client/pom.xml
index 1bb2c578b0..3fbbd9e5ec 100644
--- a/openid-connect-client/pom.xml
+++ b/openid-connect-client/pom.xml
@@ -1,75 +1,76 @@
+
+ Copyright 2018 The MIT Internet Trust Consortium
+
+ Portions copyright 2011-2013 The MITRE Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
- 4.0.0
-
- openid-connect-parent
- org.mitre
- 1.1.10-SNAPSHOT
- ..
-
- openid-connect-client
- OpenID Connect Client filter for Spring Security
- OpenID Connect Client
-
-
- org.mitre
- openid-connect-common
- 1.1.10-SNAPSHOT
-
-
- jar
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- ${java-version}
- ${java-version}
-
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
-
-
- attach-sources
-
- jar
-
-
-
-
-
-
+ 4.0.0
+
+ openid-connect-parent
+ org.mitre
+ 1.3.5-SNAPSHOT
+ ..
+
+ openid-connect-client
+ OpenID Connect Client filter for Spring Security
+ OpenID Connect Client
+
+
+ org.mitre
+ openid-connect-common
+
+
+ jar
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${java-version}
+ ${java-version}
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java
index dd35f4edf5..b311a84d9c 100644
--- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java
+++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/IntrospectingTokenService.java
@@ -1,25 +1,27 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.oauth2.introspectingfilter;
import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.SECRET_BASIC;
import java.io.IOException;
import java.net.URI;
+import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -27,7 +29,7 @@
import java.util.Set;
import org.apache.http.client.HttpClient;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.mitre.oauth2.introspectingfilter.service.IntrospectionAuthorityGranter;
import org.mitre.oauth2.introspectingfilter.service.IntrospectionConfigurationService;
import org.mitre.oauth2.introspectingfilter.service.impl.SimpleIntrospectionAuthorityGranter;
@@ -67,22 +69,49 @@ public class IntrospectingTokenService implements ResourceServerTokenServices {
private IntrospectionConfigurationService introspectionConfigurationService;
private IntrospectionAuthorityGranter introspectionAuthorityGranter = new SimpleIntrospectionAuthorityGranter();
- private HttpClient httpClient = new SystemDefaultHttpClient();
- private HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
+ private int defaultExpireTime = 300000; // 5 minutes in milliseconds
+ private boolean forceCacheExpireTime = false; // force removal of cached tokens based on default expire time
+ private boolean cacheNonExpiringTokens = false;
+ private boolean cacheTokens = true;
+
+ private HttpComponentsClientHttpRequestFactory factory;
+
+ public IntrospectingTokenService() {
+ this(HttpClientBuilder.create().useSystemProperties().build());
+ }
+
+ public IntrospectingTokenService(HttpClient httpClient) {
+ this.factory = new HttpComponentsClientHttpRequestFactory(httpClient);
+ }
// Inner class to store in the hash map
private class TokenCacheObject {
OAuth2AccessToken token;
OAuth2Authentication auth;
+ Date cacheExpire;
private TokenCacheObject(OAuth2AccessToken token, OAuth2Authentication auth) {
this.token = token;
this.auth = auth;
+
+ // we don't need to check the cacheTokens values, because this won't actually be added to the cache if cacheTokens is false
+ // if the token isn't null we use the token expire time
+ // if forceCacheExpireTime is also true, we also make sure that the token expire time is shorter than the default expire time
+ if ((this.token.getExpiration() != null) && (!forceCacheExpireTime || (forceCacheExpireTime && (this.token.getExpiration().getTime() - System.currentTimeMillis() <= defaultExpireTime)))) {
+ this.cacheExpire = this.token.getExpiration();
+ } else { // if the token doesn't have an expire time, or if the using forceCacheExpireTime the token expire time is longer than the default, then use the default expire time
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.MILLISECOND, defaultExpireTime);
+ this.cacheExpire = cal.getTime();
+ }
}
}
- private Map authCache = new HashMap();
- private static Logger logger = LoggerFactory.getLogger(IntrospectingTokenService.class);
+ private Map authCache = new HashMap<>();
+ /**
+ * Logger for this class
+ */
+ private static final Logger logger = LoggerFactory.getLogger(IntrospectingTokenService.class);
/**
* @return the introspectionConfigurationService
@@ -112,12 +141,84 @@ public IntrospectionAuthorityGranter getIntrospectionAuthorityGranter() {
return introspectionAuthorityGranter;
}
- // Check if there is a token and authentication in the cache
- // and check if it is not expired.
+ /**
+ * get the default cache expire time in milliseconds
+ * @return
+ */
+ public int getDefaultExpireTime() {
+ return defaultExpireTime;
+ }
+
+ /**
+ * set the default cache expire time in milliseconds
+ * @param defaultExpireTime
+ */
+ public void setDefaultExpireTime(int defaultExpireTime) {
+ this.defaultExpireTime = defaultExpireTime;
+ }
+
+ /**
+ * check if forcing a cache expire time maximum value
+ * @return the forceCacheExpireTime setting
+ */
+ public boolean isForceCacheExpireTime() {
+ return forceCacheExpireTime;
+ }
+
+ /**
+ * set forcing a cache expire time maximum value
+ * @param forceCacheExpireTime
+ */
+ public void setForceCacheExpireTime(boolean forceCacheExpireTime) {
+ this.forceCacheExpireTime = forceCacheExpireTime;
+ }
+
+ /**
+ * Are non-expiring tokens cached using the default cache time
+ * @return state of cacheNonExpiringTokens
+ */
+ public boolean isCacheNonExpiringTokens() {
+ return cacheNonExpiringTokens;
+ }
+
+ /**
+ * should non-expiring tokens be cached using the default cache timeout
+ * @param cacheNonExpiringTokens
+ */
+ public void setCacheNonExpiringTokens(boolean cacheNonExpiringTokens) {
+ this.cacheNonExpiringTokens = cacheNonExpiringTokens;
+ }
+
+ /**
+ * Is the service caching tokens, or is it hitting the introspection end point every time
+ * @return true is caching tokens locally, false hits the introspection end point every time
+ */
+ public boolean isCacheTokens() {
+ return cacheTokens;
+ }
+
+ /**
+ * Configure if the client should cache tokens locally or not
+ * @param cacheTokens
+ */
+ public void setCacheTokens(boolean cacheTokens) {
+ this.cacheTokens = cacheTokens;
+ }
+
+ /**
+ * Check to see if the introspection end point response for a token has been cached locally
+ * This call will return the token if it has been cached and is still valid according to
+ * the cache expire time on the TokenCacheObject. If a cached value has been found but is
+ * expired, either by default expire times or the token's own expire time, then the token is
+ * removed from the cache and null is returned.
+ * @param key is the token to check
+ * @return the cached TokenCacheObject or null
+ */
private TokenCacheObject checkCache(String key) {
- if (authCache.containsKey(key)) {
+ if (cacheTokens && authCache.containsKey(key)) {
TokenCacheObject tco = authCache.get(key);
- if (tco.token.getExpiration().after(new Date())) {
+
+ if (tco != null && tco.cacheExpire != null && tco.cacheExpire.after(new Date())) {
return tco;
} else {
// if the token is expired, don't keep things around.
@@ -129,19 +230,27 @@ private TokenCacheObject checkCache(String key) {
private OAuth2Request createStoredRequest(final JsonObject token) {
String clientId = token.get("client_id").getAsString();
- Set scopes = new HashSet();
+ Set scopes = new HashSet<>();
if (token.has("scope")) {
scopes.addAll(OAuth2Utils.parseParameterList(token.get("scope").getAsString()));
}
- Map parameters = new HashMap();
+ Map parameters = new HashMap<>();
parameters.put("client_id", clientId);
parameters.put("scope", OAuth2Utils.formatParameterList(scopes));
OAuth2Request storedRequest = new OAuth2Request(parameters, clientId, null, true, scopes, null, null, null, null);
return storedRequest;
}
- private Authentication createAuthentication(JsonObject token) {
- return new PreAuthenticatedAuthenticationToken(token.get("sub").getAsString(), token, introspectionAuthorityGranter.getAuthorities(token));
+ private Authentication createUserAuthentication(JsonObject token) {
+ JsonElement userId = token.get("user_id");
+ if(userId == null) {
+ userId = token.get("sub");
+ if (userId == null) {
+ return null;
+ }
+ }
+
+ return new PreAuthenticatedAuthenticationToken(userId.getAsString(), token, introspectionAuthorityGranter.getAuthorities(token));
}
private OAuth2AccessToken createAccessToken(final JsonObject token, final String tokenString) {
@@ -149,10 +258,14 @@ private OAuth2AccessToken createAccessToken(final JsonObject token, final String
return accessToken;
}
- // Validate a token string against the introspection endpoint,
- // then parse it and store it in the local cache. Return true on
- // sucess, false otherwise.
- private boolean parseToken(String accessToken) {
+ /**
+ * Validate a token string against the introspection endpoint,
+ * then parse it and store it in the local cache if caching is enabled.
+ *
+ * @param accessToken Token to pass to the introspection endpoint
+ * @return TokenCacheObject containing authentication and token if the token was valid, otherwise null
+ */
+ private TokenCacheObject parseToken(String accessToken) {
// find out which URL to ask
String introspectionUrl;
@@ -162,14 +275,14 @@ private boolean parseToken(String accessToken) {
client = introspectionConfigurationService.getClientConfiguration(accessToken);
} catch (IllegalArgumentException e) {
logger.error("Unable to load introspection URL or client configuration", e);
- return false;
+ return null;
}
// Use the SpringFramework RestTemplate to send the request to the
// endpoint
String validatedToken = null;
RestTemplate restTemplate;
- MultiValueMap form = new LinkedMultiValueMap();
+ MultiValueMap form = new LinkedMultiValueMap<>();
final String clientId = client.getClientId();
final String clientSecret = client.getClientSecret();
@@ -199,12 +312,13 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE
validatedToken = restTemplate.postForObject(introspectionUrl, form, String.class);
} catch (RestClientException rce) {
logger.error("validateToken", rce);
+ return null;
}
if (validatedToken != null) {
// parse the json
JsonElement jsonRoot = new JsonParser().parse(validatedToken);
if (!jsonRoot.isJsonObject()) {
- return false; // didn't get a proper JSON object
+ return null; // didn't get a proper JSON object
}
JsonObject tokenResponse = jsonRoot.getAsJsonObject();
@@ -212,29 +326,31 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE
if (tokenResponse.get("error") != null) {
// report an error?
logger.error("Got an error back: " + tokenResponse.get("error") + ", " + tokenResponse.get("error_description"));
- return false;
+ return null;
}
if (!tokenResponse.get("active").getAsBoolean()) {
// non-valid token
logger.info("Server returned non-active token");
- return false;
+ return null;
}
// create an OAuth2Authentication
- OAuth2Authentication auth = new OAuth2Authentication(createStoredRequest(tokenResponse), createAuthentication(tokenResponse));
+ OAuth2Authentication auth = new OAuth2Authentication(createStoredRequest(tokenResponse), createUserAuthentication(tokenResponse));
// create an OAuth2AccessToken
OAuth2AccessToken token = createAccessToken(tokenResponse, accessToken);
- if (token.getExpiration().after(new Date())) {
+ if (token.getExpiration() == null || token.getExpiration().after(new Date())) {
// Store them in the cache
- authCache.put(accessToken, new TokenCacheObject(token, auth));
-
- return true;
+ TokenCacheObject tco = new TokenCacheObject(token, auth);
+ if (cacheTokens && (cacheNonExpiringTokens || token.getExpiration() != null)) {
+ authCache.put(accessToken, tco);
+ }
+ return tco;
}
}
- // If we never put a token and an authentication in the cache...
- return false;
+ // when the token is invalid for whatever reason
+ return null;
}
@Override
@@ -246,13 +362,9 @@ public OAuth2Authentication loadAuthentication(String accessToken) throws Authen
if (cacheAuth != null) {
return cacheAuth.auth;
} else {
- if (parseToken(accessToken)) {
- cacheAuth = authCache.get(accessToken);
- if (cacheAuth != null && (cacheAuth.token.getExpiration().after(new Date()))) {
- return cacheAuth.auth;
- } else {
- return null;
- }
+ cacheAuth = parseToken(accessToken);
+ if (cacheAuth != null) {
+ return cacheAuth.auth;
} else {
return null;
}
@@ -268,13 +380,9 @@ public OAuth2AccessToken readAccessToken(String accessToken) {
if (cacheAuth != null) {
return cacheAuth.token;
} else {
- if (parseToken(accessToken)) {
- cacheAuth = authCache.get(accessToken);
- if (cacheAuth != null && (cacheAuth.token.getExpiration().after(new Date()))) {
- return cacheAuth.token;
- } else {
- return null;
- }
+ cacheAuth = parseToken(accessToken);
+ if (cacheAuth != null) {
+ return cacheAuth.token;
} else {
return null;
}
diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/OAuth2AccessTokenImpl.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/OAuth2AccessTokenImpl.java
index 166747953a..723fcc54d0 100644
--- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/OAuth2AccessTokenImpl.java
+++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/OAuth2AccessTokenImpl.java
@@ -1,31 +1,27 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.oauth2.introspectingfilter;
-import java.text.DateFormat;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
@@ -37,26 +33,21 @@
public class OAuth2AccessTokenImpl implements OAuth2AccessToken {
- private JsonObject token;
+ private JsonObject introspectionResponse;
private String tokenString;
- private Set scopes = new HashSet();
+ private Set scopes = new HashSet<>();
private Date expireDate;
- public OAuth2AccessTokenImpl(JsonObject token, String tokenString) {
- this.token = token;
+ public OAuth2AccessTokenImpl(JsonObject introspectionResponse, String tokenString) {
+ this.setIntrospectionResponse(introspectionResponse);
this.tokenString = tokenString;
- if (token.get("scope") != null) {
- scopes = Sets.newHashSet(Splitter.on(" ").split(token.get("scope").getAsString()));
+ if (introspectionResponse.get("scope") != null) {
+ scopes = Sets.newHashSet(Splitter.on(" ").split(introspectionResponse.get("scope").getAsString()));
}
- DateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
- if (token.get("exp") != null) {
- try {
- expireDate = dateFormater.parse(token.get("exp").getAsString());
- } catch (ParseException ex) {
- Logger.getLogger(IntrospectingTokenService.class.getName()).log(Level.SEVERE, null, ex);
- }
+ if (introspectionResponse.get("exp") != null) {
+ expireDate = new Date(introspectionResponse.get("exp").getAsLong() * 1000L);
}
}
@@ -107,4 +98,20 @@ public String getValue() {
return tokenString;
}
+
+ /**
+ * @return the token
+ */
+ public JsonObject getIntrospectionResponse() {
+ return introspectionResponse;
+ }
+
+
+ /**
+ * @param token the token to set
+ */
+ public void setIntrospectionResponse(JsonObject token) {
+ this.introspectionResponse = token;
+ }
+
}
diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionAuthorityGranter.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionAuthorityGranter.java
index 4317c7cceb..d514bfbabe 100644
--- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionAuthorityGranter.java
+++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionAuthorityGranter.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.oauth2.introspectingfilter.service;
diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionConfigurationService.java
index 6b73029e43..fe85727b5b 100644
--- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionConfigurationService.java
+++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/IntrospectionConfigurationService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.oauth2.introspectingfilter.service;
diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/JWTParsingIntrospectionConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/JWTParsingIntrospectionConfigurationService.java
index ff80006970..b60179f604 100644
--- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/JWTParsingIntrospectionConfigurationService.java
+++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/JWTParsingIntrospectionConfigurationService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.oauth2.introspectingfilter.service.impl;
@@ -32,11 +33,11 @@
import com.nimbusds.jwt.JWTParser;
/**
- *
+ *
* Parses the incoming accesstoken as a JWT and determines the issuer based on
* the "iss" field inside the JWT. Uses the ServerConfigurationService to determine
* the introspection URL for that issuer.
- *
+ *
* @author jricher
*
*/
diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/ScopeBasedIntrospectionAuthoritiesGranter.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/ScopeBasedIntrospectionAuthoritiesGranter.java
new file mode 100644
index 0000000000..26bc7f11c7
--- /dev/null
+++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/ScopeBasedIntrospectionAuthoritiesGranter.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+
+package org.mitre.oauth2.introspectingfilter.service.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.mitre.oauth2.introspectingfilter.service.IntrospectionAuthorityGranter;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.common.util.OAuth2Utils;
+
+import com.google.gson.JsonObject;
+
+/**
+ * @author jricher
+ *
+ */
+public class ScopeBasedIntrospectionAuthoritiesGranter implements IntrospectionAuthorityGranter {
+
+ private List authorities = AuthorityUtils.createAuthorityList("ROLE_API");
+
+ /* (non-Javadoc)
+ * @see org.mitre.oauth2.introspectingfilter.IntrospectionAuthorityGranter#getAuthorities(net.minidev.json.JSONObject)
+ */
+ @Override
+ public List getAuthorities(JsonObject introspectionResponse) {
+ List auth = new ArrayList<>(getAuthorities());
+
+ if (introspectionResponse.has("scope") && introspectionResponse.get("scope").isJsonPrimitive()) {
+ String scopeString = introspectionResponse.get("scope").getAsString();
+ Set scopes = OAuth2Utils.parseParameterList(scopeString);
+ for (String scope : scopes) {
+ auth.add(new SimpleGrantedAuthority("OAUTH_SCOPE_" + scope));
+ }
+ }
+
+ return auth;
+ }
+
+ /**
+ * @return the authorities
+ */
+ public List getAuthorities() {
+ return authorities;
+ }
+
+ /**
+ * @param authorities the authorities to set
+ */
+ public void setAuthorities(List authorities) {
+ this.authorities = authorities;
+ }
+
+}
diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/SimpleIntrospectionAuthorityGranter.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/SimpleIntrospectionAuthorityGranter.java
index 2b38c50d57..45126f2463 100644
--- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/SimpleIntrospectionAuthorityGranter.java
+++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/SimpleIntrospectionAuthorityGranter.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.oauth2.introspectingfilter.service.impl;
@@ -28,9 +29,9 @@
import com.google.gson.JsonObject;
/**
- *
+ *
* Grants the same set of authorities no matter what's passed in.
- *
+ *
* @author jricher
*
*/
diff --git a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/StaticIntrospectionConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/StaticIntrospectionConfigurationService.java
index dfe8d2eb98..5aa370c415 100644
--- a/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/StaticIntrospectionConfigurationService.java
+++ b/openid-connect-client/src/main/java/org/mitre/oauth2/introspectingfilter/service/impl/StaticIntrospectionConfigurationService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.oauth2.introspectingfilter.service.impl;
@@ -23,10 +24,10 @@
import org.mitre.oauth2.model.RegisteredClient;
/**
- *
+ *
* Always provides the (configured) IntrospectionURL and RegisteredClient regardless
* of token. Useful for talking to a single, trusted authorization server.
- *
+ *
* @author jricher
*
*/
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java
new file mode 100644
index 0000000000..0fe0c7e714
--- /dev/null
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/AuthorizationEndpointException.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.mitre.openid.connect.client;
+
+import org.springframework.security.authentication.AuthenticationServiceException;
+
+public class AuthorizationEndpointException extends AuthenticationServiceException {
+
+ private static final long serialVersionUID = 6953119789654778380L;
+
+ private String error;
+
+ private String errorDescription;
+
+ private String errorURI;
+
+ public AuthorizationEndpointException(String error, String errorDescription, String errorURI) {
+ super("Error from Authorization Endpoint: " + error + " " + errorDescription + " " + errorURI);
+ this.error = error;
+ this.errorDescription = errorDescription;
+ this.errorURI = errorURI;
+ }
+
+ public String getError() {
+ return error;
+ }
+
+ public String getErrorDescription() {
+ return errorDescription;
+ }
+
+ public String getErrorURI() {
+ return errorURI;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return "AuthorizationEndpointException [error=" + error + ", errorDescription=" + errorDescription + ", errorURI=" + errorURI + "]";
+ }
+}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/NamedAdminAuthoritiesMapper.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/NamedAdminAuthoritiesMapper.java
index 0565332fa1..1d1e810f6a 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/NamedAdminAuthoritiesMapper.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/NamedAdminAuthoritiesMapper.java
@@ -1,67 +1,78 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client;
+import java.text.ParseException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
+import org.mitre.openid.connect.model.UserInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
-import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
+
+import com.nimbusds.jwt.JWT;
+import com.nimbusds.jwt.JWTClaimsSet;
/**
- *
+ *
* Simple mapper that adds ROLE_USER to the authorities map for all queries,
* plus adds ROLE_ADMIN if the subject and issuer pair are found in the
* configurable "admins" set.
- *
+ *
* @author jricher
- *
+ *
*/
-public class NamedAdminAuthoritiesMapper implements GrantedAuthoritiesMapper {
+public class NamedAdminAuthoritiesMapper implements OIDCAuthoritiesMapper {
+
+ private static Logger logger = LoggerFactory.getLogger(NamedAdminAuthoritiesMapper.class);
private static final SimpleGrantedAuthority ROLE_ADMIN = new SimpleGrantedAuthority("ROLE_ADMIN");
private static final SimpleGrantedAuthority ROLE_USER = new SimpleGrantedAuthority("ROLE_USER");
- private Set admins = new HashSet();
-
- private GrantedAuthoritiesMapper chain = new NullAuthoritiesMapper();
+ private Set admins = new HashSet<>();
@Override
- public Collection extends GrantedAuthority> mapAuthorities(Collection extends GrantedAuthority> authorities) {
+ public Collection extends GrantedAuthority> mapAuthorities(JWT idToken, UserInfo userInfo) {
+
+ Set out = new HashSet<>();
+ try {
+ JWTClaimsSet claims = idToken.getJWTClaimsSet();
- Set out = new HashSet();
- out.addAll(authorities);
+ SubjectIssuerGrantedAuthority authority = new SubjectIssuerGrantedAuthority(claims.getSubject(), claims.getIssuer());
+ out.add(authority);
- for (GrantedAuthority authority : authorities) {
if (admins.contains(authority)) {
out.add(ROLE_ADMIN);
}
- }
- // everybody's a user by default
- out.add(ROLE_USER);
+ // everybody's a user by default
+ out.add(ROLE_USER);
- return chain.mapAuthorities(out);
+ } catch (ParseException e) {
+ logger.error("Unable to parse ID Token inside of authorities mapper (huh?)");
+ }
+ return out;
}
/**
@@ -78,18 +89,4 @@ public void setAdmins(Set admins) {
this.admins = admins;
}
- /**
- * @return the chain
- */
- public GrantedAuthoritiesMapper getChain() {
- return chain;
- }
-
- /**
- * @param chain the chain to set
- */
- public void setChain(GrantedAuthoritiesMapper chain) {
- this.chain = chain;
- }
-
}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java
index d2c4c46e46..8412525471 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationFilter.java
@@ -1,19 +1,20 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.openid.connect.client;
import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.PRIVATE_KEY;
@@ -23,10 +24,14 @@
import java.io.IOException;
import java.math.BigInteger;
import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.text.ParseException;
import java.util.Date;
import java.util.Map;
+import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -34,10 +39,12 @@
import javax.servlet.http.HttpSession;
import org.apache.http.client.HttpClient;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
-import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
import org.mitre.jwt.signer.service.impl.JWKSetCacheService;
-import org.mitre.jwt.signer.service.impl.SymmetricCacheService;
+import org.mitre.jwt.signer.service.impl.SymmetricKeyJWTValidatorCacheService;
+import org.mitre.oauth2.model.PKCEAlgorithm;
import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.client.model.IssuerServiceResponse;
import org.mitre.openid.connect.client.service.AuthRequestOptionsService;
@@ -47,7 +54,7 @@
import org.mitre.openid.connect.client.service.ServerConfigurationService;
import org.mitre.openid.connect.client.service.impl.StaticAuthRequestOptionsService;
import org.mitre.openid.connect.config.ServerConfiguration;
-import org.mitre.openid.connect.model.OIDCAuthenticationToken;
+import org.mitre.openid.connect.model.PendingOIDCAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
@@ -59,10 +66,12 @@
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
-import org.springframework.web.client.HttpClientErrorException;
+import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.UriUtils;
import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -71,47 +80,61 @@
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.util.Base64;
+import com.nimbusds.jose.util.Base64URL;
import com.nimbusds.jwt.JWT;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.JWTParser;
import com.nimbusds.jwt.PlainJWT;
-import com.nimbusds.jwt.ReadOnlyJWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
/**
* OpenID Connect Authentication Filter class
- *
+ *
* @author nemonik, jricher
- *
+ *
*/
public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
protected final static String REDIRECT_URI_SESION_VARIABLE = "redirect_uri";
+ protected final static String CODE_VERIFIER_SESSION_VARIABLE = "code_verifier";
protected final static String STATE_SESSION_VARIABLE = "state";
protected final static String NONCE_SESSION_VARIABLE = "nonce";
protected final static String ISSUER_SESSION_VARIABLE = "issuer";
- protected static final String TARGET_SESSION_VARIABLE = "target";
+ protected final static String TARGET_SESSION_VARIABLE = "target";
protected final static int HTTP_SOCKET_TIMEOUT = 30000;
- protected final static String FILTER_PROCESSES_URL = "/openid_connect_login";
+ public final static String FILTER_PROCESSES_URL = "/openid_connect_login";
// Allow for time sync issues by having a window of X seconds.
private int timeSkewAllowance = 300;
- @Autowired
+ // fetches and caches public keys for servers
+ @Autowired(required=false)
private JWKSetCacheService validationServices;
+ // creates JWT signer/validators for symmetric keys
+ @Autowired(required=false)
+ private SymmetricKeyJWTValidatorCacheService symmetricCacheService;
+
+ // signer based on keypair for this client (for outgoing auth requests)
@Autowired(required=false)
- private SymmetricCacheService symmetricCacheService;
+ private JWTSigningAndValidationService authenticationSignerService;
@Autowired(required=false)
- private JwtSigningAndValidationService authenticationSignerService;
+ private HttpClient httpClient;
- // modular services to build out client filter
+ /*
+ * Modular services to build out client filter.
+ */
+ // looks at the request and determines which issuer to use for lookup on the server
+ private IssuerService issuerService;
+ // holds server information (auth URI, token URI, etc.), indexed by issuer
private ServerConfigurationService servers;
+ // holds client information (client ID, redirect URI, etc.), indexed by issuer of the server
private ClientConfigurationService clients;
- private IssuerService issuerService;
+ // provides extra options to inject into the outbound request
private AuthRequestOptionsService authOptions = new StaticAuthRequestOptionsService(); // initialize with an empty set of options
+ // builds the actual request URI based on input from all other services
private AuthRequestUrlBuilder authRequestBuilder;
// private helpers to handle target link URLs
@@ -123,7 +146,7 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi
/**
* OpenIdConnectAuthenticationFilter constructor
*/
- protected OIDCAuthenticationFilter() {
+ public OIDCAuthenticationFilter() {
super(FILTER_PROCESSES_URL);
targetSuccessHandler.passthrough = super.getSuccessHandler();
super.setAuthenticationSuccessHandler(targetSuccessHandler);
@@ -140,16 +163,16 @@ public void afterPropertiesSet() {
}
if (symmetricCacheService == null) {
- symmetricCacheService = new SymmetricCacheService();
+ symmetricCacheService = new SymmetricKeyJWTValidatorCacheService();
}
}
/*
* This is the main entry point for the filter.
- *
+ *
* (non-Javadoc)
- *
+ *
* @see org.springframework.security.web.authentication.
* AbstractAuthenticationProcessingFilter
* #attemptAuthentication(javax.servlet.http.HttpServletRequest,
@@ -182,7 +205,7 @@ public Authentication attemptAuthentication(HttpServletRequest request, HttpServ
/**
* Initiate an Authorization request
- *
+ *
* @param request
* The request from which to extract parameters and perform the
* authentication
@@ -216,8 +239,6 @@ protected void handleAuthorizationRequest(HttpServletRequest request, HttpServle
throw new AuthenticationServiceException("No issuer found: " + issuer);
}
- session.setAttribute(ISSUER_SESSION_VARIABLE, issuer);
-
ServerConfiguration serverConfig = servers.getServerConfiguration(issuer);
if (serverConfig == null) {
logger.error("No server configuration found for issuer: " + issuer);
@@ -225,6 +246,8 @@ protected void handleAuthorizationRequest(HttpServletRequest request, HttpServle
}
+ session.setAttribute(ISSUER_SESSION_VARIABLE, serverConfig.getIssuer());
+
RegisteredClient clientConfig = clients.getClientConfiguration(serverConfig);
if (clientConfig == null) {
logger.error("No client configuration found for issuer: " + issuer);
@@ -234,7 +257,7 @@ protected void handleAuthorizationRequest(HttpServletRequest request, HttpServle
String redirectUri = null;
if (clientConfig.getRegisteredRedirectUri() != null && clientConfig.getRegisteredRedirectUri().size() == 1) {
// if there's a redirect uri configured (and only one), use that
- redirectUri = clientConfig.getRegisteredRedirectUri().toArray(new String[] {})[0];
+ redirectUri = Iterables.getOnlyElement(clientConfig.getRegisteredRedirectUri());
} else {
// otherwise our redirect URI is this current URL, with no query parameters
redirectUri = request.getRequestURL().toString();
@@ -249,7 +272,27 @@ protected void handleAuthorizationRequest(HttpServletRequest request, HttpServle
Map options = authOptions.getOptions(serverConfig, clientConfig, request);
- String authRequest = authRequestBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options);
+ // if we're using PKCE, handle the challenge here
+ if (clientConfig.getCodeChallengeMethod() != null) {
+ String codeVerifier = createCodeVerifier(session);
+ options.put("code_challenge_method", clientConfig.getCodeChallengeMethod().getName());
+ if (clientConfig.getCodeChallengeMethod().equals(PKCEAlgorithm.plain)) {
+ options.put("code_challenge", codeVerifier);
+ } else if (clientConfig.getCodeChallengeMethod().equals(PKCEAlgorithm.S256)) {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ String hash = Base64URL.encode(digest.digest(codeVerifier.getBytes(StandardCharsets.US_ASCII))).toString();
+ options.put("code_challenge", hash);
+ } catch (NoSuchAlgorithmException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+
+ }
+ }
+
+ String authRequest = authRequestBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options, issResp.getLoginHint());
logger.debug("Auth Request: " + authRequest);
@@ -272,11 +315,9 @@ protected Authentication handleAuthorizationCodeResponse(HttpServletRequest requ
// check for state, if it doesn't match we bail early
String storedState = getStoredState(session);
- if (!Strings.isNullOrEmpty(storedState)) {
- String state = request.getParameter("state");
- if (!storedState.equals(state)) {
- throw new AuthenticationServiceException("State parameter mismatch on return. Expected " + storedState + " got " + state);
- }
+ String requestState = request.getParameter("state");
+ if (storedState == null || !storedState.equals(requestState)) {
+ throw new AuthenticationServiceException("State parameter mismatch on return. Expected " + storedState + " got " + requestState);
}
// look up the issuer that we set out to talk to
@@ -286,9 +327,15 @@ protected Authentication handleAuthorizationCodeResponse(HttpServletRequest requ
ServerConfiguration serverConfig = servers.getServerConfiguration(issuer);
final RegisteredClient clientConfig = clients.getClientConfiguration(serverConfig);
- MultiValueMap form = new LinkedMultiValueMap();
+ MultiValueMap form = new LinkedMultiValueMap<>();
form.add("grant_type", "authorization_code");
form.add("code", authorizationCode);
+ form.setAll(authOptions.getTokenOptions(serverConfig, clientConfig, request));
+
+ String codeVerifier = getStoredCodeVerifier(session);
+ if (codeVerifier != null) {
+ form.add("code_verifier", codeVerifier);
+ }
String redirectUri = getStoredSessionString(session, REDIRECT_URI_SESION_VARIABLE);
if (redirectUri != null) {
@@ -296,9 +343,15 @@ protected Authentication handleAuthorizationCodeResponse(HttpServletRequest requ
}
// Handle Token Endpoint interaction
- HttpClient httpClient = new SystemDefaultHttpClient();
- httpClient.getParams().setParameter("http.socket.timeout", new Integer(httpSocketTimeout));
+ if(httpClient == null) {
+ httpClient = HttpClientBuilder.create()
+ .useSystemProperties()
+ .setDefaultRequestConfig(RequestConfig.custom()
+ .setSocketTimeout(httpSocketTimeout)
+ .build())
+ .build();
+ }
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
@@ -312,9 +365,9 @@ protected Authentication handleAuthorizationCodeResponse(HttpServletRequest requ
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
ClientHttpRequest httpRequest = super.createRequest(url, method);
httpRequest.getHeaders().add("Authorization",
- String.format("Basic %s", Base64.encode(String.format("%s:%s", clientConfig.getClientId(), clientConfig.getClientSecret())) ));
-
-
+ String.format("Basic %s", Base64.encode(String.format("%s:%s",
+ UriUtils.encodePathSegment(clientConfig.getClientId(), "UTF-8"),
+ UriUtils.encodePathSegment(clientConfig.getClientSecret(), "UTF-8")))));
return httpRequest;
}
@@ -327,13 +380,13 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE
// do a symmetric secret signed JWT for auth
- JwtSigningAndValidationService signer = null;
+ JWTSigningAndValidationService signer = null;
JWSAlgorithm alg = clientConfig.getTokenEndpointAuthSigningAlg();
if (SECRET_JWT.equals(clientConfig.getTokenEndpointAuthMethod()) &&
- (alg.equals(JWSAlgorithm.HS256)
- || alg.equals(JWSAlgorithm.HS384)
- || alg.equals(JWSAlgorithm.HS512))) {
+ (JWSAlgorithm.HS256.equals(alg)
+ || JWSAlgorithm.HS384.equals(alg)
+ || JWSAlgorithm.HS512.equals(alg))) {
// generate one based on client secret
signer = symmetricCacheService.getSymmetricValidtor(clientConfig.getClient());
@@ -342,27 +395,35 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE
// needs to be wired in to the bean
signer = authenticationSignerService;
+
+ if (alg == null) {
+ alg = authenticationSignerService.getDefaultSigningAlgorithm();
+ }
}
if (signer == null) {
throw new AuthenticationServiceException("Couldn't find required signer service for use with private key auth.");
}
- JWTClaimsSet claimsSet = new JWTClaimsSet();
+ JWTClaimsSet.Builder claimsSet = new JWTClaimsSet.Builder();
- claimsSet.setIssuer(clientConfig.getClientId());
- claimsSet.setSubject(clientConfig.getClientId());
- claimsSet.setAudience(Lists.newArrayList(serverConfig.getTokenEndpointUri()));
+ claimsSet.issuer(clientConfig.getClientId());
+ claimsSet.subject(clientConfig.getClientId());
+ claimsSet.audience(Lists.newArrayList(serverConfig.getTokenEndpointUri()));
+ claimsSet.jwtID(UUID.randomUUID().toString());
// TODO: make this configurable
Date exp = new Date(System.currentTimeMillis() + (60 * 1000)); // auth good for 60 seconds
- claimsSet.setExpirationTime(exp);
+ claimsSet.expirationTime(exp);
Date now = new Date(System.currentTimeMillis());
- claimsSet.setIssueTime(now);
- claimsSet.setNotBeforeTime(now);
+ claimsSet.issueTime(now);
+ claimsSet.notBeforeTime(now);
- SignedJWT jwt = new SignedJWT(new JWSHeader(alg), claimsSet);
+ JWSHeader header = new JWSHeader(alg, null, null, null, null, null, null, null, null, null,
+ signer.getDefaultSignerKeyId(),
+ null, null);
+ SignedJWT jwt = new SignedJWT(header, claimsSet.build());
signer.signJwt(jwt, alg);
@@ -383,15 +444,13 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE
try {
jsonString = restTemplate.postForObject(serverConfig.getTokenEndpointUri(), form, String.class);
- } catch (HttpClientErrorException httpClientErrorException) {
+ } catch (RestClientException e) {
// Handle error
- logger.error("Token Endpoint error response: "
- + httpClientErrorException.getStatusText() + " : "
- + httpClientErrorException.getMessage());
+ logger.error("Token Endpoint error response: " + e.getMessage());
- throw new AuthenticationServiceException("Unable to obtain Access Token: " + httpClientErrorException.getMessage());
+ throw new AuthenticationServiceException("Unable to obtain Access Token: " + e.getMessage());
}
logger.debug("from TokenEndpoint jsonString = " + jsonString);
@@ -444,45 +503,45 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE
JWT idToken = JWTParser.parse(idTokenValue);
// validate our ID Token over a number of tests
- ReadOnlyJWTClaimsSet idClaims = idToken.getJWTClaimsSet();
+ JWTClaimsSet idClaims = idToken.getJWTClaimsSet();
// check the signature
- JwtSigningAndValidationService jwtValidator = null;
+ JWTSigningAndValidationService jwtValidator = null;
Algorithm tokenAlg = idToken.getHeader().getAlgorithm();
-
+
Algorithm clientAlg = clientConfig.getIdTokenSignedResponseAlg();
-
+
if (clientAlg != null) {
if (!clientAlg.equals(tokenAlg)) {
throw new AuthenticationServiceException("Token algorithm " + tokenAlg + " does not match expected algorithm " + clientAlg);
}
}
-
+
if (idToken instanceof PlainJWT) {
-
+
if (clientAlg == null) {
throw new AuthenticationServiceException("Unsigned ID tokens can only be used if explicitly configured in client.");
}
-
- if (tokenAlg != null && !tokenAlg.equals(JWSAlgorithm.NONE)) {
+
+ if (tokenAlg != null && !tokenAlg.equals(Algorithm.NONE)) {
throw new AuthenticationServiceException("Unsigned token received, expected signature with " + tokenAlg);
}
} else if (idToken instanceof SignedJWT) {
-
+
SignedJWT signedIdToken = (SignedJWT)idToken;
-
+
if (tokenAlg.equals(JWSAlgorithm.HS256)
- || tokenAlg.equals(JWSAlgorithm.HS384)
- || tokenAlg.equals(JWSAlgorithm.HS512)) {
-
+ || tokenAlg.equals(JWSAlgorithm.HS384)
+ || tokenAlg.equals(JWSAlgorithm.HS512)) {
+
// generate one based on client secret
jwtValidator = symmetricCacheService.getSymmetricValidtor(clientConfig.getClient());
} else {
// otherwise load from the server's public key
jwtValidator = validationServices.getValidator(serverConfig.getJwksUri());
}
-
+
if (jwtValidator != null) {
if(!jwtValidator.validateSignature(signedIdToken)) {
throw new AuthenticationServiceException("Signature validation failed");
@@ -556,13 +615,11 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE
+ "ID Token to the session " + NONCE_SESSION_VARIABLE + " failed. Expected " + storedNonce + " got " + nonce + ".");
}
- // pull the subject (user id) out as a claim on the id_token
-
- String userId = idClaims.getSubject();
+ // construct an PendingOIDCAuthenticationToken and return a Authentication object w/the userId and the idToken
- // construct an OIDCAuthenticationToken and return a Authentication object w/the userId and the idToken
-
- OIDCAuthenticationToken token = new OIDCAuthenticationToken(userId, idClaims.getIssuer(), serverConfig, idTokenValue, accessTokenValue, refreshTokenValue);
+ PendingOIDCAuthenticationToken token = new PendingOIDCAuthenticationToken(idClaims.getSubject(), idClaims.getIssuer(),
+ serverConfig,
+ idToken, accessTokenValue, refreshTokenValue);
Authentication authentication = this.getAuthenticationManager().authenticate(token);
@@ -578,7 +635,7 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE
/**
* Handle Authorization Endpoint error
- *
+ *
* @param request
* The request from which to extract parameters and handle the
* error
@@ -593,7 +650,7 @@ protected void handleError(HttpServletRequest request, HttpServletResponse respo
String errorDescription = request.getParameter("error_description");
String errorURI = request.getParameter("error_uri");
- throw new AuthenticationServiceException("Error from Authorization Endpoint: " + error + " " + errorDescription + " " + errorURI);
+ throw new AuthorizationEndpointException(error, errorDescription, errorURI);
}
/**
@@ -653,6 +710,26 @@ protected static String getStoredState(HttpSession session) {
return getStoredSessionString(session, STATE_SESSION_VARIABLE);
}
+ /**
+ * Create a random code challenge and store it in the session
+ * @param session
+ * @return
+ */
+ protected static String createCodeVerifier(HttpSession session) {
+ String challenge = new BigInteger(50, new SecureRandom()).toString(16);
+ session.setAttribute(CODE_VERIFIER_SESSION_VARIABLE, challenge);
+ return challenge;
+ }
+
+ /**
+ * Retrieve the stored challenge from our session
+ * @param session
+ * @return
+ */
+ protected static String getStoredCodeVerifier(HttpSession session) {
+ return getStoredSessionString(session, CODE_VERIFIER_SESSION_VARIABLE);
+ }
+
@Override
public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {
@@ -685,7 +762,9 @@ public void onAuthenticationSuccess(HttpServletRequest request,
if (!Strings.isNullOrEmpty(target)) {
session.removeAttribute(TARGET_SESSION_VARIABLE);
- target = deepLinkFilter.filter(target);
+ if (deepLinkFilter != null) {
+ target = deepLinkFilter.filter(target);
+ }
response.sendRedirect(target);
} else {
@@ -795,11 +874,11 @@ public void setAuthRequestOptionsService(AuthRequestOptionsService authOptions)
this.authOptions = authOptions;
}
- public SymmetricCacheService getSymmetricCacheService() {
+ public SymmetricKeyJWTValidatorCacheService getSymmetricCacheService() {
return symmetricCacheService;
}
- public void setSymmetricCacheService(SymmetricCacheService symmetricCacheService) {
+ public void setSymmetricCacheService(SymmetricKeyJWTValidatorCacheService symmetricCacheService) {
this.symmetricCacheService = symmetricCacheService;
}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationProvider.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationProvider.java
index 68a5056bfd..b43b649df2 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationProvider.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationProvider.java
@@ -1,100 +1,128 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.openid.connect.client;
import java.util.Collection;
import org.mitre.openid.connect.model.OIDCAuthenticationToken;
+import org.mitre.openid.connect.model.PendingOIDCAuthenticationToken;
import org.mitre.openid.connect.model.UserInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
+import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
+import com.nimbusds.jwt.JWT;
/**
- * @author nemonik
- *
+ * @author nemonik, Justin Richer
+ *
*/
public class OIDCAuthenticationProvider implements AuthenticationProvider {
+ private static Logger logger = LoggerFactory.getLogger(OIDCAuthenticationProvider.class);
+
private UserInfoFetcher userInfoFetcher = new UserInfoFetcher();
- private GrantedAuthoritiesMapper authoritiesMapper = new NamedAdminAuthoritiesMapper();
+ private OIDCAuthoritiesMapper authoritiesMapper = new NamedAdminAuthoritiesMapper();
/*
* (non-Javadoc)
- *
+ *
* @see org.springframework.security.authentication.AuthenticationProvider#
* authenticate(org.springframework.security.core.Authentication)
*/
@Override
- public Authentication authenticate(final Authentication authentication)
- throws AuthenticationException {
+ public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
if (!supports(authentication.getClass())) {
return null;
}
- if (authentication instanceof OIDCAuthenticationToken) {
+ if (authentication instanceof PendingOIDCAuthenticationToken) {
- OIDCAuthenticationToken token = (OIDCAuthenticationToken) authentication;
+ PendingOIDCAuthenticationToken token = (PendingOIDCAuthenticationToken) authentication;
- Collection authorities = Lists.newArrayList(new SubjectIssuerGrantedAuthority(token.getSub(), token.getIssuer()));
+ // get the ID Token value out
+ JWT idToken = token.getIdToken();
+ // load the user info if we can
UserInfo userInfo = userInfoFetcher.loadUserInfo(token);
if (userInfo == null) {
- // TODO: user Info not found -- error?
+ // user info not found -- could be an error, could be fine
} else {
+ // if we found userinfo, double check it
if (!Strings.isNullOrEmpty(userInfo.getSub()) && !userInfo.getSub().equals(token.getSub())) {
// the userinfo came back and the user_id fields don't match what was in the id_token
throw new UsernameNotFoundException("user_id mismatch between id_token and user_info call: " + token.getSub() + " / " + userInfo.getSub());
}
}
- return new OIDCAuthenticationToken(token.getSub(),
- token.getIssuer(),
- userInfo, authoritiesMapper.mapAuthorities(authorities),
- token.getIdTokenValue(), token.getAccessTokenValue(), token.getRefreshTokenValue());
+ return createAuthenticationToken(token, authoritiesMapper.mapAuthorities(idToken, userInfo), userInfo);
}
return null;
}
+ /**
+ * Override this function to return a different kind of Authentication, processes the authorities differently,
+ * or do post-processing based on the UserInfo object.
+ *
+ * @param token
+ * @param authorities
+ * @param userInfo
+ * @return
+ */
+ protected Authentication createAuthenticationToken(PendingOIDCAuthenticationToken token, Collection extends GrantedAuthority> authorities, UserInfo userInfo) {
+ return new OIDCAuthenticationToken(token.getSub(),
+ token.getIssuer(),
+ userInfo, authorities,
+ token.getIdToken(), token.getAccessTokenValue(), token.getRefreshTokenValue());
+ }
+
+ /**
+ * @param userInfoFetcher
+ */
+ public void setUserInfoFetcher(UserInfoFetcher userInfoFetcher) {
+ this.userInfoFetcher = userInfoFetcher;
+ }
+
/**
* @param authoritiesMapper
*/
- public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
+ public void setAuthoritiesMapper(OIDCAuthoritiesMapper authoritiesMapper) {
this.authoritiesMapper = authoritiesMapper;
}
/*
* (non-Javadoc)
- *
+ *
* @see
* org.springframework.security.authentication.AuthenticationProvider#supports
* (java.lang.Class)
*/
@Override
public boolean supports(Class> authentication) {
- return OIDCAuthenticationToken.class.isAssignableFrom(authentication);
+ return PendingOIDCAuthenticationToken.class.isAssignableFrom(authentication);
}
}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthoritiesMapper.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthoritiesMapper.java
new file mode 100644
index 0000000000..0ee1d1c66e
--- /dev/null
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthoritiesMapper.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+
+package org.mitre.openid.connect.client;
+
+import java.util.Collection;
+
+import org.mitre.openid.connect.model.UserInfo;
+import org.springframework.security.core.GrantedAuthority;
+
+import com.nimbusds.jwt.JWT;
+
+/**
+ * @author jricher
+ *
+ */
+public interface OIDCAuthoritiesMapper {
+
+ /**
+ * @param idToken the ID Token (parsed as a JWT, cannot be @null)
+ * @param userInfo userInfo of the current user (could be @null)
+ * @return the set of authorities to map to this user
+ */
+ Collection extends GrantedAuthority> mapAuthorities(JWT idToken, UserInfo userInfo);
+
+}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/StaticPrefixTargetLinkURIChecker.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/StaticPrefixTargetLinkURIChecker.java
index 9df9954594..b7725bbe07 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/StaticPrefixTargetLinkURIChecker.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/StaticPrefixTargetLinkURIChecker.java
@@ -1,6 +1,5 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
+ * Copyright 2018 The MIT Internet Trust Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,7 +18,7 @@
/**
* Simple target URI checker, checks whether the string in question starts
* with a configured prefix. Returns "/" if the match fails.
- *
+ *
* @author jricher
*
*/
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/SubjectIssuerGrantedAuthority.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/SubjectIssuerGrantedAuthority.java
index 01ffff0774..9d4c85c511 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/SubjectIssuerGrantedAuthority.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/SubjectIssuerGrantedAuthority.java
@@ -1,21 +1,20 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client;
@@ -24,9 +23,9 @@
import com.google.common.base.Strings;
/**
- *
+ *
* Simple authority representing a user at an issuer.
- *
+ *
* @author jricher
*
*/
@@ -51,9 +50,9 @@ public SubjectIssuerGrantedAuthority(String subject, String issuer) {
/**
* Returns a string formed by concatenating the subject with the issuer, separated by _ and prepended with OIDC_
- *
+ *
* For example, the user "bob" from issuer "http://id.example.com/" would return the authority string of:
- *
+ *
* OIDC_bob_http://id.example.com/
*/
@Override
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/TargetLinkURIChecker.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/TargetLinkURIChecker.java
index 04f5e7d63e..1fca0bfeb1 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/TargetLinkURIChecker.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/TargetLinkURIChecker.java
@@ -1,6 +1,5 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
+ * Copyright 2018 The MIT Internet Trust Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,7 +19,7 @@ public interface TargetLinkURIChecker {
/**
* Check the parameter to make sure that it's a valid deep-link into this application.
- *
+ *
* @param target
* @return
*/
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java
index 5b6caf7267..5b755617bb 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/UserInfoFetcher.java
@@ -1,31 +1,35 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.openid.connect.client;
import java.io.IOException;
import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
import org.apache.http.client.HttpClient;
import org.apache.http.client.utils.URIBuilder;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.mitre.openid.connect.config.ServerConfiguration;
import org.mitre.openid.connect.config.ServerConfiguration.UserInfoTokenMethod;
import org.mitre.openid.connect.model.DefaultUserInfo;
-import org.mitre.openid.connect.model.OIDCAuthenticationToken;
+import org.mitre.openid.connect.model.PendingOIDCAuthenticationToken;
import org.mitre.openid.connect.model.UserInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -37,44 +41,76 @@
import org.springframework.web.client.RestTemplate;
import com.google.common.base.Strings;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.util.concurrent.UncheckedExecutionException;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
/**
- * Utility class to fetch userinfo from the userinfo endpoint, if available.
+ * Utility class to fetch userinfo from the userinfo endpoint, if available. Caches the results.
* @author jricher
*
*/
public class UserInfoFetcher {
- private Logger logger = LoggerFactory.getLogger(UserInfoFetcher.class);
+ /**
+ * Logger for this class
+ */
+ private static final Logger logger = LoggerFactory.getLogger(UserInfoFetcher.class);
- public UserInfo loadUserInfo(final OIDCAuthenticationToken token) {
+ private LoadingCache cache;
- ServerConfiguration serverConfiguration = token.getServerConfiguration();
+ public UserInfoFetcher() {
+ this(HttpClientBuilder.create().useSystemProperties().build());
+ }
- if (serverConfiguration == null) {
- logger.warn("No server configuration found.");
+ public UserInfoFetcher(HttpClient httpClient) {
+ cache = CacheBuilder.newBuilder()
+ .expireAfterWrite(1, TimeUnit.HOURS) // expires 1 hour after fetch
+ .maximumSize(100)
+ .build(new UserInfoLoader(httpClient));
+ }
+
+ public UserInfo loadUserInfo(final PendingOIDCAuthenticationToken token) {
+ try {
+ return cache.get(token);
+ } catch (UncheckedExecutionException | ExecutionException e) {
+ logger.warn("Couldn't load User Info from token: " + e.getMessage());
return null;
}
- if (Strings.isNullOrEmpty(serverConfiguration.getUserInfoUri())) {
- logger.warn("No userinfo endpoint, not fetching.");
- return null;
+ }
+
+
+ private class UserInfoLoader extends CacheLoader {
+ private HttpComponentsClientHttpRequestFactory factory;
+
+ UserInfoLoader(HttpClient httpClient) {
+ this.factory = new HttpComponentsClientHttpRequestFactory(httpClient);
}
- try {
-
- // if we got this far, try to actually get the userinfo
- HttpClient httpClient = new SystemDefaultHttpClient();
-
- HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
-
+ @Override
+ public UserInfo load(final PendingOIDCAuthenticationToken token) throws URISyntaxException {
+
+ ServerConfiguration serverConfiguration = token.getServerConfiguration();
+
+ if (serverConfiguration == null) {
+ logger.warn("No server configuration found.");
+ return null;
+ }
+
+ if (Strings.isNullOrEmpty(serverConfiguration.getUserInfoUri())) {
+ logger.warn("No userinfo endpoint, not fetching.");
+ return null;
+ }
+
String userInfoString = null;
-
+
if (serverConfiguration.getUserInfoTokenMethod() == null || serverConfiguration.getUserInfoTokenMethod().equals(UserInfoTokenMethod.HEADER)) {
RestTemplate restTemplate = new RestTemplate(factory) {
-
+
@Override
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
ClientHttpRequest httpRequest = super.createRequest(url, method);
@@ -82,19 +118,19 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE
return httpRequest;
}
};
-
+
userInfoString = restTemplate.getForObject(serverConfiguration.getUserInfoUri(), String.class);
-
+
} else if (serverConfiguration.getUserInfoTokenMethod().equals(UserInfoTokenMethod.FORM)) {
- MultiValueMap form = new LinkedMultiValueMap();
+ MultiValueMap form = new LinkedMultiValueMap<>();
form.add("access_token", token.getAccessTokenValue());
-
+
RestTemplate restTemplate = new RestTemplate(factory);
userInfoString = restTemplate.postForObject(serverConfiguration.getUserInfoUri(), form, String.class);
} else if (serverConfiguration.getUserInfoTokenMethod().equals(UserInfoTokenMethod.QUERY)) {
URIBuilder builder = new URIBuilder(serverConfiguration.getUserInfoUri());
builder.setParameter("access_token", token.getAccessTokenValue());
-
+
RestTemplate restTemplate = new RestTemplate(factory);
userInfoString = restTemplate.getForObject(builder.toString(), String.class);
}
@@ -103,19 +139,19 @@ protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOE
if (!Strings.isNullOrEmpty(userInfoString)) {
JsonObject userInfoJson = new JsonParser().parse(userInfoString).getAsJsonObject();
-
- UserInfo userInfo = DefaultUserInfo.fromJson(userInfoJson);
+
+ UserInfo userInfo = fromJson(userInfoJson);
return userInfo;
} else {
- // didn't get anything, return null
- return null;
+ // didn't get anything throw exception
+ throw new IllegalArgumentException("Unable to load user info");
}
- } catch (Exception e) {
- logger.warn("Error fetching userinfo", e);
- return null;
- }
+ }
}
+ protected UserInfo fromJson(JsonObject userInfoJson) {
+ return DefaultUserInfo.fromJson(userInfoJson);
+ }
}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/keypublisher/ClientKeyPublisher.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/keypublisher/ClientKeyPublisher.java
index 673a558832..dfd0eea85c 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/keypublisher/ClientKeyPublisher.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/keypublisher/ClientKeyPublisher.java
@@ -1,26 +1,27 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.openid.connect.client.keypublisher;
import java.util.Map;
import java.util.UUID;
-import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
-import org.mitre.openid.connect.view.JwkKeyListView;
+import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
+import org.mitre.openid.connect.view.JWKSetView;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
@@ -37,13 +38,13 @@
*/
public class ClientKeyPublisher implements BeanDefinitionRegistryPostProcessor {
- private JwtSigningAndValidationService signingAndValidationService;
+ private JWTSigningAndValidationService signingAndValidationService;
private String jwkPublishUrl;
private BeanDefinitionRegistry registry;
- private String jwkViewName = "jwkKeyList";
+ private String jwkViewName = JWKSetView.VIEWNAME;
/**
* If the jwkPublishUrl field is set on this bean, set up a listener on that URL to publish keys.
@@ -61,13 +62,13 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
clientKeyMapping.addPropertyValue("jwkPublishUrl", getJwkPublishUrl());
// randomize view name to make sure it doesn't conflict with local views
- jwkViewName = "jwkKeyList-" + UUID.randomUUID().toString();
+ jwkViewName = JWKSetView.VIEWNAME + "-" + UUID.randomUUID().toString();
viewResolver.addPropertyValue("jwkViewName", jwkViewName);
// view bean
- BeanDefinitionBuilder jwkView = BeanDefinitionBuilder.rootBeanDefinition(JwkKeyListView.class);
- registry.registerBeanDefinition("jwkKeyList", jwkView.getBeanDefinition());
- viewResolver.addPropertyReference("jwk", "jwkKeyList");
+ BeanDefinitionBuilder jwkView = BeanDefinitionBuilder.rootBeanDefinition(JWKSetView.class);
+ registry.registerBeanDefinition(JWKSetView.VIEWNAME, jwkView.getBeanDefinition());
+ viewResolver.addPropertyReference("jwk", JWKSetView.VIEWNAME);
}
registry.registerBeanDefinition("clientKeyMapping", clientKeyMapping.getBeanDefinition());
@@ -114,14 +115,14 @@ public void setJwkPublishUrl(String jwkPublishUrl) {
/**
* @return the signingAndValidationService
*/
- public JwtSigningAndValidationService getSigningAndValidationService() {
+ public JWTSigningAndValidationService getSigningAndValidationService() {
return signingAndValidationService;
}
/**
* @param signingAndValidationService the signingAndValidationService to set
*/
- public void setSigningAndValidationService(JwtSigningAndValidationService signingAndValidationService) {
+ public void setSigningAndValidationService(JWTSigningAndValidationService signingAndValidationService) {
this.signingAndValidationService = signingAndValidationService;
}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/keypublisher/ClientKeyPublisherMapping.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/keypublisher/ClientKeyPublisherMapping.java
index 55e59c503c..e601f3c271 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/keypublisher/ClientKeyPublisherMapping.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/keypublisher/ClientKeyPublisherMapping.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.keypublisher;
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/keypublisher/JwkViewResolver.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/keypublisher/JwkViewResolver.java
index d5999a9ce7..30ebdd63fd 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/keypublisher/JwkViewResolver.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/keypublisher/JwkViewResolver.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.keypublisher;
@@ -26,9 +27,9 @@
import org.springframework.web.servlet.ViewResolver;
/**
- *
+ *
* Simple view resolver to map JWK view names to appropriate beans
- *
+ *
* @author jricher
*
*/
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/model/IssuerServiceResponse.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/model/IssuerServiceResponse.java
index 1355a71eff..e8de16a13d 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/model/IssuerServiceResponse.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/model/IssuerServiceResponse.java
@@ -1,28 +1,29 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.model;
/**
- *
+ *
* Data container to facilitate returns from the IssuerService API.
- *
+ *
* @author jricher
*
*/
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/AuthRequestOptionsService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/AuthRequestOptionsService.java
index ffdb85dae8..73a8d377f1 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/AuthRequestOptionsService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/AuthRequestOptionsService.java
@@ -1,6 +1,7 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +16,7 @@
* limitations under the License.
*******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service;
@@ -27,15 +28,34 @@
import org.mitre.openid.connect.config.ServerConfiguration;
/**
- *
- * This service provides any extra options that need to be passed to the authentication request.
+ *
+ * This service provides any extra options that need to be passed to the authentication request,
+ * either through the authorization endpoint (getOptions) or the token endpoint (getTokenOptions).
* These options may depend on the server configuration, client configuration, or HTTP request.
- *
+ *
* @author jricher
*
*/
public interface AuthRequestOptionsService {
+ /**
+ * The set of options needed at the authorization endpoint.
+ *
+ * @param server
+ * @param client
+ * @param request
+ * @return
+ */
public Map getOptions(ServerConfiguration server, RegisteredClient client, HttpServletRequest request);
+ /**
+ * The set of options needed at the token endpoint.
+ *
+ * @param server
+ * @param client
+ * @param request
+ * @return
+ */
+ public Map getTokenOptions(ServerConfiguration server, RegisteredClient client, HttpServletRequest request);
+
}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/AuthRequestUrlBuilder.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/AuthRequestUrlBuilder.java
index 6a89a5fd50..14eb8a09ca 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/AuthRequestUrlBuilder.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/AuthRequestUrlBuilder.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service;
@@ -25,6 +26,8 @@
import org.mitre.openid.connect.config.ServerConfiguration;
/**
+ * Builds a URL string to the IdP's authorization endpoint.
+ *
* @author jricher
*
*/
@@ -36,8 +39,9 @@ public interface AuthRequestUrlBuilder {
* @param redirectUri
* @param nonce
* @param state
+ * @param loginHint
* @return
*/
- public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options);
+ public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options, String loginHint);
}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ClientConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ClientConfigurationService.java
index e6c8eb8f62..6444376b3e 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ClientConfigurationService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ClientConfigurationService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service;
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/IssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/IssuerService.java
index b631d34a66..7e4e527024 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/IssuerService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/IssuerService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service;
@@ -24,9 +25,9 @@
import org.mitre.openid.connect.client.model.IssuerServiceResponse;
/**
- *
+ *
* Gets an issuer for the given request. Might do dynamic discovery, or might be statically configured.
- *
+ *
* @author jricher
*
*/
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/RegisteredClientService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/RegisteredClientService.java
index 3b56b376f6..0ca59bc103 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/RegisteredClientService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/RegisteredClientService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service;
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ServerConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ServerConfigurationService.java
index c8cf10f860..44613fdc5e 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ServerConfigurationService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/ServerConfigurationService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service;
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicRegistrationClientConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicRegistrationClientConfigurationService.java
index 320d32cd87..2c32fd8fd9 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicRegistrationClientConfigurationService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicRegistrationClientConfigurationService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
@@ -24,7 +25,7 @@
import java.util.concurrent.ExecutionException;
import org.apache.http.client.HttpClient;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.ClientDetailsEntityJsonProcessor;
import org.mitre.openid.connect.client.service.ClientConfigurationService;
@@ -39,6 +40,8 @@
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
+import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import com.google.common.cache.CacheBuilder;
@@ -55,7 +58,10 @@
*/
public class DynamicRegistrationClientConfigurationService implements ClientConfigurationService {
- private static Logger logger = LoggerFactory.getLogger(DynamicServerConfigurationService.class);
+ /**
+ * Logger for this class
+ */
+ private static final Logger logger = LoggerFactory.getLogger(DynamicRegistrationClientConfigurationService.class);
private LoadingCache clients;
@@ -63,29 +69,30 @@ public class DynamicRegistrationClientConfigurationService implements ClientConf
private RegisteredClient template;
- private Set whitelist = new HashSet();
- private Set blacklist = new HashSet();
+ private Set whitelist = new HashSet<>();
+ private Set blacklist = new HashSet<>();
public DynamicRegistrationClientConfigurationService() {
- clients = CacheBuilder.newBuilder().build(new DynamicClientRegistrationLoader());
+ this(HttpClientBuilder.create().useSystemProperties().build());
+ }
+
+ public DynamicRegistrationClientConfigurationService(HttpClient httpClient) {
+ clients = CacheBuilder.newBuilder().build(new DynamicClientRegistrationLoader(httpClient));
}
@Override
public RegisteredClient getClientConfiguration(ServerConfiguration issuer) {
try {
- if (!whitelist.isEmpty() && !whitelist.contains(issuer)) {
+ if (!whitelist.isEmpty() && !whitelist.contains(issuer.getIssuer())) {
throw new AuthenticationServiceException("Whitelist was nonempty, issuer was not in whitelist: " + issuer);
}
- if (blacklist.contains(issuer)) {
+ if (blacklist.contains(issuer.getIssuer())) {
throw new AuthenticationServiceException("Issuer was in blacklist: " + issuer);
}
return clients.get(issuer);
- } catch (UncheckedExecutionException ue) {
- logger.warn("Unable to get client configuration", ue);
- return null;
- } catch (ExecutionException e) {
+ } catch (UncheckedExecutionException | ExecutionException e) {
logger.warn("Unable to get client configuration", e);
return null;
}
@@ -158,18 +165,25 @@ public void setBlacklist(Set blacklist) {
/**
* Loader class that fetches the client information.
- *
+ *
* If a client has been registered (ie, it's known to the RegisteredClientService), then this
* will fetch the client's configuration from the server.
- *
+ *
* @author jricher
*
*/
public class DynamicClientRegistrationLoader extends CacheLoader {
- private HttpClient httpClient = new SystemDefaultHttpClient();
- private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
+ private HttpComponentsClientHttpRequestFactory httpFactory;
private Gson gson = new Gson(); // note that this doesn't serialize nulls by default
+ public DynamicClientRegistrationLoader() {
+ this(HttpClientBuilder.create().useSystemProperties().build());
+ }
+
+ public DynamicClientRegistrationLoader(HttpClient httpClient) {
+ this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
+ }
+
@Override
public RegisteredClient load(ServerConfiguration serverConfig) throws Exception {
RestTemplate restTemplate = new RestTemplate(httpFactory);
@@ -186,34 +200,41 @@ public RegisteredClient load(ServerConfiguration serverConfig) throws Exception
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Lists.newArrayList(MediaType.APPLICATION_JSON));
- HttpEntity entity = new HttpEntity(serializedClient, headers);
+ HttpEntity entity = new HttpEntity<>(serializedClient, headers);
- String registered = restTemplate.postForObject(serverConfig.getRegistrationEndpointUri(), entity, String.class);
- // TODO: handle HTTP errors
+ try {
+ String registered = restTemplate.postForObject(serverConfig.getRegistrationEndpointUri(), entity, String.class);
- RegisteredClient client = ClientDetailsEntityJsonProcessor.parseRegistered(registered);
+ RegisteredClient client = ClientDetailsEntityJsonProcessor.parseRegistered(registered);
- // save this client for later
- registeredClientService.save(serverConfig.getIssuer(), client);
+ // save this client for later
+ registeredClientService.save(serverConfig.getIssuer(), client);
- return client;
+ return client;
+ } catch (RestClientException rce) {
+ throw new InvalidClientException("Error registering client with server");
+ }
} else {
if (knownClient.getClientId() == null) {
-
+
// load this client's information from the server
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, knownClient.getRegistrationAccessToken()));
headers.setAccept(Lists.newArrayList(MediaType.APPLICATION_JSON));
-
- HttpEntity entity = new HttpEntity(headers);
-
- String registered = restTemplate.exchange(knownClient.getRegistrationClientUri(), HttpMethod.GET, entity, String.class).getBody();
- // TODO: handle HTTP errors
-
- RegisteredClient client = ClientDetailsEntityJsonProcessor.parseRegistered(registered);
-
- return client;
+
+ HttpEntity entity = new HttpEntity<>(headers);
+
+ try {
+ String registered = restTemplate.exchange(knownClient.getRegistrationClientUri(), HttpMethod.GET, entity, String.class).getBody();
+ // TODO: handle HTTP errors
+
+ RegisteredClient client = ClientDetailsEntityJsonProcessor.parseRegistered(registered);
+
+ return client;
+ } catch (RestClientException rce) {
+ throw new InvalidClientException("Error loading previously registered client information from server");
+ }
} else {
// it's got a client ID from the store, don't bother trying to load it
return knownClient;
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java
index 0e99633ca2..5f451c2dcb 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/DynamicServerConfigurationService.java
@@ -1,37 +1,38 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
-import static org.mitre.discovery.util.JsonUtils.getAsBoolean;
-import static org.mitre.discovery.util.JsonUtils.getAsEncryptionMethodList;
-import static org.mitre.discovery.util.JsonUtils.getAsJweAlgorithmList;
-import static org.mitre.discovery.util.JsonUtils.getAsJwsAlgorithmList;
-import static org.mitre.discovery.util.JsonUtils.getAsString;
-import static org.mitre.discovery.util.JsonUtils.getAsStringList;
+import static org.mitre.util.JsonUtils.getAsBoolean;
+import static org.mitre.util.JsonUtils.getAsEncryptionMethodList;
+import static org.mitre.util.JsonUtils.getAsJweAlgorithmList;
+import static org.mitre.util.JsonUtils.getAsJwsAlgorithmList;
+import static org.mitre.util.JsonUtils.getAsString;
+import static org.mitre.util.JsonUtils.getAsStringList;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import org.apache.http.client.HttpClient;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.mitre.openid.connect.client.service.ServerConfigurationService;
import org.mitre.openid.connect.config.ServerConfiguration;
import org.slf4j.Logger;
@@ -49,25 +50,32 @@
import com.google.gson.JsonParser;
/**
- *
+ *
* Dynamically fetches OpenID Connect server configurations based on the issuer. Caches the server configurations.
- *
+ *
* @author jricher
*
*/
public class DynamicServerConfigurationService implements ServerConfigurationService {
- private static Logger logger = LoggerFactory.getLogger(DynamicServerConfigurationService.class);
+ /**
+ * Logger for this class
+ */
+ private static final Logger logger = LoggerFactory.getLogger(DynamicServerConfigurationService.class);
// map of issuer -> server configuration, loaded dynamically from service discovery
private LoadingCache servers;
- private Set whitelist = new HashSet();
- private Set blacklist = new HashSet();
+ private Set whitelist = new HashSet<>();
+ private Set blacklist = new HashSet<>();
public DynamicServerConfigurationService() {
+ this(HttpClientBuilder.create().useSystemProperties().build());
+ }
+
+ public DynamicServerConfigurationService(HttpClient httpClient) {
// initialize the cache
- servers = CacheBuilder.newBuilder().build(new OpenIDConnectServiceConfigurationFetcher());
+ servers = CacheBuilder.newBuilder().build(new OpenIDConnectServiceConfigurationFetcher(httpClient));
}
/**
@@ -111,11 +119,8 @@ public ServerConfiguration getServerConfiguration(String issuer) {
}
return servers.get(issuer);
- } catch (UncheckedExecutionException ue) {
- logger.warn("Couldn't load configuration for " + issuer, ue);
- return null;
- } catch (ExecutionException e) {
- logger.warn("Couldn't load configuration for " + issuer, e);
+ } catch (UncheckedExecutionException | ExecutionException e) {
+ logger.warn("Couldn't load configuration for " + issuer + ": " + e);
return null;
}
@@ -126,10 +131,13 @@ public ServerConfiguration getServerConfiguration(String issuer) {
*
*/
private class OpenIDConnectServiceConfigurationFetcher extends CacheLoader {
- private HttpClient httpClient = new SystemDefaultHttpClient();
- private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
+ private HttpComponentsClientHttpRequestFactory httpFactory;
private JsonParser parser = new JsonParser();
+ OpenIDConnectServiceConfigurationFetcher(HttpClient httpClient) {
+ this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
+ }
+
@Override
public ServerConfiguration load(String issuer) throws Exception {
RestTemplate restTemplate = new RestTemplate(httpFactory);
@@ -154,7 +162,7 @@ public ServerConfiguration load(String issuer) throws Exception {
}
if (!issuer.equals(o.get("issuer").getAsString())) {
- throw new IllegalStateException("Discovered issuers didn't match, expected " + issuer + " got " + o.get("issuer").getAsString());
+ logger.info("Issuer used for discover was " + issuer + " but final issuer is " + o.get("issuer").getAsString());
}
conf.setIssuer(o.get("issuer").getAsString());
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/EncryptedAuthRequestUrlBuilder.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/EncryptedAuthRequestUrlBuilder.java
index be83e70ac9..cad7d7399a 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/EncryptedAuthRequestUrlBuilder.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/EncryptedAuthRequestUrlBuilder.java
@@ -1,6 +1,7 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +16,7 @@
* limitations under the License.
*******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
@@ -24,7 +25,7 @@
import java.util.Map.Entry;
import org.apache.http.client.utils.URIBuilder;
-import org.mitre.jwt.encryption.service.JwtEncryptionAndDecryptionService;
+import org.mitre.jwt.encryption.service.JWTEncryptionAndDecryptionService;
import org.mitre.jwt.signer.service.impl.JWKSetCacheService;
import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.client.service.AuthRequestUrlBuilder;
@@ -32,6 +33,7 @@
import org.springframework.security.authentication.AuthenticationServiceException;
import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JWEAlgorithm;
import com.nimbusds.jose.JWEHeader;
@@ -54,33 +56,38 @@ public class EncryptedAuthRequestUrlBuilder implements AuthRequestUrlBuilder {
* @see org.mitre.openid.connect.client.service.AuthRequestUrlBuilder#buildAuthRequestUrl(org.mitre.openid.connect.config.ServerConfiguration, org.mitre.oauth2.model.RegisteredClient, java.lang.String, java.lang.String, java.lang.String, java.util.Map)
*/
@Override
- public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options) {
+ public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options, String loginHint) {
// create our signed JWT for the request object
- JWTClaimsSet claims = new JWTClaimsSet();
+ JWTClaimsSet.Builder claims = new JWTClaimsSet.Builder();
//set parameters to JwtClaims
- claims.setClaim("response_type", "code");
- claims.setClaim("client_id", clientConfig.getClientId());
- claims.setClaim("scope", Joiner.on(" ").join(clientConfig.getScope()));
+ claims.claim("response_type", "code");
+ claims.claim("client_id", clientConfig.getClientId());
+ claims.claim("scope", Joiner.on(" ").join(clientConfig.getScope()));
// build our redirect URI
- claims.setClaim("redirect_uri", redirectUri);
+ claims.claim("redirect_uri", redirectUri);
// this comes back in the id token
- claims.setClaim("nonce", nonce);
+ claims.claim("nonce", nonce);
// this comes back in the auth request return
- claims.setClaim("state", state);
+ claims.claim("state", state);
// Optional parameters
for (Entry option : options.entrySet()) {
- claims.setClaim(option.getKey(), option.getValue());
+ claims.claim(option.getKey(), option.getValue());
+ }
+
+ // if there's a login hint, send it
+ if (!Strings.isNullOrEmpty(loginHint)) {
+ claims.claim("login_hint", loginHint);
}
- EncryptedJWT jwt = new EncryptedJWT(new JWEHeader(alg, enc), claims);
+ EncryptedJWT jwt = new EncryptedJWT(new JWEHeader(alg, enc), claims.build());
- JwtEncryptionAndDecryptionService encryptor = encrypterService.getEncrypter(serverConfig.getJwksUri());
+ JWTEncryptionAndDecryptionService encryptor = encrypterService.getEncrypter(serverConfig.getJwksUri());
encryptor.encryptJwt(jwt);
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/HybridClientConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/HybridClientConfigurationService.java
index 940263c325..16fed24ed3 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/HybridClientConfigurationService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/HybridClientConfigurationService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
@@ -31,12 +32,12 @@
* Houses both a static client configuration and a dynamic client configuration
* service in one object. Checks the static service first, then falls through to
* the dynamic service.
- *
+ *
* Provides configuration passthrough for the template, registered client service, whitelist,
* and blacklist for the dynamic service, and to the static service's client map.
- *
+ *
* @author jricher
- *
+ *
*/
public class HybridClientConfigurationService implements ClientConfigurationService {
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/HybridIssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/HybridIssuerService.java
index 47e581bee8..816f03698e 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/HybridIssuerService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/HybridIssuerService.java
@@ -1,6 +1,7 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,16 +27,48 @@
import com.google.common.collect.Sets;
/**
- *
+ *
* Issuer service that tries to parse input from the inputs from a third-party
* account chooser service (if possible), but falls back to webfinger discovery
* if not.
- *
+ *
* @author jricher
*
*/
public class HybridIssuerService implements IssuerService {
+ /**
+ * @return
+ * @see org.mitre.openid.connect.client.service.impl.ThirdPartyIssuerService#getAccountChooserUrl()
+ */
+ public String getAccountChooserUrl() {
+ return thirdPartyIssuerService.getAccountChooserUrl();
+ }
+
+ /**
+ * @param accountChooserUrl
+ * @see org.mitre.openid.connect.client.service.impl.ThirdPartyIssuerService#setAccountChooserUrl(java.lang.String)
+ */
+ public void setAccountChooserUrl(String accountChooserUrl) {
+ thirdPartyIssuerService.setAccountChooserUrl(accountChooserUrl);
+ }
+
+ /**
+ * @return
+ * @see org.mitre.openid.connect.client.service.impl.WebfingerIssuerService#isForceHttps()
+ */
+ public boolean isForceHttps() {
+ return webfingerIssuerService.isForceHttps();
+ }
+
+ /**
+ * @param forceHttps
+ * @see org.mitre.openid.connect.client.service.impl.WebfingerIssuerService#setForceHttps(boolean)
+ */
+ public void setForceHttps(boolean forceHttps) {
+ webfingerIssuerService.setForceHttps(forceHttps);
+ }
+
private ThirdPartyIssuerService thirdPartyIssuerService = new ThirdPartyIssuerService();
private WebfingerIssuerService webfingerIssuerService = new WebfingerIssuerService();
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/HybridServerConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/HybridServerConfigurationService.java
index 2a01339e07..cf519442ca 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/HybridServerConfigurationService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/HybridServerConfigurationService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
@@ -29,11 +30,11 @@
* Houses both a static server configuration and a dynamic server configuration
* service in one object. Checks the static service first, then falls through to
* the dynamic service.
- *
+ *
* Provides configuration passthrough to the dynamic service's whitelist and blacklist,
* and to the static service's server map.
- *
- *
+ *
+ *
* @author jricher
*
*/
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/InMemoryRegisteredClientService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/InMemoryRegisteredClientService.java
index 315fe47fda..6be9eca8e7 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/InMemoryRegisteredClientService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/InMemoryRegisteredClientService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
@@ -31,7 +32,7 @@
*/
public class InMemoryRegisteredClientService implements RegisteredClientService {
- private Map clients = new HashMap();
+ private Map clients = new HashMap<>();
/* (non-Javadoc)
* @see org.mitre.openid.connect.client.service.RegisteredClientService#getByIssuer(java.lang.String)
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/JsonFileRegisteredClientService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/JsonFileRegisteredClientService.java
index 68cfd10080..de69bb8f3e 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/JsonFileRegisteredClientService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/JsonFileRegisteredClientService.java
@@ -1,26 +1,26 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
import java.io.File;
-import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
@@ -50,27 +50,30 @@
*/
public class JsonFileRegisteredClientService implements RegisteredClientService {
- private static Logger logger = LoggerFactory.getLogger(JsonFileRegisteredClientService.class);
+ /**
+ * Logger for this class
+ */
+ private static final Logger logger = LoggerFactory.getLogger(JsonFileRegisteredClientService.class);
private Gson gson = new GsonBuilder()
- .registerTypeAdapter(RegisteredClient.class, new JsonSerializer() {
- @Override
- public JsonElement serialize(RegisteredClient src, Type typeOfSrc, JsonSerializationContext context) {
- return ClientDetailsEntityJsonProcessor.serialize(src);
- }
- })
- .registerTypeAdapter(RegisteredClient.class, new JsonDeserializer() {
- @Override
- public RegisteredClient deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
- return ClientDetailsEntityJsonProcessor.parseRegistered(json);
- }
- })
- .setPrettyPrinting()
- .create();
+ .registerTypeAdapter(RegisteredClient.class, new JsonSerializer() {
+ @Override
+ public JsonElement serialize(RegisteredClient src, Type typeOfSrc, JsonSerializationContext context) {
+ return ClientDetailsEntityJsonProcessor.serialize(src);
+ }
+ })
+ .registerTypeAdapter(RegisteredClient.class, new JsonDeserializer() {
+ @Override
+ public RegisteredClient deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ return ClientDetailsEntityJsonProcessor.parseRegistered(json);
+ }
+ })
+ .setPrettyPrinting()
+ .create();
private File file;
- private Map clients = new HashMap();
+ private Map clients = new HashMap<>();
public JsonFileRegisteredClientService(String filename) {
this.file = new File(filename);
@@ -97,6 +100,7 @@ public void save(String issuer, RegisteredClient client) {
/**
* Sync the map of clients out to disk.
*/
+ @SuppressWarnings("serial")
private void write() {
try {
if (!file.exists()) {
@@ -110,8 +114,6 @@ private void write() {
out.close();
- } catch (FileNotFoundException e) {
- logger.error("Could not write to output file", e);
} catch (IOException e) {
logger.error("Could not write to output file", e);
}
@@ -120,6 +122,7 @@ private void write() {
/**
* Load the map in from disk.
*/
+ @SuppressWarnings("serial")
private void load() {
try {
if (!file.exists()) {
@@ -132,8 +135,6 @@ private void load() {
in.close();
- } catch (FileNotFoundException e) {
- logger.error("Could not read from input file", e);
} catch (IOException e) {
logger.error("Could not read from input file", e);
}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java
index 7c4cccc776..86ecece0ef 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/PlainAuthRequestUrlBuilder.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
@@ -30,11 +31,12 @@
import org.springframework.security.authentication.AuthenticationServiceException;
import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
/**
- *
+ *
* Builds an auth request redirect URI with normal query parameters.
- *
+ *
* @author jricher
*
*/
@@ -44,7 +46,7 @@ public class PlainAuthRequestUrlBuilder implements AuthRequestUrlBuilder {
* @see org.mitre.openid.connect.client.service.AuthRequestUrlBuilder#buildAuthRequest(javax.servlet.http.HttpServletRequest, org.mitre.openid.connect.config.ServerConfiguration, org.springframework.security.oauth2.provider.ClientDetails)
*/
@Override
- public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options) {
+ public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options, String loginHint) {
try {
URIBuilder uriBuilder = new URIBuilder(serverConfig.getAuthorizationEndpointUri());
@@ -63,6 +65,11 @@ public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredCl
uriBuilder.addParameter(option.getKey(), option.getValue());
}
+ // if there's a login hint, send it
+ if (!Strings.isNullOrEmpty(loginHint)) {
+ uriBuilder.addParameter("login_hint", loginHint);
+ }
+
return uriBuilder.build().toString();
} catch (URISyntaxException e) {
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/SignedAuthRequestUrlBuilder.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/SignedAuthRequestUrlBuilder.java
index 78fcd9bd7e..604a72a391 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/SignedAuthRequestUrlBuilder.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/SignedAuthRequestUrlBuilder.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
@@ -24,13 +25,15 @@
import java.util.Map.Entry;
import org.apache.http.client.utils.URIBuilder;
-import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
+import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.client.service.AuthRequestUrlBuilder;
import org.mitre.openid.connect.config.ServerConfiguration;
import org.springframework.security.authentication.AuthenticationServiceException;
import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
+import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
@@ -41,41 +44,49 @@
*/
public class SignedAuthRequestUrlBuilder implements AuthRequestUrlBuilder {
- private JwtSigningAndValidationService signingAndValidationService;
+ private JWTSigningAndValidationService signingAndValidationService;
/* (non-Javadoc)
* @see org.mitre.openid.connect.client.service.AuthRequestUrlBuilder#buildAuthRequestUrl(org.mitre.openid.connect.config.ServerConfiguration, org.springframework.security.oauth2.provider.ClientDetails, java.lang.String, java.lang.String, java.lang.String)
*/
@Override
- public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options) {
+ public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map options, String loginHint) {
// create our signed JWT for the request object
- JWTClaimsSet claims = new JWTClaimsSet();
+ JWTClaimsSet.Builder claims = new JWTClaimsSet.Builder();
//set parameters to JwtClaims
- claims.setClaim("response_type", "code");
- claims.setClaim("client_id", clientConfig.getClientId());
- claims.setClaim("scope", Joiner.on(" ").join(clientConfig.getScope()));
+ claims.claim("response_type", "code");
+ claims.claim("client_id", clientConfig.getClientId());
+ claims.claim("scope", Joiner.on(" ").join(clientConfig.getScope()));
// build our redirect URI
- claims.setClaim("redirect_uri", redirectUri);
+ claims.claim("redirect_uri", redirectUri);
// this comes back in the id token
- claims.setClaim("nonce", nonce);
+ claims.claim("nonce", nonce);
// this comes back in the auth request return
- claims.setClaim("state", state);
+ claims.claim("state", state);
// Optional parameters
for (Entry option : options.entrySet()) {
- claims.setClaim(option.getKey(), option.getValue());
+ claims.claim(option.getKey(), option.getValue());
}
+ // if there's a login hint, send it
+ if (!Strings.isNullOrEmpty(loginHint)) {
+ claims.claim("login_hint", loginHint);
+ }
+ JWSAlgorithm alg = clientConfig.getRequestObjectSigningAlg();
+ if (alg == null) {
+ alg = signingAndValidationService.getDefaultSigningAlgorithm();
+ }
- SignedJWT jwt = new SignedJWT(new JWSHeader(signingAndValidationService.getDefaultSigningAlgorithm()), claims);
+ SignedJWT jwt = new SignedJWT(new JWSHeader(alg), claims.build());
- signingAndValidationService.signJwt(jwt);
+ signingAndValidationService.signJwt(jwt, alg);
try {
URIBuilder uriBuilder = new URIBuilder(serverConfig.getAuthorizationEndpointUri());
@@ -91,14 +102,14 @@ public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredCl
/**
* @return the signingAndValidationService
*/
- public JwtSigningAndValidationService getSigningAndValidationService() {
+ public JWTSigningAndValidationService getSigningAndValidationService() {
return signingAndValidationService;
}
/**
* @param signingAndValidationService the signingAndValidationService to set
*/
- public void setSigningAndValidationService(JwtSigningAndValidationService signingAndValidationService) {
+ public void setSigningAndValidationService(JWTSigningAndValidationService signingAndValidationService) {
this.signingAndValidationService = signingAndValidationService;
}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticAuthRequestOptionsService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticAuthRequestOptionsService.java
index 87ccb40086..8febc64a09 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticAuthRequestOptionsService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticAuthRequestOptionsService.java
@@ -1,6 +1,7 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,7 +16,7 @@
* limitations under the License.
*******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
@@ -29,15 +30,16 @@
import org.mitre.openid.connect.config.ServerConfiguration;
/**
- *
+ *
* Always returns the same set of options.
- *
+ *
* @author jricher
*
*/
public class StaticAuthRequestOptionsService implements AuthRequestOptionsService {
- private Map options = new HashMap();
+ private Map options = new HashMap<>();
+ private Map tokenOptions = new HashMap<>();
/* (non-Javadoc)
* @see org.mitre.openid.connect.client.service.AuthRequestOptionsService#getOptions(org.mitre.openid.connect.config.ServerConfiguration, org.mitre.oauth2.model.RegisteredClient, javax.servlet.http.HttpServletRequest)
@@ -47,8 +49,16 @@ public Map getOptions(ServerConfiguration server, RegisteredClie
return options;
}
+ /* (non-Javadoc)
+ * @see org.mitre.openid.connect.client.service.AuthRequestOptionsService#getTokenOptions(org.mitre.openid.connect.config.ServerConfiguration, org.mitre.oauth2.model.RegisteredClient, javax.servlet.http.HttpServletRequest)
+ */
+ @Override
+ public Map getTokenOptions(ServerConfiguration server, RegisteredClient client, HttpServletRequest request) {
+ return tokenOptions;
+ }
+
/**
- * @return the options
+ * @return the options object directly
*/
public Map getOptions() {
return options;
@@ -61,6 +71,18 @@ public void setOptions(Map options) {
this.options = options;
}
+ /**
+ * @return the tokenOptions
+ */
+ public Map getTokenOptions() {
+ return tokenOptions;
+ }
+ /**
+ * @param tokenOptions the tokenOptions to set
+ */
+ public void setTokenOptions(Map tokenOptions) {
+ this.tokenOptions = tokenOptions;
+ }
}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticClientConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticClientConfigurationService.java
index 8b1422ad94..df31018047 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticClientConfigurationService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticClientConfigurationService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
@@ -29,9 +30,9 @@
/**
* Client configuration service that holds a static map from issuer URL to a ClientDetails object to use at that issuer.
- *
+ *
* Designed to be configured as a bean.
- *
+ *
* @author jricher
*
*/
@@ -56,7 +57,7 @@ public void setClients(Map clients) {
/**
* Get the client configured for this issuer
- *
+ *
* @see org.mitre.openid.connect.client.service.ClientConfigurationService#getClientConfiguration(java.lang.String)
*/
@Override
@@ -66,7 +67,7 @@ public RegisteredClient getClientConfiguration(ServerConfiguration issuer) {
}
@PostConstruct
- public void afterPropertiesSet() throws Exception {
+ public void afterPropertiesSet() {
if (clients == null || clients.isEmpty()) {
throw new IllegalArgumentException("Clients map cannot be null or empty");
}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticServerConfigurationService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticServerConfigurationService.java
index 3ead65c284..ebca40c1e4 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticServerConfigurationService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticServerConfigurationService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
@@ -28,7 +29,7 @@
/**
* Statically configured server configuration service that maps issuer URLs to server configurations to use at that issuer.
- *
+ *
* @author jricher
*
*/
@@ -60,7 +61,7 @@ public ServerConfiguration getServerConfiguration(String issuer) {
}
@PostConstruct
- public void afterPropertiesSet() throws Exception {
+ public void afterPropertiesSet() {
if (servers == null || servers.isEmpty()) {
throw new IllegalArgumentException("Servers map cannot be null or empty.");
}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticSingleIssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticSingleIssuerService.java
index 7d9cf8ab92..c72b655236 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticSingleIssuerService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/StaticSingleIssuerService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
@@ -51,7 +52,7 @@ public void setIssuer(String issuer) {
/**
* Always returns the configured issuer URL
- *
+ *
* @see org.mitre.openid.connect.client.service.IssuerService#getIssuer(javax.servlet.http.HttpServletRequest)
*/
@Override
@@ -60,7 +61,7 @@ public IssuerServiceResponse getIssuer(HttpServletRequest request) {
}
@PostConstruct
- public void afterPropertiesSet() throws Exception {
+ public void afterPropertiesSet() {
if (Strings.isNullOrEmpty(issuer)) {
throw new IllegalArgumentException("Issuer must not be null or empty.");
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/ThirdPartyIssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/ThirdPartyIssuerService.java
index e6de8f4191..b26b91c897 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/ThirdPartyIssuerService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/ThirdPartyIssuerService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
@@ -34,9 +35,9 @@
import com.google.common.base.Strings;
/**
- *
+ *
* Determines the issuer using an account chooser or other third-party-initiated login
- *
+ *
* @author jricher
*
*/
@@ -44,8 +45,8 @@ public class ThirdPartyIssuerService implements IssuerService {
private String accountChooserUrl;
- private Set whitelist = new HashSet();
- private Set blacklist = new HashSet();
+ private Set whitelist = new HashSet<>();
+ private Set blacklist = new HashSet<>();
/* (non-Javadoc)
* @see org.mitre.openid.connect.client.service.IssuerService#getIssuer(javax.servlet.http.HttpServletRequest)
@@ -127,11 +128,8 @@ public void setBlacklist(Set blacklist) {
this.blacklist = blacklist;
}
- /* (non-Javadoc)
- * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
- */
@PostConstruct
- public void afterPropertiesSet() throws Exception {
+ public void afterPropertiesSet() {
if (Strings.isNullOrEmpty(this.accountChooserUrl)) {
throw new IllegalArgumentException("Account Chooser URL cannot be null or empty");
}
diff --git a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java
index ecb71a2c62..ca2fe59494 100644
--- a/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java
+++ b/openid-connect-client/src/main/java/org/mitre/openid/connect/client/service/impl/WebfingerIssuerService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.openid.connect.client.service.impl;
@@ -27,7 +28,7 @@
import org.apache.http.client.HttpClient;
import org.apache.http.client.utils.URIBuilder;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.mitre.discovery.util.WebfingerURLNormalizer;
import org.mitre.openid.connect.client.model.IssuerServiceResponse;
import org.mitre.openid.connect.client.service.IssuerService;
@@ -35,6 +36,7 @@
import org.slf4j.LoggerFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.security.authentication.AuthenticationServiceException;
+import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponents;
@@ -46,6 +48,7 @@
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
/**
@@ -55,13 +58,26 @@
*/
public class WebfingerIssuerService implements IssuerService {
- private static Logger logger = LoggerFactory.getLogger(WebfingerIssuerService.class);
+ /**
+ * Logger for this class
+ */
+ private static final Logger logger = LoggerFactory.getLogger(WebfingerIssuerService.class);
// map of user input -> issuer, loaded dynamically from webfinger discover
- private LoadingCache issuers;
+ private LoadingCache issuers;
+
+ // private data shuttle class to get back two bits of info from the cache loader
+ private class LoadingResult {
+ public String loginHint;
+ public String issuer;
+ public LoadingResult(String loginHint, String issuer) {
+ this.loginHint = loginHint;
+ this.issuer = issuer;
+ }
+ }
- private Set whitelist = new HashSet();
- private Set blacklist = new HashSet();
+ private Set whitelist = new HashSet<>();
+ private Set blacklist = new HashSet<>();
/**
* Name of the incoming parameter to check for discovery purposes.
@@ -73,8 +89,17 @@ public class WebfingerIssuerService implements IssuerService {
*/
private String loginPageUrl;
+ /**
+ * Strict enfocement of "https"
+ */
+ private boolean forceHttps = true;
+
public WebfingerIssuerService() {
- issuers = CacheBuilder.newBuilder().build(new WebfingerIssuerFetcher());
+ this(HttpClientBuilder.create().useSystemProperties().build());
+ }
+
+ public WebfingerIssuerService(HttpClient httpClient) {
+ issuers = CacheBuilder.newBuilder().build(new WebfingerIssuerFetcher(httpClient));
}
/* (non-Javadoc)
@@ -86,21 +111,18 @@ public IssuerServiceResponse getIssuer(HttpServletRequest request) {
String identifier = request.getParameter(parameterName);
if (!Strings.isNullOrEmpty(identifier)) {
try {
- String issuer = issuers.get(WebfingerURLNormalizer.normalizeResource(identifier));
- if (!whitelist.isEmpty() && !whitelist.contains(issuer)) {
- throw new AuthenticationServiceException("Whitelist was nonempty, issuer was not in whitelist: " + issuer);
+ LoadingResult lr = issuers.get(identifier);
+ if (!whitelist.isEmpty() && !whitelist.contains(lr.issuer)) {
+ throw new AuthenticationServiceException("Whitelist was nonempty, issuer was not in whitelist: " + lr.issuer);
}
- if (blacklist.contains(issuer)) {
- throw new AuthenticationServiceException("Issuer was in blacklist: " + issuer);
+ if (blacklist.contains(lr.issuer)) {
+ throw new AuthenticationServiceException("Issuer was in blacklist: " + lr.issuer);
}
- return new IssuerServiceResponse(issuer, null, null);
- } catch (UncheckedExecutionException ue) {
- logger.warn("Issue fetching issuer for user input: " + identifier, ue);
- return null;
- } catch (ExecutionException e) {
- logger.warn("Issue fetching issuer for user input: " + identifier, e);
+ return new IssuerServiceResponse(lr.issuer, lr.loginHint, request.getParameter("target_link_uri"));
+ } catch (UncheckedExecutionException | ExecutionException e) {
+ logger.warn("Issue fetching issuer for user input: " + identifier + ": " + e.getMessage());
return null;
}
@@ -167,27 +189,52 @@ public void setBlacklist(Set blacklist) {
this.blacklist = blacklist;
}
+ /**
+ * @return the forceHttps
+ */
+ public boolean isForceHttps() {
+ return forceHttps;
+ }
+
+ /**
+ * @param forceHttps the forceHttps to set
+ */
+ public void setForceHttps(boolean forceHttps) {
+ this.forceHttps = forceHttps;
+ }
+
/**
* @author jricher
*
*/
- private class WebfingerIssuerFetcher extends CacheLoader {
- private HttpClient httpClient = new SystemDefaultHttpClient();
- private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
+ private class WebfingerIssuerFetcher extends CacheLoader {
+ private HttpComponentsClientHttpRequestFactory httpFactory;
private JsonParser parser = new JsonParser();
+ WebfingerIssuerFetcher(HttpClient httpClient) {
+ this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
+ }
+
@Override
- public String load(UriComponents key) throws Exception {
+ public LoadingResult load(String identifier) throws Exception {
+
+ UriComponents key = WebfingerURLNormalizer.normalizeResource(identifier);
RestTemplate restTemplate = new RestTemplate(httpFactory);
// construct the URL to go to
- // preserving http scheme is strictly for demo system use only.
String scheme = key.getScheme();
- if (!Strings.isNullOrEmpty(scheme) && scheme.equals("http")) {
- scheme = "http://"; // add on colon and slashes.
- logger.warn("Webfinger endpoint MUST use the https URI scheme.");
+
+ // preserving http scheme is strictly for demo system use only.
+ if (!Strings.isNullOrEmpty(scheme) &&scheme.equals("http")) {
+ if (forceHttps) {
+ throw new IllegalArgumentException("Scheme must not be 'http'");
+ } else {
+ logger.warn("Webfinger endpoint MUST use the https URI scheme, overriding by configuration");
+ scheme = "http://"; // add on colon and slashes.
+ }
} else {
+ // otherwise we don't know the scheme, assume HTTPS
scheme = "https://";
}
@@ -199,46 +246,56 @@ public String load(UriComponents key) throws Exception {
+ "/.well-known/webfinger"
+ (Strings.isNullOrEmpty(key.getQuery()) ? "" : "?" + key.getQuery())
);
- builder.addParameter("resource", key.toString());
+ builder.addParameter("resource", identifier);
builder.addParameter("rel", "http://openid.net/specs/connect/1.0/issuer");
- // do the fetch
- logger.info("Loading: " + builder.toString());
- String webfingerResponse = restTemplate.getForObject(builder.build(), String.class);
-
- // TODO: catch and handle HTTP errors
-
- JsonElement json = parser.parse(webfingerResponse);
-
- // TODO: catch and handle JSON errors
-
- if (json != null && json.isJsonObject()) {
- // find the issuer
- JsonArray links = json.getAsJsonObject().get("links").getAsJsonArray();
- for (JsonElement link : links) {
- if (link.isJsonObject()) {
- JsonObject linkObj = link.getAsJsonObject();
- if (linkObj.has("href")
- && linkObj.has("rel")
- && linkObj.get("rel").getAsString().equals("http://openid.net/specs/connect/1.0/issuer")) {
+ try {
- // we found the issuer, return it
- return linkObj.get("href").getAsString();
+ // do the fetch
+ logger.info("Loading: " + builder.toString());
+ String webfingerResponse = restTemplate.getForObject(builder.build(), String.class);
+
+ JsonElement json = parser.parse(webfingerResponse);
+
+ if (json != null && json.isJsonObject()) {
+ // find the issuer
+ JsonArray links = json.getAsJsonObject().get("links").getAsJsonArray();
+ for (JsonElement link : links) {
+ if (link.isJsonObject()) {
+ JsonObject linkObj = link.getAsJsonObject();
+ if (linkObj.has("href")
+ && linkObj.has("rel")
+ && linkObj.get("rel").getAsString().equals("http://openid.net/specs/connect/1.0/issuer")) {
+
+ // we found the issuer, return it
+ String href = linkObj.get("href").getAsString();
+
+ if (identifier.equals(href)
+ || identifier.startsWith("http")) {
+ // try to avoid sending a URL as the login hint
+ return new LoadingResult(null, href);
+ } else {
+ // otherwise pass back whatever the user typed as a login hint
+ return new LoadingResult(identifier, href);
+ }
+ }
}
}
}
+ } catch (JsonParseException | RestClientException e) {
+ logger.warn("Failure in fetching webfinger input", e.getMessage());
}
- // we couldn't find it
+ // we couldn't find it!
if (key.getScheme().equals("http") || key.getScheme().equals("https")) {
- // if it looks like HTTP then punt and return the input
- logger.warn("Returning normalized input string as issuer, hoping for the best: " + key.toString());
- return key.toString();
+ // if it looks like HTTP then punt: return the input, hope for the best
+ logger.warn("Returning normalized input string as issuer, hoping for the best: " + identifier);
+ return new LoadingResult(null, identifier);
} else {
// if it's not HTTP, give up
- logger.warn("Couldn't find issuer: " + key.toString());
- return null;
+ logger.warn("Couldn't find issuer: " + identifier);
+ throw new IllegalArgumentException();
}
}
diff --git a/openid-connect-client/src/test/java/org/mitre/oauth2/introspectingfilter/TestOAuth2AccessTokenImpl.java b/openid-connect-client/src/test/java/org/mitre/oauth2/introspectingfilter/TestOAuth2AccessTokenImpl.java
new file mode 100644
index 0000000000..051b5a26c2
--- /dev/null
+++ b/openid-connect-client/src/test/java/org/mitre/oauth2/introspectingfilter/TestOAuth2AccessTokenImpl.java
@@ -0,0 +1,106 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.mitre.oauth2.introspectingfilter;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.Set;
+
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.JsonObject;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+
+import static org.junit.Assert.assertThat;
+
+public class TestOAuth2AccessTokenImpl {
+
+ private static String tokenString = "thisisatokenstring";
+
+ private static Set scopes = ImmutableSet.of("bar", "foo");
+ private static String scopeString = "foo bar";
+
+ private static Date exp = new Date(123 * 1000L);
+ private static Long expVal = 123L;
+
+ @Test
+ public void testFullToken() {
+
+
+ JsonObject tokenObj = new JsonObject();
+ tokenObj.addProperty("active", true);
+ tokenObj.addProperty("scope", scopeString);
+ tokenObj.addProperty("exp", expVal);
+ tokenObj.addProperty("sub", "subject");
+ tokenObj.addProperty("client_id", "123-456-789");
+
+ OAuth2AccessTokenImpl tok = new OAuth2AccessTokenImpl(tokenObj, tokenString);
+
+ assertThat(tok.getScope(), is(equalTo(scopes)));
+ assertThat(tok.getExpiration(), is(equalTo(exp)));
+ }
+
+ @Test
+ public void testNullExp() {
+
+
+ JsonObject tokenObj = new JsonObject();
+ tokenObj.addProperty("active", true);
+ tokenObj.addProperty("scope", scopeString);
+ tokenObj.addProperty("sub", "subject");
+ tokenObj.addProperty("client_id", "123-456-789");
+
+ OAuth2AccessTokenImpl tok = new OAuth2AccessTokenImpl(tokenObj, tokenString);
+
+ assertThat(tok.getScope(), is(equalTo(scopes)));
+ assertThat(tok.getExpiration(), is(equalTo(null)));
+ }
+
+ @Test
+ public void testNullScopes() {
+
+
+ JsonObject tokenObj = new JsonObject();
+ tokenObj.addProperty("active", true);
+ tokenObj.addProperty("exp", expVal);
+ tokenObj.addProperty("sub", "subject");
+ tokenObj.addProperty("client_id", "123-456-789");
+
+ OAuth2AccessTokenImpl tok = new OAuth2AccessTokenImpl(tokenObj, tokenString);
+
+ assertThat(tok.getScope(), is(equalTo(Collections.EMPTY_SET)));
+ assertThat(tok.getExpiration(), is(equalTo(exp)));
+ }
+
+ @Test
+ public void testNullScopesNullExp() {
+
+
+ JsonObject tokenObj = new JsonObject();
+ tokenObj.addProperty("active", true);
+ tokenObj.addProperty("sub", "subject");
+ tokenObj.addProperty("client_id", "123-456-789");
+
+ OAuth2AccessTokenImpl tok = new OAuth2AccessTokenImpl(tokenObj, tokenString);
+
+ assertThat(tok.getScope(), is(equalTo(Collections.EMPTY_SET)));
+ assertThat(tok.getExpiration(), is(equalTo(null)));
+ }
+
+}
diff --git a/openid-connect-client/src/test/java/org/mitre/oauth2/introspectingfilter/service/impl/TestScopeBasedIntrospectionAuthoritiesGranter.java b/openid-connect-client/src/test/java/org/mitre/oauth2/introspectingfilter/service/impl/TestScopeBasedIntrospectionAuthoritiesGranter.java
new file mode 100644
index 0000000000..2323019eff
--- /dev/null
+++ b/openid-connect-client/src/test/java/org/mitre/oauth2/introspectingfilter/service/impl/TestScopeBasedIntrospectionAuthoritiesGranter.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+
+package org.mitre.oauth2.introspectingfilter.service.impl;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+
+import com.google.gson.JsonObject;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author jricher
+ *
+ */
+public class TestScopeBasedIntrospectionAuthoritiesGranter {
+
+ private JsonObject introspectionResponse;
+
+ private ScopeBasedIntrospectionAuthoritiesGranter granter = new ScopeBasedIntrospectionAuthoritiesGranter();
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ introspectionResponse = new JsonObject();
+ }
+
+ /**
+ * Test method for {@link org.mitre.oauth2.introspectingfilter.service.impl.ScopeBasedIntrospectionAuthoritiesGranter#getAuthorities(com.google.gson.JsonObject)}.
+ */
+ @Test
+ public void testGetAuthoritiesJsonObject_withScopes() {
+ introspectionResponse.addProperty("scope", "foo bar baz batman");
+
+ List expected = new ArrayList<>();
+ expected.add(new SimpleGrantedAuthority("ROLE_API"));
+ expected.add(new SimpleGrantedAuthority("OAUTH_SCOPE_foo"));
+ expected.add(new SimpleGrantedAuthority("OAUTH_SCOPE_bar"));
+ expected.add(new SimpleGrantedAuthority("OAUTH_SCOPE_baz"));
+ expected.add(new SimpleGrantedAuthority("OAUTH_SCOPE_batman"));
+
+ List authorities = granter.getAuthorities(introspectionResponse);
+
+ assertTrue(authorities.containsAll(expected));
+ assertTrue(expected.containsAll(authorities));
+ }
+
+ /**
+ * Test method for {@link org.mitre.oauth2.introspectingfilter.service.impl.ScopeBasedIntrospectionAuthoritiesGranter#getAuthorities(com.google.gson.JsonObject)}.
+ */
+ @Test
+ public void testGetAuthoritiesJsonObject_withoutScopes() {
+
+ List expected = new ArrayList<>();
+ expected.add(new SimpleGrantedAuthority("ROLE_API"));
+
+ List authorities = granter.getAuthorities(introspectionResponse);
+
+ assertTrue(authorities.containsAll(expected));
+ assertTrue(expected.containsAll(authorities));
+ }
+
+}
diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java
new file mode 100644
index 0000000000..ae3018bbc8
--- /dev/null
+++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/TestOIDCAuthenticationFilter.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.mitre.openid.connect.client;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.springframework.security.authentication.AuthenticationServiceException;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+
+import static org.mockito.Mockito.mock;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+
+public class TestOIDCAuthenticationFilter {
+
+ private OIDCAuthenticationFilter filter = new OIDCAuthenticationFilter();
+
+ @Test
+ public void attemptAuthentication_error() throws Exception {
+
+ HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
+ Mockito.when(request.getParameter("error")).thenReturn("Error");
+ Mockito.when(request.getParameter("error_description")).thenReturn("Description");
+ Mockito.when(request.getParameter("error_uri")).thenReturn("http://example.com");
+
+ try {
+ filter.attemptAuthentication(request, mock(HttpServletResponse.class));
+
+ fail("AuthorizationEndpointException expected.");
+ }
+ catch (AuthorizationEndpointException exception) {
+ assertThat(exception.getMessage(),
+ is("Error from Authorization Endpoint: Error Description http://example.com"));
+
+ assertThat(exception.getError(), is("Error"));
+ assertThat(exception.getErrorDescription(), is("Description"));
+ assertThat(exception.getErrorURI(), is("http://example.com"));
+
+ assertThat(exception, is(instanceOf(AuthenticationServiceException.class)));
+ }
+ }
+}
diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestHybridClientConfigurationService.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestHybridClientConfigurationService.java
index 373fb420b5..f7455981d9 100644
--- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestHybridClientConfigurationService.java
+++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestHybridClientConfigurationService.java
@@ -1,26 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.openid.connect.client.service.impl;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,6 +28,12 @@
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
/**
* @author wkim
*
diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestHybridServerConfigurationService.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestHybridServerConfigurationService.java
index 07827c2672..c14e756f14 100644
--- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestHybridServerConfigurationService.java
+++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestHybridServerConfigurationService.java
@@ -1,27 +1,23 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.openid.connect.client.service.impl;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,6 +28,12 @@
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.nullValue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
/**
* @author wkim
*
diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestPlainAuthRequestUrlBuilder.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestPlainAuthRequestUrlBuilder.java
index 6f7a735080..391afb612c 100644
--- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestPlainAuthRequestUrlBuilder.java
+++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestPlainAuthRequestUrlBuilder.java
@@ -1,24 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.openid.connect.client.service.impl;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.junit.Assert.assertThat;
-
import java.util.Map;
import org.junit.Before;
@@ -31,6 +29,10 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
+import static org.hamcrest.CoreMatchers.equalTo;
+
+import static org.junit.Assert.assertThat;
+
/**
* @author wkim
*
@@ -68,7 +70,27 @@ public void buildAuthRequestUrl() {
Map options = ImmutableMap.of("foo", "bar");
- String actualUrl = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "https://client.example.org/", "34fasf3ds", "af0ifjsldkj", options);
+ String actualUrl = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "https://client.example.org/", "34fasf3ds", "af0ifjsldkj", options, null);
+
+ assertThat(actualUrl, equalTo(expectedUrl));
+ }
+
+ @Test
+ public void buildAuthRequestUrl_withLoginHint() {
+
+ String expectedUrl = "https://server.example.com/authorize?" +
+ "response_type=code" +
+ "&client_id=s6BhdRkqt3" +
+ "&scope=openid+profile" + // plus sign used for space per application/x-www-form-encoded standard
+ "&redirect_uri=https%3A%2F%2Fclient.example.org%2F" +
+ "&nonce=34fasf3ds" +
+ "&state=af0ifjsldkj" +
+ "&foo=bar" +
+ "&login_hint=bob";
+
+ Map options = ImmutableMap.of("foo", "bar");
+
+ String actualUrl = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "https://client.example.org/", "34fasf3ds", "af0ifjsldkj", options, "bob");
assertThat(actualUrl, equalTo(expectedUrl));
}
@@ -80,7 +102,7 @@ public void buildAuthRequestUrl_badUri() {
Map options = ImmutableMap.of("foo", "bar");
- urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options);
+ urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options, null);
}
}
diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java
index 34eead9dd3..1e733cadbf 100644
--- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java
+++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestSignedAuthRequestUrlBuilder.java
@@ -1,25 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.openid.connect.client.service.impl;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
import java.net.URI;
import java.net.URISyntaxException;
import java.security.NoSuchAlgorithmException;
@@ -31,7 +28,7 @@
import org.junit.Before;
import org.junit.Test;
-import org.mitre.jwt.signer.service.impl.DefaultJwtSigningAndValidationService;
+import org.mitre.jwt.signer.service.impl.DefaultJWTSigningAndValidationService;
import org.mitre.oauth2.model.RegisteredClient;
import org.mitre.openid.connect.config.ServerConfiguration;
import org.mockito.Mockito;
@@ -44,15 +41,19 @@
import com.google.common.collect.Sets;
import com.nimbusds.jose.Algorithm;
import com.nimbusds.jose.jwk.JWK;
-import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.KeyUse;
+import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.util.Base64URL;
-import com.nimbusds.jwt.ReadOnlyJWTClaimsSet;
+import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
/**
* @author wkim
- *
+ *
*/
public class TestSignedAuthRequestUrlBuilder {
@@ -82,19 +83,20 @@ public class TestSignedAuthRequestUrlBuilder {
"9Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q";
private String alg = "RS256";
private String kid = "2011-04-29";
+ private String loginHint = "bob";
- private DefaultJwtSigningAndValidationService signingAndValidationService;
+ private DefaultJWTSigningAndValidationService signingAndValidationService;
private SignedAuthRequestUrlBuilder urlBuilder = new SignedAuthRequestUrlBuilder();
@Before
public void prepare() throws NoSuchAlgorithmException, InvalidKeySpecException {
- RSAKey key = new RSAKey(new Base64URL(n), new Base64URL(e), new Base64URL(d), KeyUse.SIGNATURE, null, new Algorithm(alg), kid, null, null, null);
+ RSAKey key = new RSAKey(new Base64URL(n), new Base64URL(e), new Base64URL(d), KeyUse.SIGNATURE, null, new Algorithm(alg), kid, null, null, null, null, null);
Map keys = Maps.newHashMap();
keys.put("client", key);
- signingAndValidationService = new DefaultJwtSigningAndValidationService(keys);
+ signingAndValidationService = new DefaultJWTSigningAndValidationService(keys);
signingAndValidationService.setDefaultSignerKeyId("client");
signingAndValidationService.setDefaultSigningAlgorithmName(alg);
@@ -116,7 +118,46 @@ public void prepare() throws NoSuchAlgorithmException, InvalidKeySpecException {
@Test
public void buildAuthRequestUrl() {
- String requestUri = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options);
+ String requestUri = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options, null);
+
+ // parsing the result
+ UriComponentsBuilder builder = null;
+
+ try {
+ builder = UriComponentsBuilder.fromUri(new URI(requestUri));
+ } catch (URISyntaxException e1) {
+ fail("URISyntaxException was thrown.");
+ }
+
+ UriComponents components = builder.build();
+ String jwtString = components.getQueryParams().get("request").get(0);
+ JWTClaimsSet claims = null;
+
+ try {
+ SignedJWT jwt = SignedJWT.parse(jwtString);
+ claims = jwt.getJWTClaimsSet();
+ } catch (ParseException e) {
+ fail("ParseException was thrown.");
+ }
+
+ assertEquals(responseType, claims.getClaim("response_type"));
+ assertEquals(clientConfig.getClientId(), claims.getClaim("client_id"));
+
+ List scopeList = Arrays.asList(((String) claims.getClaim("scope")).split(" "));
+ assertTrue(scopeList.containsAll(clientConfig.getScope()));
+
+ assertEquals(redirectUri, claims.getClaim("redirect_uri"));
+ assertEquals(nonce, claims.getClaim("nonce"));
+ assertEquals(state, claims.getClaim("state"));
+ for (String claim : options.keySet()) {
+ assertEquals(options.get(claim), claims.getClaim(claim));
+ }
+ }
+
+ @Test
+ public void buildAuthRequestUrl_withLoginHint() {
+
+ String requestUri = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options, loginHint);
// parsing the result
UriComponentsBuilder builder = null;
@@ -129,7 +170,7 @@ public void buildAuthRequestUrl() {
UriComponents components = builder.build();
String jwtString = components.getQueryParams().get("request").get(0);
- ReadOnlyJWTClaimsSet claims = null;
+ JWTClaimsSet claims = null;
try {
SignedJWT jwt = SignedJWT.parse(jwtString);
@@ -150,6 +191,7 @@ public void buildAuthRequestUrl() {
for (String claim : options.keySet()) {
assertEquals(options.get(claim), claims.getClaim(claim));
}
+ assertEquals(loginHint, claims.getClaim("login_hint"));
}
@Test(expected = AuthenticationServiceException.class)
@@ -157,6 +199,6 @@ public void buildAuthRequestUrl_badUri() {
Mockito.when(serverConfig.getAuthorizationEndpointUri()).thenReturn("e=mc^2");
- urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options);
+ urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options, null);
}
}
diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestStaticClientConfigurationService.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestStaticClientConfigurationService.java
index 1c921bc5d5..4f251a4e3c 100644
--- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestStaticClientConfigurationService.java
+++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestStaticClientConfigurationService.java
@@ -1,27 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.openid.connect.client.service.impl;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
import java.util.HashMap;
import java.util.Map;
@@ -34,6 +29,13 @@
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
/**
* @author wkim
*
@@ -56,7 +58,7 @@ public void prepare() {
service = new StaticClientConfigurationService();
- Map clients = new HashMap();
+ Map clients = new HashMap<>();
clients.put(issuer, mockClient);
service.setClients(clients);
diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestStaticServerConfigurationService.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestStaticServerConfigurationService.java
index 6e819949a4..9f86bd3469 100644
--- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestStaticServerConfigurationService.java
+++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestStaticServerConfigurationService.java
@@ -1,27 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.openid.connect.client.service.impl;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-
import java.util.HashMap;
import java.util.Map;
@@ -32,6 +27,13 @@
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
/**
* @author wkim
*
@@ -52,7 +54,7 @@ public void prepare() {
service = new StaticServerConfigurationService();
- Map servers = new HashMap();
+ Map servers = new HashMap<>();
servers.put(issuer, mockServerConfig);
service.setServers(servers);
diff --git a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestThirdPartyIssuerService.java b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestThirdPartyIssuerService.java
index f32a07fd46..7a54e7d16c 100644
--- a/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestThirdPartyIssuerService.java
+++ b/openid-connect-client/src/test/java/org/mitre/openid/connect/client/service/impl/TestThirdPartyIssuerService.java
@@ -1,25 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.openid.connect.client.service.impl;
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.junit.Assert.assertThat;
-
import javax.servlet.http.HttpServletRequest;
import org.junit.Before;
@@ -30,6 +27,11 @@
import com.google.common.collect.Sets;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.nullValue;
+
+import static org.junit.Assert.assertThat;
+
/**
* @author wkim
*
diff --git a/openid-connect-client/src/test/resources/test-context.xml b/openid-connect-client/src/test/resources/test-context.xml
index d96dd5d9bf..51d33c9227 100644
--- a/openid-connect-client/src/test/resources/test-context.xml
+++ b/openid-connect-client/src/test/resources/test-context.xml
@@ -1,20 +1,21 @@
+ Copyright 2018 The MIT Internet Trust Consortium
+
+ Portions copyright 2011-2013 The MITRE Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+ Copyright 2018 The MIT Internet Trust Consortium
+
+ Portions copyright 2011-2013 The MITRE Corporation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
- 4.0.0
-
- openid-connect-parent
- org.mitre
- 1.1.10-SNAPSHOT
- ..
-
- openid-connect-common
- OpenID Connect Common modules
- OpenID Connect Common
-
-
- org.springframework
- spring-core
- ${org.springframework-version}
-
-
- org.springframework
- spring-webmvc
- ${org.springframework-version}
-
-
- org.springframework.security
- spring-security-core
- ${spring.security.version}
-
-
- org.springframework
- *
-
-
-
-
- com.google.guava
- guava
- 16.0
-
-
- org.apache.httpcomponents
- httpclient
- 4.2.3
-
-
- org.springframework.security.oauth
- 2.0.2.RELEASE
- spring-security-oauth2
-
-
- com.nimbusds
- nimbus-jose-jwt
- 2.26.1
-
-
- org.eclipse.persistence
- javax.persistence
- 2.1.0
-
-
- com.google.code.gson
- gson
- 2.0
-
-
-
-
- org.slf4j
- slf4j-api
- ${org.slf4j-version}
-
-
+ 4.0.0
+
+ openid-connect-parent
+ org.mitre
+ 1.3.5-SNAPSHOT
+ ..
+
+ openid-connect-common
+ OpenID Connect Common modules
+ OpenID Connect Common
+
+
+ org.springframework
+ spring-core
+
+
+ commons-logging
+ commons-logging
+
+
+
+
+ org.springframework
+ spring-webmvc
+
+
+ org.springframework.security
+ spring-security-core
+
+
+ com.google.guava
+ guava
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.springframework.security.oauth
+ spring-security-oauth2
+
+
+ com.nimbusds
+ nimbus-jose-jwt
+
+
+ org.eclipse.persistence
+ javax.persistence
+
+
+ com.google.code.gson
+ gson
+
+
+ org.slf4j
+ slf4j-api
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+
+
+ javax.annotation
+ javax.annotation-api
+
+
+ jakarta.xml.bind
+ jakarta.xml.bind-api
+
+
+ javax.xml.bind
+ jaxb-api
+
+
+ javax.activation
+ activation
+
+
+ org.glassfish.jaxb
+ jaxb-runtime
+
+
+
+ jar
-
- jar
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- ${java-version}
- ${java-version}
-
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
-
-
- attach-sources
-
- jar
-
-
-
-
-
-
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${java-version}
+ ${java-version}
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
diff --git a/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java b/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java
new file mode 100644
index 0000000000..1962366f8c
--- /dev/null
+++ b/openid-connect-common/src/main/java/org/mitre/data/AbstractPageOperationTemplate.java
@@ -0,0 +1,206 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.mitre.data;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract class for performing an operation on a potentially large
+ * number of items by paging through the items in discreet chunks.
+ *
+ * @param the type parameter
+ * @author Colm Smyth.
+ */
+public abstract class AbstractPageOperationTemplate {
+
+ private static final Logger logger = LoggerFactory.getLogger(AbstractPageOperationTemplate.class);
+
+ private static int DEFAULT_MAX_PAGES = 1000;
+ private static long DEFAULT_MAX_TIME_MILLIS = 600000L; //10 Minutes
+
+ /**
+ * int specifying the maximum number of
+ * pages which should be fetched before
+ * execution should terminate
+ */
+ private int maxPages;
+
+ /**
+ * long specifying the maximum execution time
+ * in milliseconds
+ */
+ private long maxTime;
+
+ /**
+ * boolean specifying whether or not Exceptions
+ * incurred performing the operation should be
+ * swallowed during execution default true.
+ */
+ private boolean swallowExceptions = true;
+
+ /**
+ * String that is used for logging in final tallies.
+ */
+ private String operationName = "";
+
+
+ /**
+ * default constructor which sets the value of
+ * maxPages and maxTime to DEFAULT_MAX_PAGES and
+ * DEFAULT_MAX_TIME_MILLIS respectively
+ */
+ public AbstractPageOperationTemplate(String operationName){
+ this(DEFAULT_MAX_PAGES, DEFAULT_MAX_TIME_MILLIS, operationName);
+ }
+
+ /**
+ * Instantiates a new AbstractPageOperationTemplate with the
+ * given maxPages and maxTime
+ *
+ * @param maxPages the maximum number of pages to fetch.
+ * @param maxTime the maximum execution time.
+ */
+ public AbstractPageOperationTemplate(int maxPages, long maxTime, String operationName){
+ this.maxPages = maxPages;
+ this.maxTime = maxTime;
+ this.operationName = operationName;
+ }
+
+ /**
+ * Execute the operation on each member of a page of results
+ * retrieved through the fetch method. the method will execute
+ * until either the maxPages or maxTime limit is reached or until
+ * the fetch method returns no more results. Exceptions thrown
+ * performing the operation on the item will be swallowed if the
+ * swallowException (default true) field is set true.
+ */
+ public void execute(){
+ logger.debug("[" + getOperationName() + "] Starting execution of paged operation. maximum time: " + maxTime + ", maximum pages: " + maxPages);
+
+ long startTime = System.currentTimeMillis();
+ long executionTime = 0;
+ int i = 0;
+
+ int exceptionsSwallowedCount = 0;
+ int operationsCompleted = 0;
+ Set exceptionsSwallowedClasses = new HashSet();
+
+
+ while (i< maxPages && executionTime < maxTime){
+ Collection page = fetchPage();
+ if(page == null || page.size() == 0){
+ break;
+ }
+
+ for (T item : page) {
+ try {
+ doOperation(item);
+ operationsCompleted++;
+ } catch (Exception e){
+ if(swallowExceptions){
+ exceptionsSwallowedCount++;
+ exceptionsSwallowedClasses.add(e.getClass().getName());
+ logger.debug("Swallowing exception " + e.getMessage(), e);
+ } else {
+ logger.debug("Rethrowing exception " + e.getMessage());
+ throw e;
+ }
+ }
+ }
+
+ i++;
+ executionTime = System.currentTimeMillis() - startTime;
+ }
+
+ finalReport(operationsCompleted, exceptionsSwallowedCount, exceptionsSwallowedClasses);
+ }
+
+
+
+ /**
+ * method responsible for fetching
+ * a page of items.
+ *
+ * @return the collection of items
+ */
+ public abstract Collection fetchPage();
+
+ /**
+ * method responsible for performing desired
+ * operation on a fetched page item.
+ *
+ * @param item the item
+ */
+ protected abstract void doOperation(T item);
+
+ /**
+ * Method responsible for final report of progress.
+ * @return
+ */
+ protected void finalReport(int operationsCompleted, int exceptionsSwallowedCount, Set exceptionsSwallowedClasses) {
+ if (operationsCompleted > 0 || exceptionsSwallowedCount > 0) {
+ logger.info("[" + getOperationName() + "] Paged operation run: completed " + operationsCompleted + "; swallowed " + exceptionsSwallowedCount + " exceptions");
+ }
+ for(String className: exceptionsSwallowedClasses) {
+ logger.warn("[" + getOperationName() + "] Paged operation swallowed at least one exception of type " + className);
+ }
+ }
+
+ public int getMaxPages() {
+ return maxPages;
+ }
+
+ public void setMaxPages(int maxPages) {
+ this.maxPages = maxPages;
+ }
+
+ public long getMaxTime() {
+ return maxTime;
+ }
+
+ public void setMaxTime(long maxTime) {
+ this.maxTime = maxTime;
+ }
+
+ public boolean isSwallowExceptions() {
+ return swallowExceptions;
+ }
+
+ public void setSwallowExceptions(boolean swallowExceptions) {
+ this.swallowExceptions = swallowExceptions;
+ }
+
+
+ /**
+ * @return the operationName
+ */
+ public String getOperationName() {
+ return operationName;
+ }
+
+
+ /**
+ * @param operationName the operationName to set
+ */
+ public void setOperationName(String operationName) {
+ this.operationName = operationName;
+ }
+}
diff --git a/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java b/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java
new file mode 100644
index 0000000000..daec512b0a
--- /dev/null
+++ b/openid-connect-common/src/main/java/org/mitre/data/DefaultPageCriteria.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.mitre.data;
+
+/**
+ * Default implementation of PageCriteria which specifies
+ * both page to be retrieved and page size in the constructor.
+ *
+ * @author Colm Smyth
+ */
+public class DefaultPageCriteria implements PageCriteria {
+
+ private static final int DEFAULT_PAGE_NUMBER = 0;
+ private static final int DEFAULT_PAGE_SIZE = 100;
+
+ private int pageNumber;
+ private int pageSize;
+
+ public DefaultPageCriteria(){
+ this(DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_SIZE);
+ }
+
+ public DefaultPageCriteria(int pageNumber, int pageSize) {
+ this.pageNumber = pageNumber;
+ this.pageSize = pageSize;
+ }
+
+ @Override
+ public int getPageNumber() {
+ return pageNumber;
+ }
+
+ @Override
+ public int getPageSize() {
+ return pageSize;
+ }
+}
diff --git a/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java b/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java
new file mode 100644
index 0000000000..2db1077d94
--- /dev/null
+++ b/openid-connect-common/src/main/java/org/mitre/data/PageCriteria.java
@@ -0,0 +1,28 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+package org.mitre.data;
+
+/**
+ * Interface which defines page criteria for use in
+ * a repository operation.
+ *
+ * @author Colm Smyth
+ */
+public interface PageCriteria {
+
+ public int getPageNumber();
+ public int getPageSize();
+}
diff --git a/openid-connect-common/src/main/java/org/mitre/discovery/util/WebfingerURLNormalizer.java b/openid-connect-common/src/main/java/org/mitre/discovery/util/WebfingerURLNormalizer.java
index 84e63cf400..b71fbf6d6a 100644
--- a/openid-connect-common/src/main/java/org/mitre/discovery/util/WebfingerURLNormalizer.java
+++ b/openid-connect-common/src/main/java/org/mitre/discovery/util/WebfingerURLNormalizer.java
@@ -1,19 +1,20 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.discovery.util;
import java.util.regex.Matcher;
@@ -29,13 +30,16 @@
/**
* Provides utility methods for normalizing and parsing URIs for use with Webfinger Discovery.
- *
+ *
* @author wkim
*
*/
public class WebfingerURLNormalizer {
- private static Logger logger = LoggerFactory.getLogger(WebfingerURLNormalizer.class);
+ /**
+ * Logger for this class
+ */
+ private static final Logger logger = LoggerFactory.getLogger(WebfingerURLNormalizer.class);
// pattern used to parse user input; we can't use the built-in java URI parser
private static final Pattern pattern = Pattern.compile("^" +
diff --git a/openid-connect-common/src/main/java/org/mitre/jose/JWEAlgorithmEmbed.java b/openid-connect-common/src/main/java/org/mitre/jose/JWEAlgorithmEmbed.java
deleted file mode 100644
index 8e6fd44b37..0000000000
--- a/openid-connect-common/src/main/java/org/mitre/jose/JWEAlgorithmEmbed.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- ******************************************************************************/
-/**
- *
- */
-package org.mitre.jose;
-
-import javax.persistence.Basic;
-import javax.persistence.Embeddable;
-import javax.persistence.Transient;
-
-import com.google.common.base.Strings;
-import com.nimbusds.jose.JWEAlgorithm;
-
-/**
- *
- * Wrapper class for Nimbus JOSE objects to fit into JPA
- *
- * @author jricher
- *
- */
-@Embeddable
-public class JWEAlgorithmEmbed {
-
- public static final JWEAlgorithmEmbed NONE = getForAlgorithmName("none");
-
- private JWEAlgorithm algorithm;
-
- public JWEAlgorithmEmbed() {
-
- }
-
- public JWEAlgorithmEmbed(JWEAlgorithm algorithm) {
- this.algorithm = algorithm;
- }
-
- public static JWEAlgorithmEmbed getForAlgorithmName (String algorithmName) {
- JWEAlgorithmEmbed ent = new JWEAlgorithmEmbed();
- ent.setAlgorithmName(algorithmName);
- if (ent.getAlgorithm() == null) {
- return null;
- } else {
- return ent;
- }
- }
-
- /**
- * Get the name of this algorithm, return null if no algorithm set.
- * @return
- */
- @Basic
- public String getAlgorithmName() {
- if (algorithm != null) {
- return algorithm.getName();
- } else {
- return null;
- }
- }
-
- /**
- * Set the name of this algorithm.
- * Calls JWEAlgorithm.parse()
- * @param algorithmName
- */
- public void setAlgorithmName(String algorithmName) {
- if (!Strings.isNullOrEmpty(algorithmName)) {
- algorithm = JWEAlgorithm.parse(algorithmName);
- } else {
- algorithm = null;
- }
- }
-
- /* (non-Javadoc)
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return "JWEAlgorithmEmbed [algorithm=" + algorithm + "]";
- }
-
- /**
- * @return the algorithm
- */
- @Transient
- public JWEAlgorithm getAlgorithm() {
- return algorithm;
- }
-
- /**
- * @param algorithm the algorithm to set
- */
- public void setAlgorithm(JWEAlgorithm algorithm) {
- this.algorithm = algorithm;
- }
-
-}
diff --git a/openid-connect-common/src/main/java/org/mitre/jose/JWEEncryptionMethodEmbed.java b/openid-connect-common/src/main/java/org/mitre/jose/JWEEncryptionMethodEmbed.java
deleted file mode 100644
index f70f7e1520..0000000000
--- a/openid-connect-common/src/main/java/org/mitre/jose/JWEEncryptionMethodEmbed.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- ******************************************************************************/
-/**
- *
- */
-package org.mitre.jose;
-
-import javax.persistence.Basic;
-import javax.persistence.Embeddable;
-import javax.persistence.Transient;
-
-import com.google.common.base.Strings;
-import com.nimbusds.jose.EncryptionMethod;
-
-/**
- * @author jricher
- *
- */
-@Embeddable
-public class JWEEncryptionMethodEmbed {
-
- public static final JWEEncryptionMethodEmbed NONE = getForAlgorithmName("none");
-
- private EncryptionMethod algorithm;
-
- public JWEEncryptionMethodEmbed() {
-
- }
-
- public JWEEncryptionMethodEmbed(EncryptionMethod algorithm) {
- this.algorithm = algorithm;
- }
-
- public static JWEEncryptionMethodEmbed getForAlgorithmName (String algorithmName) {
- JWEEncryptionMethodEmbed ent = new JWEEncryptionMethodEmbed();
- ent.setAlgorithmName(algorithmName);
- if (ent.getAlgorithm() == null) {
- return null;
- } else {
- return ent;
- }
- }
-
- /**
- * Get the name of this algorithm, return null if no algorithm set.
- * @return
- */
- @Basic
- public String getAlgorithmName() {
- if (algorithm != null) {
- return algorithm.getName();
- } else {
- return null;
- }
- }
-
- /**
- * Set the name of this algorithm.
- * Calls EncryptionMethod.parse()
- * @param algorithmName
- */
- public void setAlgorithmName(String algorithmName) {
- if (!Strings.isNullOrEmpty(algorithmName)) {
- algorithm = EncryptionMethod.parse(algorithmName);
- } else {
- algorithm = null;
- }
- }
-
- /* (non-Javadoc)
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return "JWEEncryptionMethodEmbed [algorithm=" + algorithm + "]";
- }
-
- /**
- * @return the algorithm
- */
- @Transient
- public EncryptionMethod getAlgorithm() {
- return algorithm;
- }
-
- /**
- * @param algorithm the algorithm to set
- */
- public void setAlgorithm(EncryptionMethod algorithm) {
- this.algorithm = algorithm;
- }
-
-
-}
diff --git a/openid-connect-common/src/main/java/org/mitre/jose/JWSAlgorithmEmbed.java b/openid-connect-common/src/main/java/org/mitre/jose/JWSAlgorithmEmbed.java
deleted file mode 100644
index d062b063e7..0000000000
--- a/openid-connect-common/src/main/java/org/mitre/jose/JWSAlgorithmEmbed.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- ******************************************************************************/
-/**
- *
- */
-package org.mitre.jose;
-
-import javax.persistence.Basic;
-import javax.persistence.Embeddable;
-import javax.persistence.Transient;
-
-import com.google.common.base.Strings;
-import com.nimbusds.jose.JWSAlgorithm;
-
-/**
- *
- * Wrapper class for Nimbus JOSE objects to fit into JPA
- *
- * @author jricher
- *
- */
-@Embeddable
-public class JWSAlgorithmEmbed {
-
- public static final JWSAlgorithmEmbed NONE = getForAlgorithmName("none");
-
- private JWSAlgorithm algorithm;
-
- public JWSAlgorithmEmbed() {
-
- }
-
- public JWSAlgorithmEmbed(JWSAlgorithm algorithm) {
- this.algorithm = algorithm;
- }
-
- /**
- *
- * @param algorithmName
- * @return null if algorithmName is empty or null
- */
- public static JWSAlgorithmEmbed getForAlgorithmName (String algorithmName) {
- JWSAlgorithmEmbed ent = new JWSAlgorithmEmbed();
- ent.setAlgorithmName(algorithmName);
- if (ent.getAlgorithm() == null) {
- return null;
- } else {
- return ent;
- }
- }
-
- /**
- * Get the name of this algorithm, return null if no algorithm set.
- * @return
- */
- @Basic
- public String getAlgorithmName() {
- if (algorithm != null) {
- return algorithm.getName();
- } else {
- return null;
- }
- }
-
- /**
- * Set the name of this algorithm.
- * Calls JWSAlgorithm.parse()
- * @param algorithmName
- */
- public void setAlgorithmName(String algorithmName) {
- if (!Strings.isNullOrEmpty(algorithmName)) {
- algorithm = JWSAlgorithm.parse(algorithmName);
- } else {
- algorithm = null;
- }
- }
-
- /**
- * @return the algorithm
- */
- @Transient
- public JWSAlgorithm getAlgorithm() {
- return algorithm;
- }
-
- /**
- * @param algorithm the algorithm to set
- */
- public void setAlgorithm(JWSAlgorithm algorithm) {
- this.algorithm = algorithm;
- }
-
- /* (non-Javadoc)
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return "JWSAlgorithmEmbed [algorithm=" + algorithm + "]";
- }
-
-
-
-}
diff --git a/openid-connect-common/src/main/java/org/mitre/jose/keystore/JWKSetKeyStore.java b/openid-connect-common/src/main/java/org/mitre/jose/keystore/JWKSetKeyStore.java
index 06c36c3c76..f40997b8dc 100644
--- a/openid-connect-common/src/main/java/org/mitre/jose/keystore/JWKSetKeyStore.java
+++ b/openid-connect-common/src/main/java/org/mitre/jose/keystore/JWKSetKeyStore.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.jose.keystore;
diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/assertion/AssertionValidator.java b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/AssertionValidator.java
new file mode 100644
index 0000000000..ffc29a2e94
--- /dev/null
+++ b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/AssertionValidator.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+
+package org.mitre.jwt.assertion;
+
+import com.nimbusds.jwt.JWT;
+
+/**
+ * @author jricher
+ *
+ */
+public interface AssertionValidator {
+
+ public boolean isValid(JWT assertion);
+
+}
diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/NullAssertionValidator.java b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/NullAssertionValidator.java
new file mode 100644
index 0000000000..5fa53e6c85
--- /dev/null
+++ b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/NullAssertionValidator.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+
+package org.mitre.jwt.assertion.impl;
+
+import org.mitre.jwt.assertion.AssertionValidator;
+
+import com.nimbusds.jwt.JWT;
+
+/**
+ * Reject all assertions passed in.
+ *
+ * @author jricher
+ *
+ */
+public class NullAssertionValidator implements AssertionValidator {
+
+ /**
+ * Reject all assertions passed in, always returns false.
+ */
+ @Override
+ public boolean isValid(JWT assertion) {
+ return false;
+
+ }
+
+}
diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/SelfAssertionValidator.java b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/SelfAssertionValidator.java
new file mode 100644
index 0000000000..5655c93c73
--- /dev/null
+++ b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/SelfAssertionValidator.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+
+package org.mitre.jwt.assertion.impl;
+
+import java.text.ParseException;
+
+import org.mitre.jwt.assertion.AssertionValidator;
+import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
+import org.mitre.openid.connect.config.ConfigurationPropertiesBean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.google.common.base.Strings;
+import com.nimbusds.jwt.JWT;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+
+/**
+ * Validates all assertions generated by this server
+ *
+ * @author jricher
+ *
+ */
+@Component("selfAssertionValidator")
+public class SelfAssertionValidator implements AssertionValidator {
+
+ private static Logger logger = LoggerFactory.getLogger(SelfAssertionValidator.class);
+
+ @Autowired
+ private ConfigurationPropertiesBean config;
+
+ @Autowired
+ private JWTSigningAndValidationService jwtService;
+
+ @Override
+ public boolean isValid(JWT assertion) {
+ if (!(assertion instanceof SignedJWT)) {
+ // unsigned assertion
+ return false;
+ }
+
+ JWTClaimsSet claims;
+ try {
+ claims = assertion.getJWTClaimsSet();
+ } catch (ParseException e) {
+ logger.debug("Invalid assertion claims");
+ return false;
+ }
+
+ // make sure the issuer exists
+ if (Strings.isNullOrEmpty(claims.getIssuer())) {
+ logger.debug("No issuer for assertion, rejecting");
+ return false;
+ }
+
+ // make sure the issuer is us
+ if (!claims.getIssuer().equals(config.getIssuer())) {
+ logger.debug("Issuer is not the same as this server, rejecting");
+ return false;
+ }
+
+ // validate the signature based on our public key
+ if (jwtService.validateSignature((SignedJWT) assertion)) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+}
diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/WhitelistedIssuerAssertionValidator.java b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/WhitelistedIssuerAssertionValidator.java
new file mode 100644
index 0000000000..87f5986cea
--- /dev/null
+++ b/openid-connect-common/src/main/java/org/mitre/jwt/assertion/impl/WhitelistedIssuerAssertionValidator.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+
+package org.mitre.jwt.assertion.impl;
+
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.mitre.jwt.assertion.AssertionValidator;
+import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
+import org.mitre.jwt.signer.service.impl.JWKSetCacheService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import com.google.common.base.Strings;
+import com.nimbusds.jwt.JWT;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+
+/**
+ * Checks to see if the assertion was signed by a particular authority available from a whitelist
+ * @author jricher
+ *
+ */
+public class WhitelistedIssuerAssertionValidator implements AssertionValidator {
+
+ private static Logger logger = LoggerFactory.getLogger(WhitelistedIssuerAssertionValidator.class);
+
+ /**
+ * Map of issuer -> JWKSetUri
+ */
+ private Map whitelist = new HashMap<>();
+
+ /**
+ * @return the whitelist
+ */
+ public Map getWhitelist() {
+ return whitelist;
+ }
+
+ /**
+ * @param whitelist the whitelist to set
+ */
+ public void setWhitelist(Map whitelist) {
+ this.whitelist = whitelist;
+ }
+
+ @Autowired
+ private JWKSetCacheService jwkCache;
+
+ @Override
+ public boolean isValid(JWT assertion) {
+
+ if (!(assertion instanceof SignedJWT)) {
+ // unsigned assertion
+ return false;
+ }
+
+ JWTClaimsSet claims;
+ try {
+ claims = assertion.getJWTClaimsSet();
+ } catch (ParseException e) {
+ logger.debug("Invalid assertion claims");
+ return false;
+ }
+
+ if (Strings.isNullOrEmpty(claims.getIssuer())) {
+ logger.debug("No issuer for assertion, rejecting");
+ return false;
+ }
+
+ if (!whitelist.containsKey(claims.getIssuer())) {
+ logger.debug("Issuer is not in whitelist, rejecting");
+ return false;
+ }
+
+ String jwksUri = whitelist.get(claims.getIssuer());
+
+ JWTSigningAndValidationService validator = jwkCache.getValidator(jwksUri);
+
+ if (validator.validateSignature((SignedJWT) assertion)) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+}
diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/JwtEncryptionAndDecryptionService.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/JWTEncryptionAndDecryptionService.java
similarity index 91%
rename from openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/JwtEncryptionAndDecryptionService.java
rename to openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/JWTEncryptionAndDecryptionService.java
index 447c58bc5a..0489af58d9 100644
--- a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/JwtEncryptionAndDecryptionService.java
+++ b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/JWTEncryptionAndDecryptionService.java
@@ -1,19 +1,20 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.jwt.encryption.service;
import java.util.Collection;
@@ -28,7 +29,7 @@
* @author wkim
*
*/
-public interface JwtEncryptionAndDecryptionService {
+public interface JWTEncryptionAndDecryptionService {
/**
* Encrypts the JWT in place with the default encrypter.
diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJwtEncryptionAndDecryptionService.java b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJWTEncryptionAndDecryptionService.java
similarity index 70%
rename from openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJwtEncryptionAndDecryptionService.java
rename to openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJWTEncryptionAndDecryptionService.java
index bc9e53822d..dbe8a530bb 100644
--- a/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJwtEncryptionAndDecryptionService.java
+++ b/openid-connect-common/src/main/java/org/mitre/jwt/encryption/service/impl/DefaultJWTEncryptionAndDecryptionService.java
@@ -1,19 +1,20 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.jwt.encryption.service.impl;
import java.security.NoSuchAlgorithmException;
@@ -27,7 +28,7 @@
import javax.annotation.PostConstruct;
import org.mitre.jose.keystore.JWKSetKeyStore;
-import org.mitre.jwt.encryption.service.JwtEncryptionAndDecryptionService;
+import org.mitre.jwt.encryption.service.JWTEncryptionAndDecryptionService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -40,8 +41,12 @@
import com.nimbusds.jose.JWEObject;
import com.nimbusds.jose.crypto.DirectDecrypter;
import com.nimbusds.jose.crypto.DirectEncrypter;
+import com.nimbusds.jose.crypto.ECDHDecrypter;
+import com.nimbusds.jose.crypto.ECDHEncrypter;
import com.nimbusds.jose.crypto.RSADecrypter;
import com.nimbusds.jose.crypto.RSAEncrypter;
+import com.nimbusds.jose.crypto.bc.BouncyCastleProviderSingleton;
+import com.nimbusds.jose.jwk.ECKey;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.nimbusds.jose.jwk.RSAKey;
@@ -50,15 +55,18 @@
* @author wkim
*
*/
-public class DefaultJwtEncryptionAndDecryptionService implements JwtEncryptionAndDecryptionService {
+public class DefaultJWTEncryptionAndDecryptionService implements JWTEncryptionAndDecryptionService {
- private static Logger logger = LoggerFactory.getLogger(DefaultJwtEncryptionAndDecryptionService.class);
+ /**
+ * Logger for this class
+ */
+ private static final Logger logger = LoggerFactory.getLogger(DefaultJWTEncryptionAndDecryptionService.class);
// map of identifier to encrypter
- private Map encrypters = new HashMap();
+ private Map encrypters = new HashMap<>();
// map of identifier to decrypter
- private Map decrypters = new HashMap();
+ private Map decrypters = new HashMap<>();
private String defaultEncryptionKeyId;
@@ -67,18 +75,18 @@ public class DefaultJwtEncryptionAndDecryptionService implements JwtEncryptionAn
private JWEAlgorithm defaultAlgorithm;
// map of identifier to key
- private Map keys = new HashMap();
+ private Map keys = new HashMap<>();
/**
* Build this service based on the keys given. All public keys will be used to make encrypters,
* all private keys will be used to make decrypters.
- *
+ *
* @param keys
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws JOSEException
*/
- public DefaultJwtEncryptionAndDecryptionService(Map keys) throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException {
+ public DefaultJWTEncryptionAndDecryptionService(Map keys) throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException {
this.keys = keys;
buildEncryptersAndDecrypters();
}
@@ -86,13 +94,13 @@ public DefaultJwtEncryptionAndDecryptionService(Map keys) throws No
/**
* Build this service based on the given keystore. All keys must have a key
* id ({@code kid}) field in order to be used.
- *
+ *
* @param keyStore
* @throws NoSuchAlgorithmException
* @throws InvalidKeySpecException
* @throws JOSEException
*/
- public DefaultJwtEncryptionAndDecryptionService(JWKSetKeyStore keyStore) throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException {
+ public DefaultJWTEncryptionAndDecryptionService(JWKSetKeyStore keyStore) throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException {
// convert all keys in the keystore to a map based on key id
for (JWK key : keyStore.getKeys()) {
@@ -109,12 +117,20 @@ public DefaultJwtEncryptionAndDecryptionService(JWKSetKeyStore keyStore) throws
@PostConstruct
- public void afterPropertiesSet() throws NoSuchAlgorithmException, InvalidKeySpecException, JOSEException{
+ public void afterPropertiesSet() {
if (keys == null) {
throw new IllegalArgumentException("Encryption and decryption service must have at least one key configured.");
}
- buildEncryptersAndDecrypters();
+ try {
+ buildEncryptersAndDecrypters();
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalArgumentException("Encryption and decryption service could not find given algorithm.");
+ } catch (InvalidKeySpecException e) {
+ throw new IllegalArgumentException("Encryption and decryption service saw an invalid key specification.");
+ } catch (JOSEException e) {
+ throw new IllegalArgumentException("Encryption and decryption service was unable to process JOSE object.");
+ }
}
public String getDefaultEncryptionKeyId() {
@@ -212,23 +228,40 @@ private void buildEncryptersAndDecrypters() throws NoSuchAlgorithmException, Inv
if (jwk instanceof RSAKey) {
// build RSA encrypters and decrypters
- RSAEncrypter encrypter = new RSAEncrypter(((RSAKey) jwk).toRSAPublicKey()); // there should always at least be the public key
+ RSAEncrypter encrypter = new RSAEncrypter((RSAKey) jwk); // there should always at least be the public key
+ encrypter.getJCAContext().setProvider(BouncyCastleProviderSingleton.getInstance());
encrypters.put(id, encrypter);
if (jwk.isPrivate()) { // we can decrypt!
- RSADecrypter decrypter = new RSADecrypter(((RSAKey) jwk).toRSAPrivateKey());
+ RSADecrypter decrypter = new RSADecrypter((RSAKey) jwk);
+ decrypter.getJCAContext().setProvider(BouncyCastleProviderSingleton.getInstance());
decrypters.put(id, decrypter);
} else {
logger.warn("No private key for key #" + jwk.getKeyID());
}
+ } else if (jwk instanceof ECKey) {
+
+ // build EC Encrypters and decrypters
+
+ ECDHEncrypter encrypter = new ECDHEncrypter((ECKey) jwk);
+ encrypter.getJCAContext().setProvider(BouncyCastleProviderSingleton.getInstance());
+ encrypters.put(id, encrypter);
- // TODO: add support for EC keys
+ if (jwk.isPrivate()) { // we can decrypt too
+ ECDHDecrypter decrypter = new ECDHDecrypter((ECKey) jwk);
+ decrypter.getJCAContext().setProvider(BouncyCastleProviderSingleton.getInstance());
+ decrypters.put(id, decrypter);
+ } else {
+ logger.warn("No private key for key # " + jwk.getKeyID());
+ }
} else if (jwk instanceof OctetSequenceKey) {
// build symmetric encrypters and decrypters
- DirectEncrypter encrypter = new DirectEncrypter(((OctetSequenceKey) jwk).toByteArray());
- DirectDecrypter decrypter = new DirectDecrypter(((OctetSequenceKey) jwk).toByteArray());
+ DirectEncrypter encrypter = new DirectEncrypter((OctetSequenceKey) jwk);
+ encrypter.getJCAContext().setProvider(BouncyCastleProviderSingleton.getInstance());
+ DirectDecrypter decrypter = new DirectDecrypter((OctetSequenceKey) jwk);
+ decrypter.getJCAContext().setProvider(BouncyCastleProviderSingleton.getInstance());
encrypters.put(id, encrypter);
decrypters.put(id, decrypter);
@@ -242,7 +275,7 @@ private void buildEncryptersAndDecrypters() throws NoSuchAlgorithmException, Inv
@Override
public Map getAllPublicKeys() {
- Map pubKeys = new HashMap();
+ Map pubKeys = new HashMap<>();
// pull out all public keys
for (String keyId : keys.keySet()) {
@@ -258,14 +291,14 @@ public Map getAllPublicKeys() {
@Override
public Collection getAllEncryptionAlgsSupported() {
- Set algs = new HashSet();
+ Set algs = new HashSet<>();
for (JWEEncrypter encrypter : encrypters.values()) {
- algs.addAll(encrypter.supportedAlgorithms());
+ algs.addAll(encrypter.supportedJWEAlgorithms());
}
for (JWEDecrypter decrypter : decrypters.values()) {
- algs.addAll(decrypter.supportedAlgorithms());
+ algs.addAll(decrypter.supportedJWEAlgorithms());
}
return algs;
@@ -276,7 +309,7 @@ public Collection getAllEncryptionAlgsSupported() {
*/
@Override
public Collection getAllEncryptionEncsSupported() {
- Set encs = new HashSet();
+ Set encs = new HashSet<>();
for (JWEEncrypter encrypter : encrypters.values()) {
encs.addAll(encrypter.supportedEncryptionMethods());
diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JwtSigningAndValidationService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JWTSigningAndValidationService.java
similarity index 91%
rename from openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JwtSigningAndValidationService.java
rename to openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JWTSigningAndValidationService.java
index 61d807b1cd..1bb05f1421 100644
--- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JwtSigningAndValidationService.java
+++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/JWTSigningAndValidationService.java
@@ -1,19 +1,20 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.jwt.signer.service;
import java.security.NoSuchAlgorithmException;
@@ -24,7 +25,7 @@
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jwt.SignedJWT;
-public interface JwtSigningAndValidationService {
+public interface JWTSigningAndValidationService {
/**
* Get all public keys for this service, mapped by their Key ID
@@ -34,7 +35,7 @@ public interface JwtSigningAndValidationService {
/**
* Checks the signature of the given JWT against all configured signers,
* returns true if at least one of the signers validates it.
- *
+ *
* @param jwtString
* the string representation of the JWT as sent on the wire
* @return true if the signature is valid, false if not
@@ -45,7 +46,7 @@ public interface JwtSigningAndValidationService {
/**
* Called to sign a jwt in place for a client that hasn't registered a preferred signing algorithm.
* Use the default algorithm to sign.
- *
+ *
* @param jwt the jwt to sign
* @return the signed jwt
* @throws NoSuchAlgorithmException
@@ -67,13 +68,15 @@ public interface JwtSigningAndValidationService {
/**
* Sign a jwt using the selected algorithm. The algorithm is selected using the String parameter values specified
* in the JWT spec, section 6. I.E., "HS256" means HMAC with SHA-256 and corresponds to our HmacSigner class.
- *
+ *
* @param jwt the jwt to sign
* @param alg the name of the algorithm to use, as specified in JWS s.6
* @return the signed jwt
*/
public void signJwt(SignedJWT jwt, JWSAlgorithm alg);
+ public String getDefaultSignerKeyId();
+
/**
* TODO: method to sign a jwt using a specified algorithm and a key id
*/
diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/ClientKeyCacheService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/ClientKeyCacheService.java
new file mode 100644
index 0000000000..3e46b9b4bd
--- /dev/null
+++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/ClientKeyCacheService.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+
+package org.mitre.jwt.signer.service.impl;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import org.mitre.jose.keystore.JWKSetKeyStore;
+import org.mitre.jwt.encryption.service.JWTEncryptionAndDecryptionService;
+import org.mitre.jwt.encryption.service.impl.DefaultJWTEncryptionAndDecryptionService;
+import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
+import org.mitre.oauth2.model.ClientDetailsEntity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import com.google.common.base.Strings;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.util.concurrent.UncheckedExecutionException;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.jwk.JWKSet;
+
+/**
+ *
+ * Takes in a client and returns the appropriate validator or encrypter for
+ * that client's registered key types.
+ *
+ * @author jricher
+ *
+ */
+@Service
+public class ClientKeyCacheService {
+
+ private static Logger logger = LoggerFactory.getLogger(ClientKeyCacheService.class);
+
+ @Autowired
+ private JWKSetCacheService jwksUriCache = new JWKSetCacheService();
+
+ @Autowired
+ private SymmetricKeyJWTValidatorCacheService symmetricCache = new SymmetricKeyJWTValidatorCacheService();
+
+ // cache of validators for by-value JWKs
+ private LoadingCache jwksValidators;
+
+ // cache of encryptors for by-value JWKs
+ private LoadingCache jwksEncrypters;
+
+ public ClientKeyCacheService() {
+ this.jwksValidators = CacheBuilder.newBuilder()
+ .expireAfterWrite(1, TimeUnit.HOURS) // expires 1 hour after fetch
+ .maximumSize(100)
+ .build(new JWKSetVerifierBuilder());
+ this.jwksEncrypters = CacheBuilder.newBuilder()
+ .expireAfterWrite(1, TimeUnit.HOURS) // expires 1 hour after fetch
+ .maximumSize(100)
+ .build(new JWKSetEncryptorBuilder());
+ }
+
+
+ public JWTSigningAndValidationService getValidator(ClientDetailsEntity client, JWSAlgorithm alg) {
+
+ try {
+ if (alg.equals(JWSAlgorithm.RS256)
+ || alg.equals(JWSAlgorithm.RS384)
+ || alg.equals(JWSAlgorithm.RS512)
+ || alg.equals(JWSAlgorithm.ES256)
+ || alg.equals(JWSAlgorithm.ES384)
+ || alg.equals(JWSAlgorithm.ES512)
+ || alg.equals(JWSAlgorithm.PS256)
+ || alg.equals(JWSAlgorithm.PS384)
+ || alg.equals(JWSAlgorithm.PS512)) {
+
+ // asymmetric key
+ if (client.getJwks() != null) {
+ return jwksValidators.get(client.getJwks());
+ } else if (!Strings.isNullOrEmpty(client.getJwksUri())) {
+ return jwksUriCache.getValidator(client.getJwksUri());
+ } else {
+ return null;
+ }
+
+ } else if (alg.equals(JWSAlgorithm.HS256)
+ || alg.equals(JWSAlgorithm.HS384)
+ || alg.equals(JWSAlgorithm.HS512)) {
+
+ // symmetric key
+
+ return symmetricCache.getSymmetricValidtor(client);
+
+ } else {
+
+ return null;
+ }
+ } catch (UncheckedExecutionException | ExecutionException e) {
+ logger.error("Problem loading client validator", e);
+ return null;
+ }
+
+ }
+
+ public JWTEncryptionAndDecryptionService getEncrypter(ClientDetailsEntity client) {
+
+ try {
+ if (client.getJwks() != null) {
+ return jwksEncrypters.get(client.getJwks());
+ } else if (!Strings.isNullOrEmpty(client.getJwksUri())) {
+ return jwksUriCache.getEncrypter(client.getJwksUri());
+ } else {
+ return null;
+ }
+ } catch (UncheckedExecutionException | ExecutionException e) {
+ logger.error("Problem loading client encrypter", e);
+ return null;
+ }
+
+ }
+
+
+ private class JWKSetEncryptorBuilder extends CacheLoader {
+
+ @Override
+ public JWTEncryptionAndDecryptionService load(JWKSet key) throws Exception {
+ return new DefaultJWTEncryptionAndDecryptionService(new JWKSetKeyStore(key));
+ }
+
+ }
+
+ private class JWKSetVerifierBuilder extends CacheLoader {
+
+ @Override
+ public JWTSigningAndValidationService load(JWKSet key) throws Exception {
+ return new DefaultJWTSigningAndValidationService(new JWKSetKeyStore(key));
+ }
+
+ }
+
+
+}
diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DefaultJwtSigningAndValidationService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DefaultJWTSigningAndValidationService.java
similarity index 72%
rename from openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DefaultJwtSigningAndValidationService.java
rename to openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DefaultJWTSigningAndValidationService.java
index 32a27bfcbc..eea51fcfd2 100644
--- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DefaultJwtSigningAndValidationService.java
+++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/DefaultJWTSigningAndValidationService.java
@@ -1,19 +1,20 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.jwt.signer.service.impl;
import java.security.NoSuchAlgorithmException;
@@ -26,7 +27,7 @@
import java.util.UUID;
import org.mitre.jose.keystore.JWKSetKeyStore;
-import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
+import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,6 +36,8 @@
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.ECDSASigner;
+import com.nimbusds.jose.crypto.ECDSAVerifier;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jose.crypto.MACVerifier;
import com.nimbusds.jose.crypto.RSASSASigner;
@@ -45,36 +48,39 @@
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jwt.SignedJWT;
-public class DefaultJwtSigningAndValidationService implements JwtSigningAndValidationService {
+public class DefaultJWTSigningAndValidationService implements JWTSigningAndValidationService {
// map of identifier to signer
- private Map signers = new HashMap();
+ private Map signers = new HashMap<>();
// map of identifier to verifier
- private Map verifiers = new HashMap();
+ private Map verifiers = new HashMap<>();
- private static Logger logger = LoggerFactory.getLogger(DefaultJwtSigningAndValidationService.class);
+ /**
+ * Logger for this class
+ */
+ private static final Logger logger = LoggerFactory.getLogger(DefaultJWTSigningAndValidationService.class);
private String defaultSignerKeyId;
private JWSAlgorithm defaultAlgorithm;
// map of identifier to key
- private Map keys = new HashMap();
+ private Map keys = new HashMap<>();
/**
* Build this service based on the keys given. All public keys will be used
* to make verifiers, all private keys will be used to make signers.
- *
+ *
* @param keys
* A map of key identifier to key
- *
+ *
* @throws InvalidKeySpecException
* If the keys in the JWKs are not valid
* @throws NoSuchAlgorithmException
* If there is no appropriate algorithm to tie the keys to.
*/
- public DefaultJwtSigningAndValidationService(Map keys) throws NoSuchAlgorithmException, InvalidKeySpecException {
+ public DefaultJWTSigningAndValidationService(Map keys) throws NoSuchAlgorithmException, InvalidKeySpecException {
this.keys = keys;
buildSignersAndVerifiers();
}
@@ -82,22 +88,21 @@ public DefaultJwtSigningAndValidationService(Map keys) throws NoSuc
/**
* Build this service based on the given keystore. All keys must have a key
* id ({@code kid}) field in order to be used.
- *
+ *
* @param keyStore
* the keystore to load all keys from
- *
+ *
* @throws InvalidKeySpecException
* If the keys in the JWKs are not valid
* @throws NoSuchAlgorithmException
* If there is no appropriate algorithm to tie the keys to.
*/
- public DefaultJwtSigningAndValidationService(JWKSetKeyStore keyStore) throws NoSuchAlgorithmException, InvalidKeySpecException {
+ public DefaultJWTSigningAndValidationService(JWKSetKeyStore keyStore) throws NoSuchAlgorithmException, InvalidKeySpecException {
// convert all keys in the keystore to a map based on key id
if (keyStore!= null && keyStore.getJwkSet() != null) {
for (JWK key : keyStore.getKeys()) {
if (!Strings.isNullOrEmpty(key.getKeyID())) {
// use the key ID that's built into the key itself
- // TODO (#641): deal with JWK thumbprints
this.keys.put(key.getKeyID(), key);
} else {
// create a random key id
@@ -113,8 +118,9 @@ public DefaultJwtSigningAndValidationService(JWKSetKeyStore keyStore) throws NoS
/**
* @return the defaultSignerKeyId
*/
+ @Override
public String getDefaultSignerKeyId() {
- return defaultSignerKeyId;
+ return defaultSignerKeyId;
}
/**
@@ -155,39 +161,48 @@ private void buildSignersAndVerifiers() throws NoSuchAlgorithmException, Invalid
String id = jwkEntry.getKey();
JWK jwk = jwkEntry.getValue();
- if (jwk instanceof RSAKey) {
- // build RSA signers & verifiers
+ try {
+ if (jwk instanceof RSAKey) {
+ // build RSA signers & verifiers
- if (jwk.isPrivate()) { // only add the signer if there's a private key
- RSASSASigner signer = new RSASSASigner(((RSAKey) jwk).toRSAPrivateKey());
- signers.put(id, signer);
- }
+ if (jwk.isPrivate()) { // only add the signer if there's a private key
+ RSASSASigner signer = new RSASSASigner((RSAKey) jwk);
+ signers.put(id, signer);
+ }
- RSASSAVerifier verifier = new RSASSAVerifier(((RSAKey) jwk).toRSAPublicKey());
- verifiers.put(id, verifier);
+ RSASSAVerifier verifier = new RSASSAVerifier((RSAKey) jwk);
+ verifiers.put(id, verifier);
- } else if (jwk instanceof ECKey) {
- // build EC signers & verifiers
+ } else if (jwk instanceof ECKey) {
+ // build EC signers & verifiers
- // TODO: add support for EC keys
- logger.warn("EC Keys are not yet supported.");
+ if (jwk.isPrivate()) {
+ ECDSASigner signer = new ECDSASigner((ECKey) jwk);
+ signers.put(id, signer);
+ }
- } else if (jwk instanceof OctetSequenceKey) {
- // build HMAC signers & verifiers
+ ECDSAVerifier verifier = new ECDSAVerifier((ECKey) jwk);
+ verifiers.put(id, verifier);
- if (jwk.isPrivate()) { // technically redundant check because all HMAC keys are private
- MACSigner signer = new MACSigner(((OctetSequenceKey) jwk).toByteArray());
- signers.put(id, signer);
- }
+ } else if (jwk instanceof OctetSequenceKey) {
+ // build HMAC signers & verifiers
- MACVerifier verifier = new MACVerifier(((OctetSequenceKey) jwk).toByteArray());
- verifiers.put(id, verifier);
+ if (jwk.isPrivate()) { // technically redundant check because all HMAC keys are private
+ MACSigner signer = new MACSigner((OctetSequenceKey) jwk);
+ signers.put(id, signer);
+ }
- } else {
- logger.warn("Unknown key type: " + jwk);
+ MACVerifier verifier = new MACVerifier((OctetSequenceKey) jwk);
+ verifiers.put(id, verifier);
+
+ } else {
+ logger.warn("Unknown key type: " + jwk);
+ }
+ } catch (JOSEException e) {
+ logger.warn("Exception loading signer/verifier", e);
}
}
-
+
if (defaultSignerKeyId == null && keys.size() == 1) {
// if there's only one key, it's the default
setDefaultSignerKeyId(keys.keySet().iterator().next());
@@ -220,7 +235,7 @@ public void signJwt(SignedJWT jwt, JWSAlgorithm alg) {
JWSSigner signer = null;
for (JWSSigner s : signers.values()) {
- if (s.supportedAlgorithms().contains(alg)) {
+ if (s.supportedJWSAlgorithms().contains(alg)) {
signer = s;
break;
}
@@ -251,7 +266,7 @@ public boolean validateSignature(SignedJWT jwt) {
}
} catch (JOSEException e) {
- logger.error("Failed to validate signature, error was: ", e);
+ logger.error("Failed to validate signature with " + verifier + " error message: " + e.getMessage());
}
}
return false;
@@ -259,7 +274,7 @@ public boolean validateSignature(SignedJWT jwt) {
@Override
public Map getAllPublicKeys() {
- Map pubKeys = new HashMap();
+ Map pubKeys = new HashMap<>();
// pull all keys out of the verifiers if we know how
for (String keyId : keys.keySet()) {
@@ -279,14 +294,14 @@ public Map getAllPublicKeys() {
@Override
public Collection getAllSigningAlgsSupported() {
- Set algs = new HashSet();
+ Set algs = new HashSet<>();
for (JWSSigner signer : signers.values()) {
- algs.addAll(signer.supportedAlgorithms());
+ algs.addAll(signer.supportedJWSAlgorithms());
}
for (JWSVerifier verifier : verifiers.values()) {
- algs.addAll(verifier.supportedAlgorithms());
+ algs.addAll(verifier.supportedJWSAlgorithms());
}
return algs;
diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JWKSetCacheService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JWKSetCacheService.java
index 81abdd2f06..8c98005115 100644
--- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JWKSetCacheService.java
+++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/JWKSetCacheService.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.jwt.signer.service.impl;
@@ -23,51 +24,56 @@
import java.util.concurrent.TimeUnit;
import org.apache.http.client.HttpClient;
-import org.apache.http.impl.client.SystemDefaultHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
import org.mitre.jose.keystore.JWKSetKeyStore;
-import org.mitre.jwt.encryption.service.JwtEncryptionAndDecryptionService;
-import org.mitre.jwt.encryption.service.impl.DefaultJwtEncryptionAndDecryptionService;
-import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
+import org.mitre.jwt.encryption.service.JWTEncryptionAndDecryptionService;
+import org.mitre.jwt.encryption.service.impl.DefaultJWTEncryptionAndDecryptionService;
+import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.UncheckedExecutionException;
+import com.google.gson.JsonParseException;
import com.nimbusds.jose.jwk.JWKSet;
/**
- *
+ *
* Creates a caching map of JOSE signers/validators and encrypters/decryptors
* keyed on the JWK Set URI. Dynamically loads JWK Sets to create the services.
- *
+ *
* @author jricher
- *
+ *
*/
@Service
public class JWKSetCacheService {
- private static Logger logger = LoggerFactory.getLogger(JWKSetCacheService.class);
+ /**
+ * Logger for this class
+ */
+ private static final Logger logger = LoggerFactory.getLogger(JWKSetCacheService.class);
// map of jwk set uri -> signing/validation service built on the keys found in that jwk set
- private LoadingCache validators;
+ private LoadingCache validators;
// map of jwk set uri -> encryption/decryption service built on the keys found in that jwk set
- private LoadingCache encrypters;
+ private LoadingCache encrypters;
public JWKSetCacheService() {
this.validators = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.HOURS) // expires 1 hour after fetch
.maximumSize(100)
- .build(new JWKSetVerifierFetcher());
+ .build(new JWKSetVerifierFetcher(HttpClientBuilder.create().useSystemProperties().build()));
this.encrypters = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnit.HOURS) // expires 1 hour after fetch
.maximumSize(100)
- .build(new JWKSetEncryptorFetcher());
+ .build(new JWKSetEncryptorFetcher(HttpClientBuilder.create().useSystemProperties().build()));
}
/**
@@ -76,26 +82,20 @@ public JWKSetCacheService() {
* @throws ExecutionException
* @see com.google.common.cache.Cache#get(java.lang.Object)
*/
- public JwtSigningAndValidationService getValidator(String jwksUri) {
+ public JWTSigningAndValidationService getValidator(String jwksUri) {
try {
return validators.get(jwksUri);
- } catch (UncheckedExecutionException ue) {
- logger.warn("Couldn't load JWK Set from " + jwksUri, ue);
- return null;
- } catch (ExecutionException e) {
- logger.warn("Couldn't load JWK Set from " + jwksUri, e);
+ } catch (UncheckedExecutionException | ExecutionException e) {
+ logger.warn("Couldn't load JWK Set from " + jwksUri + ": " + e.getMessage());
return null;
}
}
- public JwtEncryptionAndDecryptionService getEncrypter(String jwksUri) {
+ public JWTEncryptionAndDecryptionService getEncrypter(String jwksUri) {
try {
return encrypters.get(jwksUri);
- } catch (UncheckedExecutionException ue) {
- logger.warn("Couldn't load JWK Set from " + jwksUri, ue);
- return null;
- } catch (ExecutionException e) {
- logger.warn("Couldn't load JWK Set from " + jwksUri, e);
+ } catch (UncheckedExecutionException | ExecutionException e) {
+ logger.warn("Couldn't load JWK Set from " + jwksUri + ": " + e.getMessage());
return null;
}
}
@@ -104,26 +104,28 @@ public JwtEncryptionAndDecryptionService getEncrypter(String jwksUri) {
* @author jricher
*
*/
- private class JWKSetVerifierFetcher extends CacheLoader {
- private HttpClient httpClient = new SystemDefaultHttpClient();
- private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
- private RestTemplate restTemplate = new RestTemplate(httpFactory);
+ private class JWKSetVerifierFetcher extends CacheLoader {
+ private HttpComponentsClientHttpRequestFactory httpFactory;
+ private RestTemplate restTemplate;
+
+ JWKSetVerifierFetcher(HttpClient httpClient) {
+ this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
+ this.restTemplate = new RestTemplate(httpFactory);
+ }
/**
* Load the JWK Set and build the appropriate signing service.
*/
@Override
- public JwtSigningAndValidationService load(String key) throws Exception {
-
+ public JWTSigningAndValidationService load(String key) throws Exception {
String jsonString = restTemplate.getForObject(key, String.class);
JWKSet jwkSet = JWKSet.parse(jsonString);
JWKSetKeyStore keyStore = new JWKSetKeyStore(jwkSet);
- JwtSigningAndValidationService service = new DefaultJwtSigningAndValidationService(keyStore);
+ JWTSigningAndValidationService service = new DefaultJWTSigningAndValidationService(keyStore);
return service;
-
}
}
@@ -132,23 +134,32 @@ public JwtSigningAndValidationService load(String key) throws Exception {
* @author jricher
*
*/
- private class JWKSetEncryptorFetcher extends CacheLoader {
- private HttpClient httpClient = new SystemDefaultHttpClient();
- private HttpComponentsClientHttpRequestFactory httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
- private RestTemplate restTemplate = new RestTemplate(httpFactory);
+ private class JWKSetEncryptorFetcher extends CacheLoader {
+ private HttpComponentsClientHttpRequestFactory httpFactory;
+ private RestTemplate restTemplate;
+
+ public JWKSetEncryptorFetcher(HttpClient httpClient) {
+ this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
+ this.restTemplate = new RestTemplate(httpFactory);
+ }
+
/* (non-Javadoc)
* @see com.google.common.cache.CacheLoader#load(java.lang.Object)
*/
@Override
- public JwtEncryptionAndDecryptionService load(String key) throws Exception {
- String jsonString = restTemplate.getForObject(key, String.class);
- JWKSet jwkSet = JWKSet.parse(jsonString);
+ public JWTEncryptionAndDecryptionService load(String key) throws Exception {
+ try {
+ String jsonString = restTemplate.getForObject(key, String.class);
+ JWKSet jwkSet = JWKSet.parse(jsonString);
- JWKSetKeyStore keyStore = new JWKSetKeyStore(jwkSet);
+ JWKSetKeyStore keyStore = new JWKSetKeyStore(jwkSet);
- JwtEncryptionAndDecryptionService service = new DefaultJwtEncryptionAndDecryptionService(keyStore);
+ JWTEncryptionAndDecryptionService service = new DefaultJWTEncryptionAndDecryptionService(keyStore);
- return service;
+ return service;
+ } catch (JsonParseException | RestClientException e) {
+ throw new IllegalArgumentException("Unable to load JWK Set");
+ }
}
}
diff --git a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricCacheService.java b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricKeyJWTValidatorCacheService.java
similarity index 76%
rename from openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricCacheService.java
rename to openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricKeyJWTValidatorCacheService.java
index 843ecad871..817cffefce 100644
--- a/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricCacheService.java
+++ b/openid-connect-common/src/main/java/org/mitre/jwt/signer/service/impl/SymmetricKeyJWTValidatorCacheService.java
@@ -1,6 +1,5 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
+ * Copyright 2018 The MIT Internet Trust Consortium
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,8 +21,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
-import com.nimbusds.jose.Algorithm;
-import org.mitre.jwt.signer.service.JwtSigningAndValidationService;
+import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -36,25 +34,28 @@
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.UncheckedExecutionException;
import com.nimbusds.jose.jwk.JWK;
-import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.nimbusds.jose.jwk.KeyUse;
+import com.nimbusds.jose.jwk.OctetSequenceKey;
import com.nimbusds.jose.util.Base64URL;
/**
* Creates and caches symmetrical validators for clients based on client secrets.
- *
+ *
* @author jricher
*
*/
@Service
-public class SymmetricCacheService {
+public class SymmetricKeyJWTValidatorCacheService {
- private static Logger logger = LoggerFactory.getLogger(SymmetricCacheService.class);
+ /**
+ * Logger for this class
+ */
+ private static final Logger logger = LoggerFactory.getLogger(SymmetricKeyJWTValidatorCacheService.class);
- private LoadingCache validators;
+ private LoadingCache validators;
- public SymmetricCacheService() {
+ public SymmetricKeyJWTValidatorCacheService() {
validators = CacheBuilder.newBuilder()
.expireAfterAccess(24, TimeUnit.HOURS)
.maximumSize(100)
@@ -64,11 +65,11 @@ public SymmetricCacheService() {
/**
* Create a symmetric signing and validation service for the given client
- *
+ *
* @param client
* @return
*/
- public JwtSigningAndValidationService getSymmetricValidtor(ClientDetailsEntity client) {
+ public JWTSigningAndValidationService getSymmetricValidtor(ClientDetailsEntity client) {
if (client == null) {
logger.error("Couldn't create symmetric validator for null client");
@@ -92,22 +93,22 @@ public JwtSigningAndValidationService getSymmetricValidtor(ClientDetailsEntity c
}
- public class SymmetricValidatorBuilder extends CacheLoader {
+ public class SymmetricValidatorBuilder extends CacheLoader {
@Override
- public JwtSigningAndValidationService load(String key) throws Exception {
+ public JWTSigningAndValidationService load(String key) throws Exception {
try {
String id = "SYMMETRIC-KEY";
-
- JWK jwk = new OctetSequenceKey(Base64URL.encode(key), KeyUse.SIGNATURE, null, null, id, null, null, null);
+ JWK jwk = new OctetSequenceKey.Builder(Base64URL.encode(key))
+ .keyUse(KeyUse.SIGNATURE)
+ .keyID(id)
+ .build();
Map keys = ImmutableMap.of(id, jwk);
- JwtSigningAndValidationService service = new DefaultJwtSigningAndValidationService(keys);
+ JWTSigningAndValidationService service = new DefaultJWTSigningAndValidationService(keys);
return service;
- } catch (NoSuchAlgorithmException e) {
- logger.error("Couldn't create symmetric validator for client", e);
- } catch (InvalidKeySpecException e) {
+ } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
logger.error("Couldn't create symmetric validator for client", e);
}
diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/exception/DeviceCodeCreationException.java b/openid-connect-common/src/main/java/org/mitre/oauth2/exception/DeviceCodeCreationException.java
new file mode 100644
index 0000000000..9924505071
--- /dev/null
+++ b/openid-connect-common/src/main/java/org/mitre/oauth2/exception/DeviceCodeCreationException.java
@@ -0,0 +1,50 @@
+/*******************************************************************************
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *******************************************************************************/
+
+package org.mitre.oauth2.exception;
+
+/**
+ * @author jricher
+ *
+ */
+public class DeviceCodeCreationException extends Exception {
+
+ private static final long serialVersionUID = 8078568710169208466L;
+
+ private String error;
+
+ public DeviceCodeCreationException(String error, String message) {
+ super(message);
+ this.error = error;
+ }
+
+ /**
+ * @return the error
+ */
+ public String getError() {
+ return error;
+ }
+
+ /**
+ * @param error the error to set
+ */
+ public void setError(String error) {
+ this.error = error;
+ }
+
+
+
+}
diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java
index cfe901f3e3..28accd47e7 100644
--- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java
+++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthenticationHolderEntity.java
@@ -1,48 +1,89 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.oauth2.model;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
import javax.persistence.Basic;
+import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
import javax.persistence.Column;
+import javax.persistence.Convert;
+import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
-import javax.persistence.Lob;
+import javax.persistence.JoinColumn;
+import javax.persistence.MapKeyColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
+import javax.persistence.OneToOne;
import javax.persistence.Table;
+import javax.persistence.Transient;
+import org.mitre.oauth2.model.convert.SerializableStringConverter;
+import org.mitre.oauth2.model.convert.SimpleGrantedAuthorityStringConverter;
+import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.OAuth2Request;
@Entity
@Table(name = "authentication_holder")
@NamedQueries ({
- @NamedQuery(name = "AuthenticationHolderEntity.getByAuthentication", query = "select a from AuthenticationHolderEntity a where a.authentication = :authentication"),
- @NamedQuery(name = "AuthenticationHolderEntity.getUnusedAuthenticationHolders", query = "select a from AuthenticationHolderEntity a where a.id not in (select t.authenticationHolder.id from OAuth2AccessTokenEntity t) and a.id not in (select r.authenticationHolder.id from OAuth2RefreshTokenEntity r)")
+ @NamedQuery(name = AuthenticationHolderEntity.QUERY_ALL, query = "select a from AuthenticationHolderEntity a"),
+ @NamedQuery(name = AuthenticationHolderEntity.QUERY_GET_UNUSED, query = "select a from AuthenticationHolderEntity a where " +
+ "a.id not in (select t.authenticationHolder.id from OAuth2AccessTokenEntity t) and " +
+ "a.id not in (select r.authenticationHolder.id from OAuth2RefreshTokenEntity r) and " +
+ "a.id not in (select c.authenticationHolder.id from AuthorizationCodeEntity c)")
})
public class AuthenticationHolderEntity {
+ public static final String QUERY_GET_UNUSED = "AuthenticationHolderEntity.getUnusedAuthenticationHolders";
+ public static final String QUERY_ALL = "AuthenticationHolderEntity.getAll";
+
private Long id;
- private Long ownerId;
+ private SavedUserAuthentication userAuth;
+
+ private Collection authorities;
+
+ private Set resourceIds;
+
+ private boolean approved;
+
+ private String redirectUri;
+
+ private Set responseTypes;
- private OAuth2Authentication authentication;
+ private Map extensions;
+
+ private String clientId;
+
+ private Set scope;
+
+ private Map requestParameters;
public AuthenticationHolderEntity() {
@@ -59,25 +100,226 @@ public void setId(Long id) {
this.id = id;
}
+ @Transient
+ public OAuth2Authentication getAuthentication() {
+ // TODO: memoize this
+ return new OAuth2Authentication(createOAuth2Request(), getUserAuth());
+ }
+
+ /**
+ * @return
+ */
+ private OAuth2Request createOAuth2Request() {
+ return new OAuth2Request(requestParameters, clientId, authorities, approved, scope, resourceIds, redirectUri, responseTypes, extensions);
+ }
+
+ public void setAuthentication(OAuth2Authentication authentication) {
+
+ // pull apart the request and save its bits
+ OAuth2Request o2Request = authentication.getOAuth2Request();
+ setAuthorities(o2Request.getAuthorities() == null ? null : new HashSet<>(o2Request.getAuthorities()));
+ setClientId(o2Request.getClientId());
+ setExtensions(o2Request.getExtensions() == null ? null : new HashMap<>(o2Request.getExtensions()));
+ setRedirectUri(o2Request.getRedirectUri());
+ setRequestParameters(o2Request.getRequestParameters() == null ? null : new HashMap<>(o2Request.getRequestParameters()));
+ setResourceIds(o2Request.getResourceIds() == null ? null : new HashSet<>(o2Request.getResourceIds()));
+ setResponseTypes(o2Request.getResponseTypes() == null ? null : new HashSet<>(o2Request.getResponseTypes()));
+ setScope(o2Request.getScope() == null ? null : new HashSet<>(o2Request.getScope()));
+ setApproved(o2Request.isApproved());
+
+ if (authentication.getUserAuthentication() != null) {
+ this.userAuth = new SavedUserAuthentication(authentication.getUserAuthentication());
+ } else {
+ this.userAuth = null;
+ }
+ }
+
+ /**
+ * @return the userAuth
+ */
+ @OneToOne(cascade=CascadeType.ALL)
+ @JoinColumn(name = "user_auth_id")
+ public SavedUserAuthentication getUserAuth() {
+ return userAuth;
+ }
+
+ /**
+ * @param userAuth the userAuth to set
+ */
+ public void setUserAuth(SavedUserAuthentication userAuth) {
+ this.userAuth = userAuth;
+ }
+
+ /**
+ * @return the authorities
+ */
+ @ElementCollection(fetch = FetchType.EAGER)
+ @CollectionTable(
+ name="authentication_holder_authority",
+ joinColumns=@JoinColumn(name="owner_id")
+ )
+ @Convert(converter = SimpleGrantedAuthorityStringConverter.class)
+ @Column(name="authority")
+ public Collection getAuthorities() {
+ return authorities;
+ }
+
+ /**
+ * @param authorities the authorities to set
+ */
+ public void setAuthorities(Collection authorities) {
+ this.authorities = authorities;
+ }
+
+ /**
+ * @return the resourceIds
+ */
+ @ElementCollection(fetch = FetchType.EAGER)
+ @CollectionTable(
+ name="authentication_holder_resource_id",
+ joinColumns=@JoinColumn(name="owner_id")
+ )
+ @Column(name="resource_id")
+ public Set getResourceIds() {
+ return resourceIds;
+ }
+
+ /**
+ * @param resourceIds the resourceIds to set
+ */
+ public void setResourceIds(Set resourceIds) {
+ this.resourceIds = resourceIds;
+ }
+
+ /**
+ * @return the approved
+ */
@Basic
- @Column(name = "owner_id")
- public Long getOwnerId() {
- return ownerId;
+ @Column(name="approved")
+ public boolean isApproved() {
+ return approved;
}
- public void setOwnerId(Long owner_id) {
- this.ownerId = owner_id;
+ /**
+ * @param approved the approved to set
+ */
+ public void setApproved(boolean approved) {
+ this.approved = approved;
}
- @Lob
- @Basic(fetch=FetchType.LAZY)
- @Column(name = "authentication")
- public OAuth2Authentication getAuthentication() {
- return authentication;
+ /**
+ * @return the redirectUri
+ */
+ @Basic
+ @Column(name="redirect_uri")
+ public String getRedirectUri() {
+ return redirectUri;
}
- public void setAuthentication(OAuth2Authentication authentication) {
- this.authentication = authentication;
+ /**
+ * @param redirectUri the redirectUri to set
+ */
+ public void setRedirectUri(String redirectUri) {
+ this.redirectUri = redirectUri;
+ }
+
+ /**
+ * @return the responseTypes
+ */
+ @ElementCollection(fetch = FetchType.EAGER)
+ @CollectionTable(
+ name="authentication_holder_response_type",
+ joinColumns=@JoinColumn(name="owner_id")
+ )
+ @Column(name="response_type")
+ public Set getResponseTypes() {
+ return responseTypes;
+ }
+
+ /**
+ * @param responseTypes the responseTypes to set
+ */
+ public void setResponseTypes(Set responseTypes) {
+ this.responseTypes = responseTypes;
+ }
+
+ /**
+ * @return the extensions
+ */
+ @ElementCollection(fetch = FetchType.EAGER)
+ @CollectionTable(
+ name="authentication_holder_extension",
+ joinColumns=@JoinColumn(name="owner_id")
+ )
+ @Column(name="val")
+ @MapKeyColumn(name="extension")
+ @Convert(converter=SerializableStringConverter.class)
+ public Map getExtensions() {
+ return extensions;
+ }
+
+ /**
+ * @param extensions the extensions to set
+ */
+ public void setExtensions(Map extensions) {
+ this.extensions = extensions;
+ }
+
+ /**
+ * @return the clientId
+ */
+ @Basic
+ @Column(name="client_id")
+ public String getClientId() {
+ return clientId;
+ }
+
+ /**
+ * @param clientId the clientId to set
+ */
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ /**
+ * @return the scope
+ */
+ @ElementCollection(fetch = FetchType.EAGER)
+ @CollectionTable(
+ name="authentication_holder_scope",
+ joinColumns=@JoinColumn(name="owner_id")
+ )
+ @Column(name="scope")
+ public Set getScope() {
+ return scope;
+ }
+
+ /**
+ * @param scope the scope to set
+ */
+ public void setScope(Set scope) {
+ this.scope = scope;
+ }
+
+ /**
+ * @return the requestParameters
+ */
+ @ElementCollection(fetch = FetchType.EAGER)
+ @CollectionTable(
+ name="authentication_holder_request_parameter",
+ joinColumns=@JoinColumn(name="owner_id")
+ )
+ @Column(name="val")
+ @MapKeyColumn(name="param")
+ public Map getRequestParameters() {
+ return requestParameters;
+ }
+
+ /**
+ * @param requestParameters the requestParameters to set
+ */
+ public void setRequestParameters(Map requestParameters) {
+ this.requestParameters = requestParameters;
}
diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthorizationCodeEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthorizationCodeEntity.java
index 7411ba5e53..385f467685 100644
--- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthorizationCodeEntity.java
+++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/AuthorizationCodeEntity.java
@@ -1,53 +1,63 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
package org.mitre.oauth2.model;
+import java.util.Date;
+
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
-import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
-import javax.persistence.Lob;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
-
-import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import javax.persistence.Temporal;
/**
* Entity class for authorization codes
- *
+ *
* @author aanganes
*
*/
@Entity
@Table(name = "authorization_code")
@NamedQueries({
- @NamedQuery(name = "AuthorizationCodeEntity.getByValue", query = "select a from AuthorizationCodeEntity a where a.code = :code")
+ @NamedQuery(name = AuthorizationCodeEntity.QUERY_BY_VALUE, query = "select a from AuthorizationCodeEntity a where a.code = :code"),
+ @NamedQuery(name = AuthorizationCodeEntity.QUERY_EXPIRATION_BY_DATE, query = "select a from AuthorizationCodeEntity a where a.expiration <= :" + AuthorizationCodeEntity.PARAM_DATE)
})
public class AuthorizationCodeEntity {
+ public static final String QUERY_BY_VALUE = "AuthorizationCodeEntity.getByValue";
+ public static final String QUERY_EXPIRATION_BY_DATE = "AuthorizationCodeEntity.expirationByDate";
+
+ public static final String PARAM_DATE = "date";
+
private Long id;
private String code;
- private OAuth2Authentication authentication;
+ private AuthenticationHolderEntity authenticationHolder;
+
+ private Date expiration;
/**
* Default constructor.
@@ -58,13 +68,14 @@ public AuthorizationCodeEntity() {
/**
* Create a new AuthorizationCodeEntity with the given code and AuthorizationRequestHolder.
- *
+ *
* @param code the authorization code
* @param authRequest the AuthoriztionRequestHolder associated with the original code request
*/
- public AuthorizationCodeEntity(String code, OAuth2Authentication authRequest) {
+ public AuthorizationCodeEntity(String code, AuthenticationHolderEntity authenticationHolder, Date expiration) {
this.code = code;
- this.authentication = authRequest;
+ this.authenticationHolder = authenticationHolder;
+ this.expiration = expiration;
}
/**
@@ -101,20 +112,30 @@ public void setCode(String code) {
}
/**
+ * The authentication in place when this token was created.
* @return the authentication
*/
- @Lob
- @Basic(fetch=FetchType.EAGER)
- @Column(name="authentication")
- public OAuth2Authentication getAuthentication() {
- return authentication;
+ @ManyToOne
+ @JoinColumn(name = "auth_holder_id")
+ public AuthenticationHolderEntity getAuthenticationHolder() {
+ return authenticationHolder;
}
/**
* @param authentication the authentication to set
*/
- public void setAuthentication(OAuth2Authentication authentication) {
- this.authentication = authentication;
+ public void setAuthenticationHolder(AuthenticationHolderEntity authenticationHolder) {
+ this.authenticationHolder = authenticationHolder;
}
+ @Basic
+ @Temporal(javax.persistence.TemporalType.TIMESTAMP)
+ @Column(name = "expiration")
+ public Date getExpiration() {
+ return expiration;
+ }
+
+ public void setExpiration(Date expiration) {
+ this.expiration = expiration;
+ }
}
diff --git a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java
index 252a41cada..c161c07970 100644
--- a/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java
+++ b/openid-connect-common/src/main/java/org/mitre/oauth2/model/ClientDetailsEntity.java
@@ -1,21 +1,22 @@
/*******************************************************************************
- * Copyright 2014 The MITRE Corporation
- * and the MIT Kerberos and Internet Trust Consortium
- *
+ * Copyright 2018 The MIT Internet Trust Consortium
+ *
+ * Portions copyright 2011-2013 The MITRE Corporation
+ *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
- ******************************************************************************/
+ *******************************************************************************/
/**
- *
+ *
*/
package org.mitre.oauth2.model;
@@ -25,13 +26,11 @@
import java.util.Map;
import java.util.Set;
-import javax.persistence.AttributeOverride;
-import javax.persistence.AttributeOverrides;
import javax.persistence.Basic;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
+import javax.persistence.Convert;
import javax.persistence.ElementCollection;
-import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
@@ -49,31 +48,39 @@
import javax.persistence.TemporalType;
import javax.persistence.Transient;
-import org.mitre.jose.JWEAlgorithmEmbed;
-import org.mitre.jose.JWEEncryptionMethodEmbed;
-import org.mitre.jose.JWSAlgorithmEmbed;
+import org.mitre.oauth2.model.convert.JWEAlgorithmStringConverter;
+import org.mitre.oauth2.model.convert.JWEEncryptionMethodStringConverter;
+import org.mitre.oauth2.model.convert.JWKSetStringConverter;
+import org.mitre.oauth2.model.convert.JWSAlgorithmStringConverter;
+import org.mitre.oauth2.model.convert.JWTStringConverter;
+import org.mitre.oauth2.model.convert.PKCEAlgorithmStringConverter;
+import org.mitre.oauth2.model.convert.SimpleGrantedAuthorityStringConverter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.provider.ClientDetails;
import com.nimbusds.jose.EncryptionMethod;
import com.nimbusds.jose.JWEAlgorithm;
import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.jwt.JWT;
/**
* @author jricher
- *
+ *
*/
@Entity
@Table(name = "client_details")
@NamedQueries({
- @NamedQuery(name = "ClientDetailsEntity.findAll", query = "SELECT c FROM ClientDetailsEntity c"),
- @NamedQuery(name = "ClientDetailsEntity.getByClientId", query = "select c from ClientDetailsEntity c where c.clientId = :clientId")
+ @NamedQuery(name = ClientDetailsEntity.QUERY_ALL, query = "SELECT c FROM ClientDetailsEntity c"),
+ @NamedQuery(name = ClientDetailsEntity.QUERY_BY_CLIENT_ID, query = "select c from ClientDetailsEntity c where c.clientId = :" + ClientDetailsEntity.PARAM_CLIENT_ID)
})
public class ClientDetailsEntity implements ClientDetails {
- /**
- *
- */
+ public static final String QUERY_BY_CLIENT_ID = "ClientDetailsEntity.getByClientId";
+ public static final String QUERY_ALL = "ClientDetailsEntity.findAll";
+
+ public static final String PARAM_CLIENT_ID = "clientId";
+
private static final int DEFAULT_ID_TOKEN_VALIDITY_SECONDS = 600;
private static final long serialVersionUID = -1617727085733786296L;
@@ -83,51 +90,54 @@ public class ClientDetailsEntity implements ClientDetails {
/** Fields from the OAuth2 Dynamic Registration Specification */
private String clientId = null; // client_id
private String clientSecret = null; // client_secret
- private Set redirectUris = new HashSet(); // redirect_uris
+ private Set redirectUris = new HashSet<>(); // redirect_uris
private String clientName; // client_name
private String clientUri; // client_uri
private String logoUri; // logo_uri
private Set contacts; // contacts
private String tosUri; // tos_uri
private AuthMethod tokenEndpointAuthMethod = AuthMethod.SECRET_BASIC; // token_endpoint_auth_method
- private Set scope = new HashSet(); // scope
- private Set grantTypes = new HashSet(); // grant_types
- private Set responseTypes = new HashSet(); // response_types
+ private Set scope = new HashSet<>(); // scope
+ private Set grantTypes = new HashSet<>(); // grant_types
+ private Set responseTypes = new HashSet<>(); // response_types
private String policyUri;
- private String jwksUri;
+ private String jwksUri; // URI pointer to keys
+ private JWKSet jwks; // public key stored by value
+ private String softwareId;
+ private String softwareVersion;
/** Fields from OIDC Client Registration Specification **/
private AppType applicationType; // application_type
private String sectorIdentifierUri; // sector_identifier_uri
private SubjectType subjectType; // subject_type
- private JWSAlgorithmEmbed requestObjectSigningAlg = null; // request_object_signing_alg
+ private JWSAlgorithm requestObjectSigningAlg = null; // request_object_signing_alg
- private JWSAlgorithmEmbed userInfoSignedResponseAlg = null; // user_info_signed_response_alg
- private JWEAlgorithmEmbed userInfoEncryptedResponseAlg = null; // user_info_encrypted_response_alg
- private JWEEncryptionMethodEmbed userInfoEncryptedResponseEnc = null; // user_info_encrypted_response_enc
+ private JWSAlgorithm userInfoSignedResponseAlg = null; // user_info_signed_response_alg
+ private JWEAlgorithm userInfoEncryptedResponseAlg = null; // user_info_encrypted_response_alg
+ private EncryptionMethod userInfoEncryptedResponseEnc = null; // user_info_encrypted_response_enc
- private JWSAlgorithmEmbed idTokenSignedResponseAlg = null; // id_token_signed_response_alg
- private JWEAlgorithmEmbed idTokenEncryptedResponseAlg = null; // id_token_encrypted_response_alg
- private JWEEncryptionMethodEmbed idTokenEncryptedResponseEnc = null; // id_token_encrypted_response_enc
+ private JWSAlgorithm idTokenSignedResponseAlg = null; // id_token_signed_response_alg
+ private JWEAlgorithm idTokenEncryptedResponseAlg = null; // id_token_encrypted_response_alg
+ private EncryptionMethod idTokenEncryptedResponseEnc = null; // id_token_encrypted_response_enc
- private JWSAlgorithmEmbed tokenEndpointAuthSigningAlg = null; // token_endpoint_auth_signing_alg
+ private JWSAlgorithm tokenEndpointAuthSigningAlg = null; // token_endpoint_auth_signing_alg
private Integer defaultMaxAge; // default_max_age
private Boolean requireAuthTime; // require_auth_time
private Set defaultACRvalues; // default_acr_values
private String initiateLoginUri; // initiate_login_uri
- private String postLogoutRedirectUri; // post_logout_redirect_uri
+ private Set postLogoutRedirectUris; // post_logout_redirect_uris
private Set requestUris; // request_uris
/** Fields to support the ClientDetails interface **/
- private Set authorities = new HashSet();
+ private Set authorities = new HashSet<>();
private Integer accessTokenValiditySeconds = 0; // in seconds
private Integer refreshTokenValiditySeconds = 0; // in seconds
- private Set resourceIds = new HashSet();
- private Map additionalInformation = new HashMap();
+ private Set resourceIds = new HashSet<>();
+ private Map additionalInformation = new HashMap<>();
/** Our own fields **/
private String clientDescription = ""; // human-readable description
@@ -136,6 +146,17 @@ public class ClientDetailsEntity implements ClientDetails {
private boolean allowIntrospection = false; // do we let this client call the introspection endpoint?
private Integer idTokenValiditySeconds; //timeout for id tokens
private Date createdAt; // time the client was created
+ private boolean clearAccessTokensOnRefresh = true; // do we clear access tokens on refresh?
+ private Integer deviceCodeValiditySeconds; // timeout for device codes
+
+ /** fields for UMA */
+ private Set claimsRedirectUris;
+
+ /** Software statement **/
+ private JWT softwareStatement;
+
+ /** PKCE **/
+ private PKCEAlgorithm codeChallengeMethod;
public enum AuthMethod {
SECRET_POST("client_secret_post"),
@@ -147,7 +168,7 @@ public enum AuthMethod {
private final String value;
// map to aid reverse lookup
- private static final Map lookup = new HashMap();
+ private static final Map lookup = new HashMap<>();
static {
for (AuthMethod a : AuthMethod.values()) {
lookup.put(a.getValue(), a);
@@ -173,7 +194,7 @@ public enum AppType {
private final String value;
// map to aid reverse lookup
- private static final Map lookup = new HashMap();
+ private static final Map lookup = new HashMap<>();
static {
for (AppType a : AppType.values()) {
lookup.put(a.getValue(), a);
@@ -199,7 +220,7 @@ public enum SubjectType {
private final String value;
// map to aid reverse lookup
- private static final Map lookup = new HashMap