From b4520c170ed0b080e75c926010f9a93e01cbc185 Mon Sep 17 00:00:00 2001 From: Justin Richer Date: Wed, 24 Jun 2015 15:36:55 -0400 Subject: [PATCH] ID Token carried through as parsed JWT instead of string, closes #832 --- .../client/OIDCAuthenticationFilter.java | 2 +- .../client/OIDCAuthenticationProvider.java | 39 ++++++++---------- .../openid/connect/model/DefaultUserInfo.java | 28 ++++++++++++- .../model/OIDCAuthenticationToken.java | 40 +++++++++++++++---- 4 files changed, 76 insertions(+), 33 deletions(-) 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 dd4b25d11..a45fc0e09 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 @@ -584,7 +584,7 @@ public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFi // 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); + OIDCAuthenticationToken token = new OIDCAuthenticationToken(userId, idClaims.getIssuer(), serverConfig, idToken, accessTokenValue, refreshTokenValue); Authentication authentication = this.getAuthenticationManager().authenticate(token); 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 04987c4f0..f61403898 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 @@ -62,30 +62,23 @@ public class OIDCAuthenticationProvider implements AuthenticationProvider { OIDCAuthenticationToken token = (OIDCAuthenticationToken) authentication; - try { - - // get the ID Token value out - String idTokenString = token.getIdTokenValue(); - JWT idToken = JWTParser.parse(idTokenString); - - // load the user info if we can - UserInfo userInfo = userInfoFetcher.loadUserInfo(token); - - if (userInfo == null) { - // 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()); - } + // 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) { + // 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 createAuthenticationToken(token, authoritiesMapper.mapAuthorities(idToken, userInfo), userInfo); - } catch (ParseException e) { - logger.error("Unable to parse ID token in the token"); - return null; } + + return createAuthenticationToken(token, authoritiesMapper.mapAuthorities(idToken, userInfo), userInfo); } return null; @@ -104,7 +97,7 @@ public class OIDCAuthenticationProvider implements AuthenticationProvider { return new OIDCAuthenticationToken(token.getSub(), token.getIssuer(), userInfo, authorities, - token.getIdTokenValue(), token.getAccessTokenValue(), token.getRefreshTokenValue()); + token.getIdToken(), token.getAccessTokenValue(), token.getRefreshTokenValue()); } /** diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java index 9743677c1..2bbb959cc 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java @@ -16,6 +16,10 @@ *******************************************************************************/ package org.mitre.openid.connect.model; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + import javax.persistence.Basic; import javax.persistence.Column; import javax.persistence.Convert; @@ -32,6 +36,7 @@ import javax.persistence.Table; import org.mitre.openid.connect.model.convert.JsonObjectStringConverter; import com.google.gson.JsonObject; +import com.google.gson.JsonParser; @Entity @Table(name="user_info") @@ -70,7 +75,7 @@ public class DefaultUserInfo implements UserInfo { private Address address; private String updatedTime; private String birthdate; - private JsonObject src; // source JSON if this is loaded remotely + private transient JsonObject src; // source JSON if this is loaded remotely /** @@ -727,5 +732,26 @@ public class DefaultUserInfo implements UserInfo { } return true; } + + + /* + * Custom serialization to handle the JSON object + */ + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + if (src == null) { + out.writeObject(null); + } else { + out.writeObject(src.toString()); + } + } + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + Object o = in.readObject(); + if (o != null) { + JsonParser parser = new JsonParser(); + src = parser.parse((String)o).getAsJsonObject(); + } + } } diff --git a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/OIDCAuthenticationToken.java b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/OIDCAuthenticationToken.java index 20cb389f6..d6bd76028 100644 --- a/openid-connect-common/src/main/java/org/mitre/openid/connect/model/OIDCAuthenticationToken.java +++ b/openid-connect-common/src/main/java/org/mitre/openid/connect/model/OIDCAuthenticationToken.java @@ -16,6 +16,10 @@ *******************************************************************************/ package org.mitre.openid.connect.model; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; @@ -24,6 +28,8 @@ import org.springframework.security.authentication.AbstractAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import com.google.common.collect.ImmutableMap; +import com.nimbusds.jwt.JWT; +import com.nimbusds.jwt.JWTParser; /** * @@ -35,14 +41,14 @@ public class OIDCAuthenticationToken extends AbstractAuthenticationToken { private static final long serialVersionUID = 22100073066377804L; private final ImmutableMap principal; - private final String idTokenValue; // string representation of the id token private final String accessTokenValue; // string representation of the access token private final String refreshTokenValue; // string representation of the refresh token + private transient JWT idToken; // this needs a custom serializer private final String issuer; // issuer URL (parsed from the id token) private final String sub; // user id (parsed from the id token) private final transient ServerConfiguration serverConfiguration; // server configuration used to fulfill this token, don't serialize it - private final UserInfo userInfo; // user info container, don't serialize it b/c it might be huge and can be re-fetched + private final UserInfo userInfo; // user info container /** * Constructs OIDCAuthenticationToken with a full set of authorities, marking this as authenticated. @@ -57,7 +63,7 @@ public class OIDCAuthenticationToken extends AbstractAuthenticationToken { */ public OIDCAuthenticationToken(String subject, String issuer, UserInfo userInfo, Collection authorities, - String idTokenValue, String accessTokenValue, String refreshTokenValue) { + JWT idToken, String accessTokenValue, String refreshTokenValue) { super(authorities); @@ -65,7 +71,7 @@ public class OIDCAuthenticationToken extends AbstractAuthenticationToken { this.userInfo = userInfo; this.sub = subject; this.issuer = issuer; - this.idTokenValue = idTokenValue; + this.idToken = idToken; this.accessTokenValue = accessTokenValue; this.refreshTokenValue = refreshTokenValue; @@ -85,14 +91,14 @@ public class OIDCAuthenticationToken extends AbstractAuthenticationToken { */ public OIDCAuthenticationToken(String subject, String issuer, ServerConfiguration serverConfiguration, - String idTokenValue, String accessTokenValue, String refreshTokenValue) { + JWT idToken, String accessTokenValue, String refreshTokenValue) { super(new ArrayList(0)); this.principal = ImmutableMap.of("sub", subject, "iss", issuer); this.sub = subject; this.issuer = issuer; - this.idTokenValue = idTokenValue; + this.idToken = idToken; this.accessTokenValue = accessTokenValue; this.refreshTokenValue = refreshTokenValue; @@ -129,8 +135,8 @@ public class OIDCAuthenticationToken extends AbstractAuthenticationToken { /** * @return the idTokenValue */ - public String getIdTokenValue() { - return idTokenValue; + public JWT getIdToken() { + return idToken; } /** @@ -168,5 +174,23 @@ public class OIDCAuthenticationToken extends AbstractAuthenticationToken { return userInfo; } + /* + * Custom serialization to handle the JSON object + */ + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + if (idToken == null) { + out.writeObject(null); + } else { + out.writeObject(idToken.serialize()); + } + } + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException, ParseException { + in.defaultReadObject(); + Object o = in.readObject(); + if (o != null) { + idToken = JWTParser.parse((String)o); + } + } }