For installed app urn redirect to a page instead of returning the html, this is to prevent NPE if page is refreshed
This commit is contained in:
parent
fd9317a295
commit
eb47d43497
10 changed files with 86 additions and 41 deletions
|
@ -12,7 +12,7 @@
|
|||
<p>Please copy this code and paste it into your application:</p>
|
||||
<textarea id="code" class="${properties.kcTextareaClass!}">${code.code}</textarea>
|
||||
<#else>
|
||||
<p>${code.error}</p>
|
||||
<p id="error">${code.error}</p>
|
||||
</#if>
|
||||
</div>
|
||||
</#if>
|
||||
|
|
|
@ -560,7 +560,7 @@ public class AccountService {
|
|||
ApplicationModel application = realm.getApplicationByName(referrer);
|
||||
if (application != null) {
|
||||
if (referrerUri != null) {
|
||||
referrerUri = TokenService.verifyRedirectUri(uriInfo, referrerUri, application);
|
||||
referrerUri = TokenService.verifyRedirectUri(uriInfo, referrerUri, realm, application);
|
||||
} else {
|
||||
referrerUri = ResolveRelative.resolveRelativeUri(uriInfo.getRequestUri(), application.getBaseUrl());
|
||||
}
|
||||
|
@ -571,7 +571,7 @@ public class AccountService {
|
|||
} else if (referrerUri != null) {
|
||||
ClientModel client = realm.getOAuthClient(referrer);
|
||||
if (client != null) {
|
||||
referrerUri = TokenService.verifyRedirectUri(uriInfo, referrerUri, application);
|
||||
referrerUri = TokenService.verifyRedirectUri(uriInfo, referrerUri, realm, application);
|
||||
|
||||
if (referrerUri != null) {
|
||||
return new String[]{referrer, referrerUri};
|
||||
|
|
|
@ -122,6 +122,10 @@ public class SocialResource {
|
|||
Map<String, String[]> queryParams = getQueryParams();
|
||||
|
||||
RequestDetails requestData = getRequestDetails(queryParams);
|
||||
if (requestData == null) {
|
||||
Flows.forms(providerSession, null, uriInfo).setError("Unexpected callback").createErrorPage();
|
||||
}
|
||||
|
||||
SocialProvider provider = SocialLoader.load(requestData.getProviderId());
|
||||
|
||||
String realmName = requestData.getClientAttribute("realm");
|
||||
|
@ -296,7 +300,7 @@ public class SocialResource {
|
|||
logger.warn("Login requester not enabled.");
|
||||
return Flows.forms(providerSession, realm, uriInfo).setError("Login requester not enabled.").createErrorPage();
|
||||
}
|
||||
redirectUri = TokenService.verifyRedirectUri(uriInfo, redirectUri, client);
|
||||
redirectUri = TokenService.verifyRedirectUri(uriInfo, redirectUri, realm, client);
|
||||
if (redirectUri == null) {
|
||||
audit.error(Errors.INVALID_REDIRECT_URI);
|
||||
return Flows.forms(providerSession, realm, uriInfo).setError("Invalid redirect_uri.").createErrorPage();
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.keycloak.authentication.AuthenticationProviderException;
|
|||
import org.keycloak.authentication.AuthenticationProviderManager;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.crypto.RSAProvider;
|
||||
import org.keycloak.login.LoginFormsProvider;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
|
@ -42,6 +43,7 @@ import org.keycloak.services.managers.TokenManager;
|
|||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.flows.Flows;
|
||||
import org.keycloak.services.resources.flows.OAuthFlows;
|
||||
import org.keycloak.services.resources.flows.Urls;
|
||||
import org.keycloak.services.validation.Validation;
|
||||
import org.keycloak.util.BasicAuthHelper;
|
||||
import org.keycloak.util.Time;
|
||||
|
@ -363,7 +365,7 @@ public class TokenService {
|
|||
return oauth.forwardToSecurityFailure("Login requester not enabled.");
|
||||
}
|
||||
|
||||
redirect = verifyRedirectUri(uriInfo, redirect, client);
|
||||
redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
|
||||
if (redirect == null) {
|
||||
audit.error(Errors.INVALID_REDIRECT_URI);
|
||||
return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
|
||||
|
@ -457,7 +459,7 @@ public class TokenService {
|
|||
return oauth.forwardToSecurityFailure("Login requester not enabled.");
|
||||
}
|
||||
|
||||
redirect = verifyRedirectUri(uriInfo, redirect, client);
|
||||
redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
|
||||
if (redirect == null) {
|
||||
audit.error(Errors.INVALID_REDIRECT_URI);
|
||||
return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
|
||||
|
@ -754,7 +756,7 @@ public class TokenService {
|
|||
audit.error(Errors.NOT_ALLOWED);
|
||||
return oauth.forwardToSecurityFailure("Bearer-only applications are not allowed to initiate login");
|
||||
}
|
||||
redirect = verifyRedirectUri(uriInfo, redirect, client);
|
||||
redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
|
||||
if (redirect == null) {
|
||||
audit.error(Errors.INVALID_REDIRECT_URI);
|
||||
return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
|
||||
|
@ -811,7 +813,7 @@ public class TokenService {
|
|||
return oauth.forwardToSecurityFailure("Login requester not enabled.");
|
||||
}
|
||||
|
||||
redirect = verifyRedirectUri(uriInfo, redirect, client);
|
||||
redirect = verifyRedirectUri(uriInfo, redirect, realm, client);
|
||||
if (redirect == null) {
|
||||
audit.error(Errors.INVALID_REDIRECT_URI);
|
||||
return oauth.forwardToSecurityFailure("Invalid redirect_uri.");
|
||||
|
@ -937,6 +939,17 @@ public class TokenService {
|
|||
return oauth.redirectAccessCode(accessCodeEntry, session, state, redirect);
|
||||
}
|
||||
|
||||
@Path("oauth/oob")
|
||||
@GET
|
||||
public Response installedAppUrnCallback(final @QueryParam("code") String code, final @QueryParam("error") String error, final @QueryParam("error_description") String errorDescription) {
|
||||
LoginFormsProvider forms = Flows.forms(providerSession, realm, uriInfo);
|
||||
if (code != null) {
|
||||
return forms.setAccessCode(null, code).createCode();
|
||||
} else {
|
||||
return forms.setError(error).createCode();
|
||||
}
|
||||
}
|
||||
|
||||
protected Response redirectAccessDenied(String redirect, String state) {
|
||||
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, "access_denied");
|
||||
if (state != null)
|
||||
|
@ -961,7 +974,7 @@ public class TokenService {
|
|||
return false;
|
||||
}
|
||||
|
||||
public static String verifyRedirectUri(UriInfo uriInfo, String redirectUri, ClientModel client) {
|
||||
public static String verifyRedirectUri(UriInfo uriInfo, String redirectUri, RealmModel realm, ClientModel client) {
|
||||
Set<String> validRedirects = client.getRedirectUris();
|
||||
if (redirectUri == null) {
|
||||
if (validRedirects.size() != 1) return null;
|
||||
|
@ -970,10 +983,10 @@ public class TokenService {
|
|||
if (idx > -1) {
|
||||
validRedirect = validRedirect.substring(0, idx);
|
||||
}
|
||||
return validRedirect;
|
||||
redirectUri = validRedirect;
|
||||
} else if (validRedirects.isEmpty()) {
|
||||
logger.error("Redirect URI is required for client: " + client.getClientId());
|
||||
return null;
|
||||
redirectUri = null;
|
||||
} else {
|
||||
String r = redirectUri.indexOf('?') != -1 ? redirectUri.substring(0, redirectUri.indexOf('?')) : redirectUri;
|
||||
Set<String> resolveValidRedirects = resolveValidRedirects(uriInfo, validRedirects);
|
||||
|
@ -996,7 +1009,13 @@ public class TokenService {
|
|||
|
||||
valid = matchesRedirects(resolveValidRedirects, r);
|
||||
}
|
||||
return valid ? redirectUri : null;
|
||||
redirectUri = valid ? redirectUri : null;
|
||||
}
|
||||
|
||||
if (Constants.INSTALLED_APP_URN.equals(redirectUri)) {
|
||||
return Urls.realmInstalledAppUrnCallback(uriInfo.getBaseUri(), realm.getName()).toString();
|
||||
} else {
|
||||
return redirectUri;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,34 +86,25 @@ public class OAuthFlows {
|
|||
|
||||
public Response redirectAccessCode(AccessCodeEntry accessCode, UserSessionModel session, String state, String redirect, boolean rememberMe) {
|
||||
String code = accessCode.getCode();
|
||||
|
||||
if (Constants.INSTALLED_APP_URN.equals(redirect)) {
|
||||
return Flows.forms(providerSession, realm, uriInfo).setAccessCode(accessCode.getId(), code).createCode();
|
||||
} else {
|
||||
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.CODE, code);
|
||||
log.debugv("redirectAccessCode: state: {0}", state);
|
||||
if (state != null)
|
||||
redirectUri.queryParam(OAuth2Constants.STATE, state);
|
||||
Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
|
||||
Cookie remember = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
|
||||
rememberMe = rememberMe || remember != null;
|
||||
// refresh the cookies!
|
||||
authManager.createLoginCookie(realm, accessCode.getUser(), session, uriInfo, rememberMe);
|
||||
if (rememberMe) authManager.createRememberMeCookie(realm, uriInfo);
|
||||
return location.build();
|
||||
}
|
||||
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.CODE, code);
|
||||
log.debugv("redirectAccessCode: state: {0}", state);
|
||||
if (state != null)
|
||||
redirectUri.queryParam(OAuth2Constants.STATE, state);
|
||||
Response.ResponseBuilder location = Response.status(302).location(redirectUri.build());
|
||||
Cookie remember = request.getHttpHeaders().getCookies().get(AuthenticationManager.KEYCLOAK_REMEMBER_ME);
|
||||
rememberMe = rememberMe || remember != null;
|
||||
// refresh the cookies!
|
||||
authManager.createLoginCookie(realm, accessCode.getUser(), session, uriInfo, rememberMe);
|
||||
if (rememberMe) authManager.createRememberMeCookie(realm, uriInfo);
|
||||
return location.build();
|
||||
}
|
||||
|
||||
public Response redirectError(ClientModel client, String error, String state, String redirect) {
|
||||
if (Constants.INSTALLED_APP_URN.equals(redirect)) {
|
||||
return Flows.forms(providerSession, realm, uriInfo).setError(error).createCode();
|
||||
} else {
|
||||
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, error);
|
||||
if (state != null) {
|
||||
redirectUri.queryParam(OAuth2Constants.STATE, state);
|
||||
}
|
||||
return Response.status(302).location(redirectUri.build()).build();
|
||||
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.ERROR, error);
|
||||
if (state != null) {
|
||||
redirectUri.queryParam(OAuth2Constants.STATE, state);
|
||||
}
|
||||
return Response.status(302).location(redirectUri.build()).build();
|
||||
}
|
||||
|
||||
public Response processAccessCode(String scopeParam, String state, String redirect, ClientModel client, UserModel user, UserSessionModel session, String username, boolean rememberMe, String authMethod, Audit audit) {
|
||||
|
|
|
@ -148,6 +148,10 @@ public class Urls {
|
|||
return tokenBase(baseUri).path(TokenService.class, "registerPage").build(realmId);
|
||||
}
|
||||
|
||||
public static URI realmInstalledAppUrnCallback(URI baseUri, String realmId) {
|
||||
return tokenBase(baseUri).path(TokenService.class, "installedAppUrnCallback").build(realmId);
|
||||
}
|
||||
|
||||
public static URI realmOauthAction(URI baseUri, String realmId) {
|
||||
return tokenBase(baseUri).path(TokenService.class, "processOAuth").build(realmId);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.testsuite.org.keycloak.testsuite.util;
|
||||
package org.keycloak.testsuite;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
|
@ -34,7 +34,7 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.org.keycloak.testsuite.util.MailUtil;
|
||||
import org.keycloak.testsuite.MailUtil;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
|
@ -50,8 +50,6 @@ import org.openqa.selenium.WebDriver;
|
|||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import java.io.IOException;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
|
|
@ -36,7 +36,7 @@ import org.keycloak.representations.idm.CredentialRepresentation;
|
|||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.org.keycloak.testsuite.util.MailUtil;
|
||||
import org.keycloak.testsuite.MailUtil;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
|
|
|
@ -112,6 +112,35 @@ public class AuthorizationCodeTest {
|
|||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authorizationRequestInstalledAppCancel() throws IOException {
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").addRedirectUri(Constants.INSTALLED_APP_URN);
|
||||
}
|
||||
});
|
||||
oauth.redirectUri(Constants.INSTALLED_APP_URN);
|
||||
|
||||
oauth.openLoginForm();
|
||||
driver.findElement(By.name("cancel")).click();
|
||||
|
||||
String title = driver.getTitle();
|
||||
Assert.assertTrue(title.equals("Error error=access_denied"));
|
||||
|
||||
String error = driver.findElement(By.id(OAuth2Constants.ERROR)).getText();
|
||||
Assert.assertEquals("access_denied", error);
|
||||
|
||||
events.expectLogin().error("rejected_by_user").user((String) null).session((String) null).removeDetail(Details.USERNAME).removeDetail(Details.CODE_ID).detail(Details.REDIRECT_URI, Constants.INSTALLED_APP_URN).assertEvent().getDetails().get(Details.CODE_ID);
|
||||
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.getApplicationNameMap().get("test-app").removeRedirectUri(Constants.INSTALLED_APP_URN);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authorizationValidRedirectUri() throws IOException {
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
|
|
Loading…
Reference in a new issue