Add custom attributes support to organization invitations#48842
Add custom attributes support to organization invitations#48842kota65535 wants to merge 2 commits into
Conversation
9691030 to
c00970f
Compare
5c0f2b1 to
c51e773
Compare
edewit
left a comment
There was a problem hiding this comment.
other then some small nit picks looks good from a UI perspective
| const attributes = keyValueToArray(data.attributes); | ||
| await adminClient.organizations.inviteUserJson( | ||
| { orgId }, | ||
| { | ||
| email: data.email, | ||
| firstName: data.firstName, | ||
| lastName: data.lastName, | ||
| attributes: | ||
| Object.keys(attributes).length > 0 ? attributes : undefined, | ||
| }, | ||
| ); |
There was a problem hiding this comment.
bit of a nit pick, but this could be a lot shorter:
| const attributes = keyValueToArray(data.attributes); | |
| await adminClient.organizations.inviteUserJson( | |
| { orgId }, | |
| { | |
| email: data.email, | |
| firstName: data.firstName, | |
| lastName: data.lastName, | |
| attributes: | |
| Object.keys(attributes).length > 0 ? attributes : undefined, | |
| }, | |
| ); | |
| const { attr, ...rest } = data; | |
| const attributes = keyValueToArray(attr) && undefined; | |
| await adminClient.organizations.inviteUserJson({ orgId }, { ...rest, attributes }) |
There was a problem hiding this comment.
Thank you, attributes should be undefined if it is empty here, but with this code keyValueToArray(attr) && undefined doesn't return undefined because an empty object {} is truthy.
There was a problem hiding this comment.
but it's an array not an object [] && undefined returns undefined
There was a problem hiding this comment.
Thank you for sharing the snippet, but the return type of keyValueToArray function appears to be Object.
There was a problem hiding this comment.
ohh right but then my version is still a bit shorter:
const { attr, ...rest } = data;
const attributes = keyValueToArray(attr);
await adminClient.organizations.inviteUserJson({ orgId }, { ...rest, attributes: Object.keys(attributes).length ? attributes : undefined });
but couldn't we fix it on the server that when attributes is an empty object it would still work?
There was a problem hiding this comment.
Oh, it works even with an empty map. I've simplified this.
https://github.com/keycloak/keycloak/pull/48842/changes#diff-aa6e7e0717a4bf2e09aa5dc5bdd4a4bcb4f9171eca199747c73f8226572f5b36R48
cf3d202 to
8c9ec01
Compare
8c9ec01 to
22dd211
Compare
62c66a1 to
4878832
Compare
|
@keycloak/maintainers Could someone also review the backend/model/API parts of this PR? |
4878832 to
93fed69
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds end-to-end support for custom key-value attributes on organization invitations, spanning persistence (new DB table + JPA entities), admin REST API (JSON request bodies + representation updates), admin client typings, Admin UI flows for entering attributes, and integration tests validating creation/resend behavior.
Changes:
- Persist invitation attributes in a new
ORG_INVITATION_ATTRIBUTEtable and expose them via the invitation representation. - Add JSON
application/jsonvariants forinvite-user/invite-existing-userendpoints while keeping form-encoded variants for backward compatibility. - Update Admin UI invite flows to collect attributes (single-step for email invites, 2-step modal for existing-user invites) and display attribute counts/details in the invitations list.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/base/src/test/java/org/keycloak/tests/organization/admin/OrganizationInvitationAttributeTest.java | Adds integration tests covering invitation attributes creation/resend/empty/null behaviors. |
| services/src/main/java/org/keycloak/organization/admin/resource/OrganizationMemberResource.java | Adds JSON-consuming invite endpoints delegating to invitation resource. |
| services/src/main/java/org/keycloak/organization/admin/resource/OrganizationInvitationResource.java | Threads attributes through invite/create/resend and includes them in representations. |
| server-spi/src/main/java/org/keycloak/organization/InvitationManager.java | Extends invitation creation SPI to accept optional attributes (with backward-compatible default). |
| server-spi/src/main/java/org/keycloak/models/OrganizationInvitationModel.java | Adds invitation attributes accessor to the model SPI. |
| model/jpa/src/main/resources/META-INF/jpa-changelog-26.7.0.xml | Adds Liquibase change set creating ORG_INVITATION_ATTRIBUTE with FK cascade delete + index. |
| model/jpa/src/main/resources/default-persistence.xml | Registers the new JPA entity for persistence. |
| model/jpa/src/main/java/org/keycloak/organization/jpa/JpaInvitationManager.java | Persists invitation attributes during invitation creation. |
| model/jpa/src/main/java/org/keycloak/models/jpa/entities/OrganizationInvitationEntity.java | Adds @OneToMany relationship and exposes attributes as Map<String, List<String>>. |
| model/jpa/src/main/java/org/keycloak/models/jpa/entities/OrganizationInvitationAttributeEntity.java | New JPA entity mapping for invitation attributes. |
| model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/organization/InfinispanInvitationManager.java | Forwards attribute-aware create calls to delegate manager. |
| js/libs/keycloak-admin-client/src/resources/organizations.ts | Expands invite request payload typing to allow JSON request bodies. |
| js/libs/keycloak-admin-client/src/defs/organizationInvitationRequest.ts | Adds admin-client request type definitions for JSON invite endpoints. |
| js/libs/keycloak-admin-client/src/defs/organizationInvitationRepresentation.ts | Adds attributes to JS invitation representation type. |
| js/apps/admin-ui/src/user/Organizations.tsx | Implements 2-step “select orgs → set attributes” flow for inviting an existing user. |
| js/apps/admin-ui/src/organizations/OrganizationModal.tsx | Updates confirm label to “next” for 2-step flow. |
| js/apps/admin-ui/src/organizations/InviteMemberModal.tsx | Switches email-invite to JSON body and adds attribute input. |
| js/apps/admin-ui/src/organizations/Invitations.tsx | Implements 2-step invite flow for existing users and displays attributes count/tooltip. |
| js/apps/admin-ui/src/organizations/InvitationAttributesModal.tsx | New modal for entering invitation attributes. |
| js/apps/admin-ui/maven-resources/theme/keycloak.v2/admin/messages/messages_en.properties | Adds i18n string for the attributes modal title. |
| integration/admin-client/src/main/java/org/keycloak/admin/client/resource/OrganizationMembersResource.java | Adds JSON @Consumes methods for invite endpoints in the Java admin client. |
| core/src/main/java/org/keycloak/representations/idm/OrganizationInvitationUserRequest.java | New request DTO for JSON email-invite including attributes. |
| core/src/main/java/org/keycloak/representations/idm/OrganizationInvitationRepresentation.java | Adds attributes to the invitation representation. |
| core/src/main/java/org/keycloak/representations/idm/OrganizationInvitationExistingUserRequest.java | New request DTO for JSON existing-user invite including attributes. |
|
Hi @edewit, sorry for another ping. This PR seems to touch a few areas beyond the Admin UI, so I’m not sure whether you’re the only reviewer needed here. Could you please help forward this to the right maintainers, or let me know who I should ask for review? Thanks! |
edewit
left a comment
There was a problem hiding this comment.
looks good from a UI point of view
546304e to
93fed69
Compare
|
@edewit I'm so sorry, I pushed a commit to the wrong branch by mistake, which caused the approval to be dismissed. Could you please approve it again? |
93fed69 to
86d947f
Compare
86d947f to
4f6eb59
Compare
Signed-off-by: Tomohiko Ozawa <kota65535@gmail.com>
4f6eb59 to
e5cab70
Compare
Unreported flaky test detectedIf the flaky tests below are affected by the changes, please review and update the changes accordingly. Otherwise, a maintainer should report the flaky tests prior to merging the PR. org.keycloak.testsuite.forms.MultipleTabsLoginTest#testLoginPageRefreshKeycloak CI - Forms IT (chrome) org.keycloak.testsuite.forms.MultipleTabsLoginTest#multipleTabsParallelLoginTestWithAuthSessionExpiredAndRegisterClickKeycloak CI - Forms IT (chrome) |
|
Hi @pedroigor, I am mentioning you because this feature is related to Keycloak Organizations. I have resolved all of Copilot's comments and confirmed that all tests pass. Could you please review this PR or assign an appropriate reviewer so that it can be merged? |
Closes: #48837
Summary
application/jsonrequest body support toinvite-userandinvite-existing-userendpointsChanges
Backend
ORG_INVITATION_ATTRIBUTEtable (jpa-changelog-26.7.0.xml) with FK cascade deleteOrganizationInvitationAttributeEntityJPA entityOrganizationInvitationModelSPI withgetAttributes()/setAttribute()/removeAttribute()InvitationManager.createInvitation()with optionalattributesparameter (backward-compatible default method)@Consumesvariants forinvite-userandinvite-existing-userendpoints alongside existingapplication/x-www-form-urlencodedendpointsOrganizationInvitationUserRequest,OrganizationInvitationExistingUserRequestattributesfield toOrganizationInvitationRepresentationAdmin UI
InviteMemberModal: switch from FormData to JSON body, add key-value attributes input (single-step flow for email invitations)InvitationAttributesModal: reusable attributes modal shared across all invite flowsInvitations.tsx: 2-step flow usingMemberModal(user selection) →InvitationAttributesModal(attributes) for existing user invitationsOrganizations.tsx(user detail page): 2-step flow usingOrganizationModal(org selection) →InvitationAttributesModal(attributes)Invitations: show attributes count with tooltip displaying key-value details on hoverOrganization details → Invite new user
Using
InviteMemberModal:Organization details → Invite existing user
Using
OrganizationModalandInvitationAttributesModal(2-step flow):↓

User details → Send invitation
Using
MemberModalandInvitationAttributesModal(2-step flow):↓

Attributes in an invitation list
Showing the number of the attributes:
Showing the attribute in a tooltip on hover:

Admin Client (JS)
InviteUserRequest/InviteExistingUserRequesttype definitionsinviteUserJson/inviteExistingUserJsonmethods to organizations resourceTests
tests/base/)API
Migration
ORG_INVITATION_ATTRIBUTEtable added injpa-changelog-26.7.0.xmlBackward Compatibility
application/x-www-form-urlencodedendpoints remain unchangedInvitationManagerSPI maintains existing method signatures via default methodsAI-assisted development was used in this PR with Claude Code. All changes have been reviewed
and are understood by the author.