This is an exercise on how to create a user-registration possibility. As the frontend interface isn't developed (yet), this exercise follows the standard of SCIM 2.0 to allow for compatibility with existing products/service, e.g. Okta. It is implemented in Jakarta EE 9 (using Wildfly as application server).
- Java JDK 11 (e.g. OpenJDK 11.0.12)
- Apache Maven (e.g. Maven 3.6.3)
For the TL;DR - approach,
- execute within the project directory to build and deploy to an in-promptu Wildfly server
mvn clean wildfly:run -Pwildfly \ -Dwildfly.artifactId=wildfly-preview-dist \ -Dwildfly.version=24.0.1.Final - open http://localhost:8080/user/ in your browser for a Swagger-UI Endpoint interface

In case of version conflicts on the target machine (e.g. JDK), building and starting a local docker container is a potential workaround to test the service:
- copy the latest built
target/user.warfile to thedockerdirectory- (alternatively download a version from github)
- change to the
dockerdirectory and build/run the containercd docker docker build . -t user-on-wildfly docker run -d -p8080:8080 user-on-wildfly
- wait a moment until the instance started up in the background ☕
- open http://localhost:8080/user/ in your browser
Test-drive the service using Okta.
- create user-war
mvn packageand deploy to an outside-available Wildfly instance - use an Okta developer account to connect an application "SCIM 2.0 Test App (Header Auth)"
- ignore the SAML authentication steps, select "Provisioning" -> "Enable API integration"
- set "Base API" to
http://<AddYourIpHere>:8080/user/api/scim/v2/, leave "API Token" empty - enable "Provisioning to App" the following topics and save
- "Create Users"
- "Update User Attributes"
- "Deactivate Users"
- "Sync Password"
- import the demo user into Okta
- backend-system without any UI functionality (except a swagger-UI endpoint)
- services provided as RESTful web service
- payloads are in JSON format (SCIM v2 format, where possible)
- data storage is in-memory database of the application server (JBoss wildfly) only
- no SSL or authentication included (yet)
⚠ This is not production-ready code, please do not necessarily use it as that.
(implemented functional business requirements/services are marked accordingly with a checkmark ✓)
- ✓ list all users - provide a list and reduced details for all users in the system. Current solution includes paging.
- ✓ filter for users - current solution includes search by
usernameandlastModifiedAfter. - ✓ receive data from a single user
- ✓ add new user
- ✓ modify existing user - current solution, following existing SCIM implementations, differs between credential changes (done as http patch) and user details (done as http put)
- ✓ mark user as deactivated - keeps the user data, but disallows e.g. logging in as the user.
- ✓ provide available provider configuration/services - keeps the user data, but disallows e.g. logging in as the user.
- ✓ provide Open-API definition - to allow fast integration of the service.
- 🗒 self-service password reset - in case of a forgotten password, a new one can be created and sent to the user based on something the user knows/owns/can inherence.
- 🗒 throttling user creation/modification - this reduces digital vandalism.
- 🗒 confirm e-mail/phone data - by sending confirmation e-mail/text-message to user.
- 🗒 allow mass/batch CRUD - for import of pre-existing user-data, or mass-deactivation in case of organisational changes
This part explains some fundamental technical decisions about the exercise.
The user entity is split into three actual tables, connected by a one-to-one mapping.
The main user-entity holds user-specific settings like timezone and locale specification. A credential-entity, used during the login-process, holds a foreign key to the user-entity. By this, during the login process no direct access to the concrete user-entity is needed. The user-entity holds a foreign key to the contact-entity. By separating user and contact, potential policies of persisting system data and personal data separately are (hopefully) met.
Beside the internal Id, a public Id is given to all entities - it is an alphanumeric hash. We use it for endpoint-communication. The public id can be given from an external source and potentially also changed without corrupting any data. It's a soft protection towards attacks based on id-guessing.
For the sake of this example, the service persists the user password's hash and salt only. It offers a method to check a password for correctness, without giving a way to reproduce the original password.
The endpoint design is in alignment with Okta's SCIM implementation. Creating an Oka Dev account and testing towards this service is expected to be successful.
For changing existing user-data, two http methods are used:
- put - change user-specific details, but changes to active or password are ignored.
- patch - changing single-valued fields. This only works for
activeandpasswordat the moment.
The following minimal filters are supported as GET parameter, they can not be concatenated.
filter=userName eq "test.user@email.local"filter=lastModified gt "2021-11-11T04:42:34Z"
- Resteasy does not forward Runtime exceptions to exception mapper without server-specific dependencies. As result e.g., NPEs are not caught correctly.
- user patching is a minimal first-draft implementation at the moment.
- the json schema definition is mocked. Bringing this to the next level could bring advantages.