Skip to content

Commit b864697

Browse files
authored
Merge pull request #3963 from xflord/introMfa
feat: use introspection data saved in request headers for MFA
2 parents ab659df + b9c8b65 commit b864697

File tree

5 files changed

+25
-76
lines changed

5 files changed

+25
-76
lines changed

perun-base/src/main/java/cz/metacentrum/perun/core/api/CoreConfig.java

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,7 @@ public void initBeansUtils() {
9494
private List<String> userInfoEndpointExtSourceLogin;
9595
private String userInfoEndpointExtSourceName;
9696
private List<String> userInfoEndpointExtSourceFriendlyName;
97-
private String userInfoEndpointAcrPropertyName;
98-
private String userInfoEndpointMfaAcrValue;
99-
private String userInfoEndpointMfaAuthTimestampPropertyName;
97+
private String introspectionEndpointMfaAcrValue;
10098
private int mfaAuthTimeout;
10199
private boolean enforceMfa;
102100
private int idpLoginValidity;
@@ -792,28 +790,12 @@ public void setUserInfoEndpointExtSourceFriendlyName(List<String> userInfoEndpoi
792790
this.userInfoEndpointExtSourceFriendlyName = userInfoEndpointExtSourceFriendlyName;
793791
}
794792

795-
public String getUserInfoEndpointAcrPropertyName() {
796-
return userInfoEndpointAcrPropertyName;
793+
public String getIntrospectionEndpointMfaAcrValue() {
794+
return introspectionEndpointMfaAcrValue;
797795
}
798796

799-
public void setUserInfoEndpointAcrPropertyName(String userInfoEndpointAcrPropertyName) {
800-
this.userInfoEndpointAcrPropertyName = userInfoEndpointAcrPropertyName;
801-
}
802-
803-
public String getUserInfoEndpointMfaAuthTimestampPropertyName() {
804-
return userInfoEndpointMfaAuthTimestampPropertyName;
805-
}
806-
807-
public void setUserInfoEndpointMfaAuthTimestampPropertyName(String userInfoEndpointMfaAuthTimestampPropertyName) {
808-
this.userInfoEndpointMfaAuthTimestampPropertyName = userInfoEndpointMfaAuthTimestampPropertyName;
809-
}
810-
811-
public String getUserInfoEndpointMfaAcrValue() {
812-
return userInfoEndpointMfaAcrValue;
813-
}
814-
815-
public void setUserInfoEndpointMfaAcrValue(String userInfoEndpointAcrValue) {
816-
this.userInfoEndpointMfaAcrValue = userInfoEndpointAcrValue;
797+
public void setIntrospectionEndpointMfaAcrValue(String introspectionEndpointMfaAcrValue) {
798+
this.introspectionEndpointMfaAcrValue = introspectionEndpointMfaAcrValue;
817799
}
818800

819801
public int getMfaAuthTimeout() {

perun-base/src/main/java/cz/metacentrum/perun/oidc/UserInfoEndpointCall.java

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
import org.apache.commons.lang3.StringUtils;
2121

22-
import static cz.metacentrum.perun.core.api.PerunPrincipal.MFA_TIMESTAMP;
2322

2423
/**
2524
* Class for executing call to User info endpoint.
@@ -35,29 +34,11 @@ public UserInfoEndpointResponse getUserInfoEndpointData(String accessToken, Stri
3534

3635
fillAdditionalInformationWithDataFromUserInfo(userInfo, additionalInformation);
3736

38-
String mfaTimestamp = getMfaTimestamp(userInfo);
39-
if (mfaTimestamp != null && !mfaTimestamp.isEmpty()) {
40-
additionalInformation.put(MFA_TIMESTAMP, mfaTimestamp);
41-
}
42-
4337
String extSourceName = getExtSourceName(userInfo);
4438
String extSourceLogin = getExtSourceLogin(userInfo);
4539
return new UserInfoEndpointResponse(extSourceName, extSourceLogin);
4640
}
4741

48-
/**
49-
* Calls UserInfo endpoint and returns MFA timestamp if available and acr is equal to MFA acr
50-
* @param accessToken access token
51-
* @param issuer issuer
52-
* @throws ExpiredTokenException if access token is expired
53-
* @return mfa timestamp or null
54-
*/
55-
public String getUserInfoEndpointMfaData(String accessToken, String issuer) throws ExpiredTokenException {
56-
JsonNode userInfo = callUserInfo(accessToken, issuer);
57-
58-
return getMfaTimestamp(userInfo);
59-
}
60-
6142
private static JsonNode callUserInfo(String accessToken, String issuer) throws ExpiredTokenException {
6243
RestTemplate restTemplate = new RestTemplate();
6344
JsonNode config = restTemplate.getForObject(issuer + "/.well-known/openid-configuration", JsonNode.class);
@@ -168,22 +149,4 @@ private void fillAdditionalInformationWithDataFromUserInfo(JsonNode userInfo, Ma
168149
additionalInformation.put("sourceIdPName", idpName);
169150
}
170151
}
171-
172-
/**
173-
* Returns mfa timestamp if acr value is equal to MFA acr value
174-
* @param userInfo parsed response from userInfo endpoint
175-
*/
176-
private String getMfaTimestamp(JsonNode userInfo) {
177-
String acrProperty = BeansUtils.getCoreConfig().getUserInfoEndpointAcrPropertyName();
178-
String acr = userInfo.path(acrProperty).asText();
179-
if (StringUtils.isNotEmpty(acr) && acr.equals(BeansUtils.getCoreConfig().getUserInfoEndpointMfaAcrValue())) {
180-
String mfaTimestampProperty = BeansUtils.getCoreConfig().getUserInfoEndpointMfaAuthTimestampPropertyName();
181-
String mfaTimestamp = userInfo.path(mfaTimestampProperty).asText();
182-
if (StringUtils.isNotEmpty(mfaTimestamp)) {
183-
return mfaTimestamp;
184-
}
185-
}
186-
187-
return null;
188-
}
189152
}

perun-base/src/main/resources/perun-base.xml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,9 @@
7777
<property name="userInfoEndpointExtSourceLogin" value="#{'${perun.userInfoEndpoint.extSourceLogin}'.split('\s*,\s*')}"/>
7878
<property name="userInfoEndpointExtSourceName" value="${perun.userInfoEndpoint.extSourceName}"/>
7979
<property name="userInfoEndpointExtSourceFriendlyName" value="#{'${perun.userInfoEndpoint.extSourceFriendlyName}'.split('\s*,\s*')}"/>
80-
<property name="mfaAuthTimeout" value="${perun.userInfoEndpoint.mfaAuthTimeout}"/>
80+
<property name="mfaAuthTimeout" value="${perun.introspectionEndpoint.mfaAuthTimeout}"/>
8181
<property name="enforceMfa" value="${perun.enforceMfa}"/>
82-
<property name="userInfoEndpointMfaAuthTimestampPropertyName" value="${perun.userInfoEndpoint.mfaAuthTimestampPropertyName}"/>
83-
<property name="userInfoEndpointMfaAcrValue" value="${perun.userInfoEndpoint.mfaAcrValue}"/>
84-
<property name="userInfoEndpointAcrPropertyName" value="${perun.userInfoEndpoint.acrPropertyName}"/>
82+
<property name="introspectionEndpointMfaAcrValue" value="${perun.introspectionEndpoint.mfaAcrValue}"/>
8583
<property name="idpLoginValidity" value="${perun.idpLoginValidity}"/>
8684
<property name="idpLoginValidityExceptions" value="#{'${perun.idpLoginValidityExceptions}'.split('\s*,\s*')}"/>
8785
</bean>
@@ -182,10 +180,8 @@
182180
<prop key="perun.userInfoEndpoint.extSourceLogin">eduperson_unique_id, eduperson_principal_name, saml2_nameid_persistent, eduperson_targeted_id, voperson_external_id</prop>
183181
<prop key="perun.userInfoEndpoint.extSourceName">target_issuer</prop>
184182
<prop key="perun.userInfoEndpoint.extSourceFriendlyName">target_backend, display_name, text</prop>
185-
<prop key="perun.userInfoEndpoint.mfaAuthTimestampPropertyName">authn_instant</prop>
186-
<prop key="perun.userInfoEndpoint.mfaAuthTimeout">24</prop>
187-
<prop key="perun.userInfoEndpoint.acrPropertyName">acr</prop>
188-
<prop key="perun.userInfoEndpoint.mfaAcrValue">https://refeds.org/profile/mfa</prop>
183+
<prop key="perun.introspectionEndpoint.mfaAuthTimeout">24</prop>
184+
<prop key="perun.introspectionEndpoint.mfaAcrValue">https://refeds.org/profile/mfa</prop>
189185
<prop key="perun.enforceMfa">false</prop>
190186
</props>
191187
</property>

perun-core/src/main/java/cz/metacentrum/perun/core/blImpl/AuthzResolverBlImpl.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@
7777
import cz.metacentrum.perun.core.impl.AuthzRoles;
7878
import cz.metacentrum.perun.core.impl.Utils;
7979
import cz.metacentrum.perun.core.implApi.AuthzResolverImplApi;
80-
import cz.metacentrum.perun.oidc.UserInfoEndpointCall;
8180
import cz.metacentrum.perun.registrar.model.Application;
8281
import org.slf4j.Logger;
8382
import org.slf4j.LoggerFactory;
@@ -2639,13 +2638,8 @@ public static void refreshMfa(PerunSession sess) throws ExpiredTokenException, M
26392638
throw new MFAuthenticationException("Cannot verify MFA - issuer is missing.");
26402639
}
26412640

2642-
UserInfoEndpointCall userInfoEndpointCall = new UserInfoEndpointCall();
2643-
String timestamp = userInfoEndpointCall.getUserInfoEndpointMfaData(accessToken, issuer);
2644-
if (timestamp != null && !timestamp.isEmpty()) {
2645-
sess.getPerunPrincipal().getAdditionalInformations().put(MFA_TIMESTAMP, timestamp);
2646-
if (isAuthorizedByMfa(sess)) {
2647-
sess.getPerunPrincipal().getRoles().putAuthzRole(Role.MFA);
2648-
}
2641+
if (isAuthorizedByMfa(sess)) {
2642+
sess.getPerunPrincipal().getRoles().putAuthzRole(Role.MFA);
26492643
}
26502644
}
26512645

perun-rpc/src/main/java/cz/metacentrum/perun/rpc/Api.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import java.security.cert.CertificateParsingException;
5151
import java.security.cert.X509Certificate;
5252
import java.sql.Timestamp;
53+
import java.time.Instant;
5354
import java.util.ArrayList;
5455
import java.util.Arrays;
5556
import java.util.Collection;
@@ -67,6 +68,7 @@
6768

6869
import static cz.metacentrum.perun.core.api.PerunPrincipal.ACCESS_TOKEN;
6970
import static cz.metacentrum.perun.core.api.PerunPrincipal.ISSUER;
71+
import static cz.metacentrum.perun.core.api.PerunPrincipal.MFA_TIMESTAMP;
7072
import static org.apache.commons.lang3.StringUtils.isEmpty;
7173
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
7274

@@ -103,6 +105,8 @@ public class Api extends HttpServlet {
103105
private static final String OIDC_CLAIM_CLIENT_ID = "OIDC_CLAIM_client_id";
104106
private static final String OIDC_CLAIM_SCOPE = "OIDC_CLAIM_scope";
105107
private static final String OIDC_CLAIM_ISS = "OIDC_CLAIM_iss";
108+
private static final String OIDC_CLAIM_AUTH_TIME = "OIDC_CLAIM_auth_time";
109+
private static final String OIDC_CLAIM_ACR = "OIDC_CLAIM_acr";
106110
private static final String OIDC_ACCESS_TOKEN = "OIDC_access_token";
107111
private static final String EXTSOURCE = "EXTSOURCE";
108112
private static final String EXTSOURCETYPE = "EXTSOURCETYPE";
@@ -267,6 +271,16 @@ else if (isNotEmpty(req.getHeader(OIDC_CLAIM_SUB))) {
267271
}
268272
extSourceLoaString = "-1";
269273

274+
// get MFA timestamp
275+
String acr = req.getHeader(OIDC_CLAIM_ACR);
276+
if (isNotEmpty(acr) && acr.equals(BeansUtils.getCoreConfig().getIntrospectionEndpointMfaAcrValue())) {
277+
String mfaTimestamp = req.getHeader(OIDC_CLAIM_AUTH_TIME);
278+
if (isNotEmpty(mfaTimestamp)) {
279+
Instant mfaReadableTimestamp = Instant.ofEpochSecond(Long.parseLong(mfaTimestamp));
280+
additionalInformations.put(MFA_TIMESTAMP, mfaReadableTimestamp.toString());
281+
}
282+
}
283+
270284
if (BeansUtils.getCoreConfig().getRequestUserInfoEndpoint()) {
271285
UserInfoEndpointResponse userInfoEndpointResponse;
272286
try {

0 commit comments

Comments
 (0)