Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Block authentication flow originating from a web view (mobile app) #3204

Merged
merged 1 commit into from
Dec 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,9 @@ public class AppConfiguration implements Configuration {
@DocProperty(description = "SSA Configuration")
private SsaConfiguration ssaConfiguration;

@DocProperty(description = "Enable/Disable block authorizations that originate from Webview (Mobile apps).", defaultValue = "false")
private Boolean blockWebviewAuthorizationEnabled = false;

public List<SsaValidationConfig> getDcrSsaValidationConfigs() {
if (dcrSsaValidationConfigs == null) dcrSsaValidationConfigs = new ArrayList<>();
return dcrSsaValidationConfigs;
Expand Down Expand Up @@ -3085,4 +3088,12 @@ public SsaConfiguration getSsaConfiguration() {
public void setSsaConfiguration(SsaConfiguration ssaConfiguration) {
this.ssaConfiguration = ssaConfiguration;
}

public Boolean getBlockWebviewAuthorizationEnabled() {
return blockWebviewAuthorizationEnabled;
}

public void setBlockWebviewAuthorizationEnabled(Boolean blockWebviewAuthorizationEnabled) {
this.blockWebviewAuthorizationEnabled = blockWebviewAuthorizationEnabled;
}
}
3 changes: 2 additions & 1 deletion jans-auth-server/server/conf/jans-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -449,5 +449,6 @@
"defaultResponseHeaders": {
"Cache-Control": "max-age=0, no-store"
}
}
},
"blockWebviewAuthorizationEnabled": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ public Response requestAuthorizationGet(
String codeChallenge, String codeChallengeMethod, String customResponseHeaders, String claims, String authReqId,
HttpServletRequest httpRequest, HttpServletResponse httpResponse, SecurityContext securityContext) {

authorizeRestWebServiceValidator.validateNotWebView(httpRequest);

AuthzRequest authzRequest = new AuthzRequest();
authzRequest.setHttpMethod(HttpMethod.GET);
authzRequest.setScope(scope);
Expand Down Expand Up @@ -210,6 +212,8 @@ public Response requestAuthorizationPost(
String codeChallenge, String codeChallengeMethod, String customResponseHeaders, String claims, String authReqId,
HttpServletRequest httpRequest, HttpServletResponse httpResponse, SecurityContext securityContext) {

authorizeRestWebServiceValidator.validateNotWebView(httpRequest);

AuthzRequest authzRequest = new AuthzRequest();
authzRequest.setHttpMethod(HttpMethod.POST);
authzRequest.setScope(scope);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,4 +458,14 @@ public void checkSignedRequestRequired(AuthzRequest authzRequest) {
throw createInvalidJwtRequestException(authzRequest.getRedirectUriResponse(), "A signed request object is required");
}
}

public void validateNotWebView(HttpServletRequest httpRequest) {
if (appConfiguration.getBlockWebviewAuthorizationEnabled()) {
String headerRequestedWith = httpRequest.getHeader("X-Requested-With");
if (headerRequestedWith != null) {
log.error("Unauthorized, request contains X-Requested-With: {}", headerRequestedWith);
throw new WebApplicationException(Response.status(Response.Status.UNAUTHORIZED).build());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@
import io.jans.as.server.service.DeviceAuthorizationService;
import io.jans.as.server.service.RedirectionUriService;
import io.jans.as.server.service.SessionIdService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.ws.rs.WebApplicationException;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.testng.MockitoTestNGListener;
import org.slf4j.Logger;
import org.testng.annotations.Listeners;
import org.testng.annotations.Test;

import static org.mockito.Mockito.when;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.mockito.Mockito.*;
import static org.testng.Assert.*;

/**
* @author Yuriy Z
Expand Down Expand Up @@ -74,4 +75,33 @@ public void isAuthnMaxAgeValid_whenMaxAgeIsZeroAndDisableAuthnForMaxAgeZeroIsTru
public void isAuthnMaxAgeValid_whenMaxAgeIsNull_shouldReturnTrue() {
assertTrue(authorizeRestWebServiceValidator.isAuthnMaxAgeValid(0, new SessionId(), new Client()));
}

@Test
public void validateNotWebView_blockWebviewDisabled_valid() {
HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
when(appConfiguration.getBlockWebviewAuthorizationEnabled()).thenReturn(false);

authorizeRestWebServiceValidator.validateNotWebView(httpServletRequest);
verifyNoInteractions(log, httpServletRequest);
}

@Test
public void validateNotWebView_blockWebviewEnabled_valid() {
HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
when(appConfiguration.getBlockWebviewAuthorizationEnabled()).thenReturn(true);

authorizeRestWebServiceValidator.validateNotWebView(httpServletRequest);
verifyNoInteractions(log);
}

@Test
public void validateNotWebView_withRequestedWithHeader_throwUnauthorized() {
HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
when(appConfiguration.getBlockWebviewAuthorizationEnabled()).thenReturn(true);
String testPackage = "test.app.package";
when(httpServletRequest.getHeader(any())).thenReturn(testPackage);

assertThrows(WebApplicationException.class, () -> authorizeRestWebServiceValidator.validateNotWebView(httpServletRequest));
verify(log).error(anyString(), eq(testPackage));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -394,5 +394,6 @@
"staticKid": "%(static_kid)s",
"forceOfflineAccessScopeToEnableRefreshToken" : false,
"redirectUrisRegexEnabled": false,
"useHighestLevelScriptIfAcrScriptNotFound": true
"useHighestLevelScriptIfAcrScriptNotFound": true,
"blockWebviewAuthorizationEnabled": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -497,5 +497,6 @@
"defaultResponseHeaders": {
"Cache-Control": "max-age=0, no-store"
}
}
},
"blockWebviewAuthorizationEnabled": false
}