Skip to content

Commit d1e8529

Browse files
committed
expose ID Token and UserInfo to the AuthoritiesProvider and AuthoritiesMapper, both extensible
closes mitreid-connect#699 closes mitreid-connect#761
1 parent f7b5228 commit d1e8529

File tree

7 files changed

+181
-51
lines changed

7 files changed

+181
-51
lines changed

openid-connect-client/src/main/java/org/mitre/openid/connect/client/NamedAdminAuthoritiesMapper.java

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,19 @@
1919
*/
2020
package org.mitre.openid.connect.client;
2121

22+
import java.text.ParseException;
2223
import java.util.Collection;
2324
import java.util.HashSet;
2425
import java.util.Set;
2526

27+
import org.mitre.openid.connect.model.UserInfo;
28+
import org.slf4j.Logger;
29+
import org.slf4j.LoggerFactory;
2630
import org.springframework.security.core.GrantedAuthority;
2731
import org.springframework.security.core.authority.SimpleGrantedAuthority;
28-
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
29-
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
32+
33+
import com.nimbusds.jwt.JWT;
34+
import com.nimbusds.jwt.ReadOnlyJWTClaimsSet;
3035

3136
/**
3237
*
@@ -37,31 +42,36 @@
3742
* @author jricher
3843
*
3944
*/
40-
public class NamedAdminAuthoritiesMapper implements GrantedAuthoritiesMapper {
45+
public class NamedAdminAuthoritiesMapper implements OIDCAuthoritiesMapper {
4146

47+
private static Logger logger = LoggerFactory.getLogger(NamedAdminAuthoritiesMapper.class);
48+
4249
private static final SimpleGrantedAuthority ROLE_ADMIN = new SimpleGrantedAuthority("ROLE_ADMIN");
4350
private static final SimpleGrantedAuthority ROLE_USER = new SimpleGrantedAuthority("ROLE_USER");
4451

4552
private Set<SubjectIssuerGrantedAuthority> admins = new HashSet<SubjectIssuerGrantedAuthority>();
4653

47-
private GrantedAuthoritiesMapper chain = new NullAuthoritiesMapper();
48-
4954
@Override
50-
public Collection<? extends GrantedAuthority> mapAuthorities(Collection<? extends GrantedAuthority> authorities) {
55+
public Collection<? extends GrantedAuthority> mapAuthorities(JWT idToken, UserInfo userInfo) {
5156

5257
Set<GrantedAuthority> out = new HashSet<GrantedAuthority>();
53-
out.addAll(authorities);
58+
try {
59+
ReadOnlyJWTClaimsSet claims = idToken.getJWTClaimsSet();
60+
61+
SubjectIssuerGrantedAuthority authority = new SubjectIssuerGrantedAuthority(claims.getSubject(), claims.getIssuer());
62+
out.add(authority);
5463

55-
for (GrantedAuthority authority : authorities) {
5664
if (admins.contains(authority)) {
5765
out.add(ROLE_ADMIN);
5866
}
67+
68+
// everybody's a user by default
69+
out.add(ROLE_USER);
70+
71+
} catch (ParseException e) {
72+
logger.error("Unable to parse ID Token inside of authorities mapper (huh?)");
5973
}
60-
61-
// everybody's a user by default
62-
out.add(ROLE_USER);
63-
64-
return chain.mapAuthorities(out);
74+
return out;
6575
}
6676

6777
/**
@@ -78,18 +88,4 @@ public void setAdmins(Set<SubjectIssuerGrantedAuthority> admins) {
7888
this.admins = admins;
7989
}
8090

81-
/**
82-
* @return the chain
83-
*/
84-
public GrantedAuthoritiesMapper getChain() {
85-
return chain;
86-
}
87-
88-
/**
89-
* @param chain the chain to set
90-
*/
91-
public void setChain(GrantedAuthoritiesMapper chain) {
92-
this.chain = chain;
93-
}
94-
9591
}

openid-connect-client/src/main/java/org/mitre/openid/connect/client/OIDCAuthenticationProvider.java

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,34 @@
1616
*******************************************************************************/
1717
package org.mitre.openid.connect.client;
1818

19+
import java.text.ParseException;
1920
import java.util.Collection;
2021

2122
import org.mitre.openid.connect.model.OIDCAuthenticationToken;
2223
import org.mitre.openid.connect.model.UserInfo;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
2326
import org.springframework.security.authentication.AuthenticationProvider;
2427
import org.springframework.security.core.Authentication;
2528
import org.springframework.security.core.AuthenticationException;
26-
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
29+
import org.springframework.security.core.GrantedAuthority;
2730
import org.springframework.security.core.userdetails.UsernameNotFoundException;
2831

2932
import com.google.common.base.Strings;
30-
import com.google.common.collect.Lists;
33+
import com.nimbusds.jwt.JWT;
34+
import com.nimbusds.jwt.JWTParser;
3135

3236
/**
3337
* @author nemonik
3438
*
3539
*/
3640
public class OIDCAuthenticationProvider implements AuthenticationProvider {
3741

42+
private static Logger logger = LoggerFactory.getLogger(OIDCAuthenticationProvider.class);
43+
3844
private UserInfoFetcher userInfoFetcher = new UserInfoFetcher();
3945

40-
private GrantedAuthoritiesMapper authoritiesMapper = new NamedAdminAuthoritiesMapper();
46+
private OIDCAuthoritiesMapper authoritiesMapper = new NamedAdminAuthoritiesMapper();
4147

4248
/*
4349
* (non-Javadoc)
@@ -46,8 +52,7 @@ public class OIDCAuthenticationProvider implements AuthenticationProvider {
4652
* authenticate(org.springframework.security.core.Authentication)
4753
*/
4854
@Override
49-
public Authentication authenticate(final Authentication authentication)
50-
throws AuthenticationException {
55+
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
5156

5257
if (!supports(authentication.getClass())) {
5358
return null;
@@ -56,33 +61,56 @@ public Authentication authenticate(final Authentication authentication)
5661
if (authentication instanceof OIDCAuthenticationToken) {
5762

5863
OIDCAuthenticationToken token = (OIDCAuthenticationToken) authentication;
59-
60-
Collection<SubjectIssuerGrantedAuthority> authorities = Lists.newArrayList(new SubjectIssuerGrantedAuthority(token.getSub(), token.getIssuer()));
61-
62-
UserInfo userInfo = userInfoFetcher.loadUserInfo(token);
63-
64-
if (userInfo == null) {
65-
// TODO: user Info not found -- error?
66-
} else {
67-
if (!Strings.isNullOrEmpty(userInfo.getSub()) && !userInfo.getSub().equals(token.getSub())) {
68-
// the userinfo came back and the user_id fields don't match what was in the id_token
69-
throw new UsernameNotFoundException("user_id mismatch between id_token and user_info call: " + token.getSub() + " / " + userInfo.getSub());
64+
65+
try {
66+
67+
// get the ID Token value out
68+
String idTokenString = token.getIdTokenValue();
69+
JWT idToken = JWTParser.parse(idTokenString);
70+
71+
// load the user info if we can
72+
UserInfo userInfo = userInfoFetcher.loadUserInfo(token);
73+
74+
if (userInfo == null) {
75+
// user info not found -- could be an error, could be fine
76+
} else {
77+
// if we found userinfo, double check it
78+
if (!Strings.isNullOrEmpty(userInfo.getSub()) && !userInfo.getSub().equals(token.getSub())) {
79+
// the userinfo came back and the user_id fields don't match what was in the id_token
80+
throw new UsernameNotFoundException("user_id mismatch between id_token and user_info call: " + token.getSub() + " / " + userInfo.getSub());
81+
}
7082
}
83+
84+
return createAuthenticationToken(token, authoritiesMapper.mapAuthorities(idToken, userInfo), userInfo);
85+
} catch (ParseException e) {
86+
logger.error("Unable to parse ID token in the token");
87+
return null;
7188
}
72-
73-
return new OIDCAuthenticationToken(token.getSub(),
74-
token.getIssuer(),
75-
userInfo, authoritiesMapper.mapAuthorities(authorities),
76-
token.getIdTokenValue(), token.getAccessTokenValue(), token.getRefreshTokenValue());
7789
}
7890

7991
return null;
8092
}
8193

94+
/**
95+
* Override this function to return a different kind of Authentication, processes the authorities differently,
96+
* or do post-processing based on the UserInfo object.
97+
*
98+
* @param token
99+
* @param authorities
100+
* @param userInfo
101+
* @return
102+
*/
103+
protected Authentication createAuthenticationToken(OIDCAuthenticationToken token, Collection<? extends GrantedAuthority> authorities, UserInfo userInfo) {
104+
return new OIDCAuthenticationToken(token.getSub(),
105+
token.getIssuer(),
106+
userInfo, authorities,
107+
token.getIdTokenValue(), token.getAccessTokenValue(), token.getRefreshTokenValue());
108+
}
109+
82110
/**
83111
* @param authoritiesMapper
84112
*/
85-
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
113+
public void setAuthoritiesMapper(OIDCAuthoritiesMapper authoritiesMapper) {
86114
this.authoritiesMapper = authoritiesMapper;
87115
}
88116

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*******************************************************************************
2+
* Copyright 2015 The MITRE Corporation
3+
* and the MIT Kerberos and Internet Trust Consortium
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*******************************************************************************/
17+
18+
package org.mitre.openid.connect.client;
19+
20+
import java.util.Collection;
21+
22+
import org.mitre.openid.connect.model.UserInfo;
23+
import org.springframework.security.core.GrantedAuthority;
24+
25+
import com.nimbusds.jwt.JWT;
26+
27+
/**
28+
* @author jricher
29+
*
30+
*/
31+
public interface OIDCAuthoritiesMapper {
32+
33+
/**
34+
* @param idToken the ID Token (parsed as a JWT, cannot be @null)
35+
* @param userInfo userInfo of the current user (could be @null)
36+
* @return the set of authorities to map to this user
37+
*/
38+
Collection<? extends GrantedAuthority> mapAuthorities(JWT idToken, UserInfo userInfo);
39+
40+
}

openid-connect-common/src/main/java/org/mitre/openid/connect/model/DefaultUserInfo.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import javax.persistence.Basic;
2020
import javax.persistence.Column;
21+
import javax.persistence.Convert;
2122
import javax.persistence.Entity;
2223
import javax.persistence.GeneratedValue;
2324
import javax.persistence.GenerationType;
@@ -28,6 +29,8 @@
2829
import javax.persistence.OneToOne;
2930
import javax.persistence.Table;
3031

32+
import org.mitre.openid.connect.model.convert.JsonObjectStringConverter;
33+
3134
import com.google.gson.JsonObject;
3235

3336
@Entity
@@ -510,6 +513,9 @@ public static UserInfo fromJson(JsonObject obj) {
510513
* @return the jsonString
511514
*/
512515
@Override
516+
@Basic
517+
@Column(name = "src")
518+
@Convert(converter = JsonObjectStringConverter.class)
513519
public JsonObject getSource() {
514520
return src;
515521
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*******************************************************************************
2+
* Copyright 2015 The MITRE Corporation
3+
* and the MIT Kerberos and Internet Trust Consortium
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*******************************************************************************/
17+
18+
package org.mitre.openid.connect.model.convert;
19+
20+
import javax.persistence.AttributeConverter;
21+
import javax.persistence.Converter;
22+
23+
import com.google.common.base.Strings;
24+
import com.google.gson.JsonObject;
25+
import com.google.gson.JsonParser;
26+
27+
/**
28+
* @author jricher
29+
*
30+
*/
31+
@Converter
32+
public class JsonObjectStringConverter implements AttributeConverter<JsonObject, String> {
33+
34+
private JsonParser parser = new JsonParser();
35+
36+
@Override
37+
public String convertToDatabaseColumn(JsonObject attribute) {
38+
if (attribute != null) {
39+
return attribute.toString();
40+
} else {
41+
return null;
42+
}
43+
}
44+
45+
/* (non-Javadoc)
46+
* @see javax.persistence.AttributeConverter#convertToEntityAttribute(java.lang.Object)
47+
*/
48+
@Override
49+
public JsonObject convertToEntityAttribute(String dbData) {
50+
if (!Strings.isNullOrEmpty(dbData)) {
51+
return parser.parse(dbData).getAsJsonObject();
52+
} else {
53+
return null;
54+
}
55+
}
56+
57+
}

openid-connect-server-webapp/src/main/resources/db/tables/hsql_database_tables.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,8 @@ CREATE TABLE IF NOT EXISTS user_info (
253253
phone_number_verified BOOLEAN,
254254
address_id VARCHAR(256),
255255
updated_time VARCHAR(256),
256-
birthdate VARCHAR(256)
256+
birthdate VARCHAR(256),
257+
src VARCHAR(4096)
257258
);
258259

259260
CREATE TABLE IF NOT EXISTS whitelisted_site (

openid-connect-server/src/main/java/org/mitre/openid/connect/filter/AuthorizationRequestFilter.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
110110
ClientDetailsEntity client = null;
111111

112112
authRequest = authRequestFactory.createAuthorizationRequest(createRequestMap(request.getParameterMap()));
113-
client = clientService.loadClientByClientId(authRequest.getClientId());
113+
if (!Strings.isNullOrEmpty(authRequest.getClientId())) {
114+
client = clientService.loadClientByClientId(authRequest.getClientId());
115+
}
114116

115117
// save the login hint to the session
116118
if (authRequest.getExtensions().get(LOGIN_HINT) != null) {

0 commit comments

Comments
 (0)