mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-07 14:02:04 -03:30
Fix organization invitation redirect to respect account client base URL
When an organization's redirect URL is left empty, Keycloak currently defaults to the account console URL, ignoring the account client's configured Home URL (base URL). This fix checks the account client's base URL before falling back to the default account console URL. Changes: - Added resolveAccountClientBaseUrl() helper method in OrganizationInvitationResource - Added setBaseUrl() method to ClientAttributeUpdater test utility - Added integration tests for the new behavior Closes #45052 Signed-off-by: Rathan Naik <30756840+Rathan-Naik@users.noreply.github.com>
This commit is contained in:
parent
a6bf194487
commit
2af7c843af
@ -39,6 +39,7 @@ import org.keycloak.email.EmailException;
|
||||
import org.keycloak.email.EmailTemplateProvider;
|
||||
import org.keycloak.events.admin.OperationType;
|
||||
import org.keycloak.events.admin.ResourceType;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.OrganizationInvitationModel;
|
||||
@ -58,6 +59,7 @@ import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.resources.KeycloakOpenAPI;
|
||||
import org.keycloak.services.resources.LoginActionsService;
|
||||
import org.keycloak.services.resources.admin.AdminEventBuilder;
|
||||
import org.keycloak.services.util.ResolveRelative;
|
||||
import org.keycloak.services.validation.Validation;
|
||||
import org.keycloak.storage.adapter.InMemoryUserAdapter;
|
||||
import org.keycloak.utils.StringUtil;
|
||||
@ -203,7 +205,7 @@ public class OrganizationInvitationResource {
|
||||
token.id(invitation.getId());
|
||||
|
||||
if (organization.getRedirectUrl() == null || organization.getRedirectUrl().isBlank()) {
|
||||
token.setRedirectUri(Urls.accountBase(session.getContext().getUri().getBaseUri()).path("/").build(realm.getName()).toString());
|
||||
token.setRedirectUri(resolveAccountClientBaseUrl());
|
||||
} else {
|
||||
token.setRedirectUri(organization.getRedirectUrl());
|
||||
}
|
||||
@ -211,6 +213,19 @@ public class OrganizationInvitationResource {
|
||||
return token.serialize(session, realm, session.getContext().getUri());
|
||||
}
|
||||
|
||||
private String resolveAccountClientBaseUrl() {
|
||||
ClientModel accountClient = realm.getClientByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID);
|
||||
|
||||
if (accountClient != null) {
|
||||
String baseUrl = accountClient.getBaseUrl();
|
||||
if (baseUrl != null && !baseUrl.isBlank()) {
|
||||
return ResolveRelative.resolveRelativeUri(session, accountClient.getRootUrl(), baseUrl);
|
||||
}
|
||||
}
|
||||
|
||||
return Urls.accountBase(session.getContext().getUri().getBaseUri()).path("/").build(realm.getName()).toString();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Get invitations for the organization")
|
||||
|
||||
@ -155,6 +155,11 @@ public class ClientAttributeUpdater extends ServerResourceUpdater<ClientAttribut
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientAttributeUpdater setBaseUrl(String baseUrl) {
|
||||
rep.setBaseUrl(baseUrl);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientAttributeUpdater addDefaultClientScope(String clientScope) {
|
||||
rep.getDefaultClientScopes().add(clientScope);
|
||||
return this;
|
||||
|
||||
@ -35,6 +35,7 @@ import org.keycloak.admin.client.resource.OrganizationResource;
|
||||
import org.keycloak.common.util.UriUtils;
|
||||
import org.keycloak.cookie.CookieType;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.utils.DefaultAuthenticationFlows;
|
||||
import org.keycloak.representations.idm.AuthenticationExecutionRepresentation;
|
||||
import org.keycloak.representations.idm.MemberRepresentation;
|
||||
@ -48,6 +49,7 @@ import org.keycloak.testsuite.admin.ApiUtil;
|
||||
import org.keycloak.testsuite.authentication.PushButtonAuthenticatorFactory;
|
||||
import org.keycloak.testsuite.pages.InfoPage;
|
||||
import org.keycloak.testsuite.pages.RegisterPage;
|
||||
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
||||
import org.keycloak.testsuite.updaters.OrganizationAttributeUpdater;
|
||||
import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
|
||||
import org.keycloak.testsuite.util.GreenMailRule;
|
||||
@ -187,6 +189,52 @@ public class OrganizationInvitationLinkTest extends AbstractOrganizationTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInviteWithAccountClientCustomBaseUrl() throws IOException, MessagingException {
|
||||
UserRepresentation user = createUser("invited", "invited@myemail.com");
|
||||
|
||||
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||
|
||||
try (
|
||||
ClientAttributeUpdater cau = ClientAttributeUpdater.forClient(adminClient, TEST_REALM_NAME, Constants.ACCOUNT_MANAGEMENT_CLIENT_ID)
|
||||
.setBaseUrl(OAuthClient.APP_AUTH_ROOT)
|
||||
.update();
|
||||
Response response = organization.members().inviteExistingUser(user.getId());
|
||||
) {
|
||||
assertThat(response.getStatus(), equalTo(Response.Status.NO_CONTENT.getStatusCode()));
|
||||
|
||||
acceptInvitation(organization, user, "AUTH_RESPONSE");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInviteNewUserRegistrationWithAccountClientCustomBaseUrl() throws IOException, MessagingException {
|
||||
String email = "inviteduser@email";
|
||||
String firstName = "Homer";
|
||||
String lastName = "Simpson";
|
||||
|
||||
OrganizationResource organization = testRealm().organizations().get(createOrganization().getId());
|
||||
|
||||
try (
|
||||
ClientAttributeUpdater cau = ClientAttributeUpdater.forClient(adminClient, TEST_REALM_NAME, Constants.ACCOUNT_MANAGEMENT_CLIENT_ID)
|
||||
.setBaseUrl(OAuthClient.APP_AUTH_ROOT)
|
||||
.update();
|
||||
) {
|
||||
organization.members().inviteUser(email, firstName, lastName).close();
|
||||
|
||||
registerUser(organization, email);
|
||||
|
||||
List<UserRepresentation> users = testRealm().users().searchByEmail(email, true);
|
||||
assertThat(users, not(empty()));
|
||||
MemberRepresentation member = organization.members().member(users.get(0).getId()).toRepresentation();
|
||||
Assert.assertNotNull(member);
|
||||
assertThat(member.getMembershipType(), equalTo(MembershipType.MANAGED));
|
||||
getCleanup().addCleanup(() -> testRealm().users().get(users.get(0).getId()).remove());
|
||||
|
||||
assertThat(driver.getTitle(), containsString("AUTH_RESPONSE"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInviteNewUserRegistration() throws IOException, MessagingException {
|
||||
String email = "inviteduser@email";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user