The example API specifies the minimal requirements for developing a new API that will be used for user provisioning from HelloID.
Note
The swagger interface can be found on: https://app.swaggerhub.com/apis-docs/ConnectorTeam/Basic-EXAMPLE-Target-API/1.0
First and foremost, this API is merely an example. Your API (or the API you need to build) probably differs in more than one way. For example: Your actions might have different names, methods or inputs. And that's okay. We understand that no two APIs are alike.
Hopefully, the example API and documentation will provide some insight on what we expect and what we need in order to build a solid connector that will interact with your API.
If you have any questions or concerns, feel free to contact us. We are always happy to explain things more in depth.
This repo contains the following:
- Source code for the 'Basic-EXAMPLE-Target-API' in the
srcfolder. - Postman collection with all API calls and examples in the
assetsfolder.
- Basic-Example-Target-API
Note
This project requires the .NET8 Desktop runtime. Make sure to download and install the .NET8 runtime from: https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-8.0.24-windows-x64-installer
- Download the application from: https://github.com/Tools4everBV/Basic-Example-Target-API/releases
- Open the
EXAMPLE.API.MANAGER.exefile. - Click
installto install API.
Note
By default, the project runs on: http://localhost:5006
If you wish to make changes to the settings, you can do so by opening the Settings window.
| Setting | Description | Default value |
|---|---|---|
LogLevel.Default |
Sets the default logging level for the entire application. Options include Trace, Debug, Information, Warning, Error, and Critical. |
Information |
LogLevel.Microsoft.AspNetCore |
Sets the logging level specifically for ASP.NET Core internals. Usually kept higher to avoid excessive log noise. | Warning |
| Setting | Description | Default value |
|---|---|---|
Endpoints.Http.Url |
Defines the HTTP endpoint and port on which the API will run locally. | http://localhost:5006 |
| AllowedHosts | (root setting) | Specifies which hosts are allowed to access the application. * allows all hosts (suitable for local development). |
| Setting | Description | Default value |
|---|---|---|
| ClientId | The ClientId used to request an OAuth token from the authentication endpoint. This uniquely identifies your application when calling the API. |
339ddde0-7219-4895-ab65-65d5dba91fff |
| ClientSecret | The ClientSecret paired with the ClientId to securely authenticate and retrieve an OAuth token. Treat this as a secret and do not expose it publicly. |
87a3354033418b90fc00ad9d06cb2cf36baf0276181efae9ce8a1e780cc024a3 |
| CopyTokenToClipBoard | When true, the retrieved OAuth token will be automatically copied to your clipboard, making it easy to paste into tools like Swagger or Postman for testing. |
false |
The project comes with a simple SQLite database. If you need a graphical interface to directly manage the database, go to: https://sqlitebrowser.org/dl/
When the Windows Service is installed properly, you can navigate to: {url/index.html}. From there you can either load the:
- User management interface at
{url}/management.html. - Swagger interface at:
{url}/swagger/index.html
The following actions are available:
| HTTP Method | Endpoint | Description |
|---|---|---|
| POST | /api/oauth/token | Retrieve an oAuth token |
We prefer OAuth for authentication because it’s secure, easy to use, and works smoothly with modern apps and APIs.
In order to retrieve a token, in our example API, we make an API call to: {url}/api/auth/token containing the following body:
{
"ClientId": "example-client-id",
"ClientSecret": "example-client-secret"
}Note
The ClientId and ClientSecret are automatically fetched from the appsettings.json file.
If CopyTokenToClipBoard is set to true, the token will automatically be copied to the clipboard for easy use.
| HTTP Method | Endpoint | Description |
|---|---|---|
| GET | /api/users | Get all users |
| POST | /api/users | Adds a user |
| GET | /api/users/employeeid/:employeeId | Gets a user by employeeId |
| GET | /api/users/:Id | Gets a user by Id |
| PATCH | /api/users/:id | Updates a user |
| DEL | /api/users/:id | Deletes a user |
We need to retrieve information about all users to support our import entitlement feature, enable reconciliation, and ultimately ensure proper governance.
Adds a new user account to the target system. The response must contain the internal database ID since this is the key we correlate on and will be used for consecutive requests to the target system. Therefore, the id field is enlisted in the user schema.
Before we add a user account to the target application, we need to validate if that user account exists.
Initially, the internal database ID is not known to us. Therefore, we prefer to validate this using the EmployeeId since this is unique and available in HelloID. In our example, validation uses a dedicated API call, though filtering on the EmployeeId would work just as well.
Note
Note that, retrieving all user accounts and do some type of lookup is -in most cases- not very well suited for HelloID and might cause performance issue's.
Warning
We only need to retrieve the user account using the employeeId in our initial create event. Subsequent events will use the internal database ID for lookups.
Before we update a particular user account, we need to validate if that user account still exists.
Note
Validating if the user account exists is an integral part in all our lifecycle events because the user account might be unintentionally deleted. In which case the lifecycle event will fail.
For example: when we want to enable the user account on the day the contract takes effect.
To update a user account we prefer to see an update call in the form of a patch. This means that we only update the values that have been changed.
Note
In the Basic-EXAMPLE-Target-API the patch method is implemented using JSON Patch. Note that this might not have to be the best solution for your application.
This action does not require a response. A [204 No Content] is sufficient.
| HTTP Method | Endpoint | Description |
|---|---|---|
| GET | /api/roles | Retrieves all roles |
Before we can assign a role to a user, we first need to retrieve all available roles. Then, we build out our business rules to, ultimately grant authorizations to user based on information coming from an HR source.
| HTTP Method | Endpoint | Description |
|---|---|---|
| GET | /api/authorizations | Get all authorizations for all users |
| GET | /api/authorizations/user/:userId | Get all authorizations for a specific user |
| POST | /api/authorizations | Add a new authorization for a specific user |
| DEL | /api/authorizations/delete/:id | Deletes an authorization |
We need to retrieve information about all authorizations (granted to all users) to support our import entitlement feature, enable reconciliation, and ultimately ensure proper governance.
An authorization may already be granted or revoked for a user. This endpoint lets us check which authorizations are currently active.
We will use this action when an authorization need to be granted to a user. Since we do not store the result in HelloID, this action does not require a response.
We will use this action when an authorization needs to be revoked from a user. This action does not require a response. A [204 No Content] is sufficient.
Note
The schema's listed below contain the bare minimum we need in order to build a solid connector.
The user schema contains all the parameters we expect to be present in an application. Your application / user schema might have different names for parameters, or far more parameters than enlisted in this schema.
For example: A PhoneNumber for two-factor authentication or a more complex multi-layered schema.
| Parameter | Description | Required | Type |
|---|---|---|---|
| Id | This is the internal / database Id. Typically, this value will be set by the application |
- | int |
| Active | Defines if the user is active or not. We will update this value when a user is enabled or disabled. When we initially create a user, we prefer to create that user in a disabled state. On the day the contract takes effect the user account will be enabled. |
True | bool |
| EmployeeId | The EmployeeId or ExternalId of the user. | True | string |
| FirstName | - | True | string |
| LastName | - | True | string |
| - | True | string |
| Parameter | Description | Required | Type |
|---|---|---|---|
| Id | This is the internal / database Id. Typically, this value will be set by the application |
- | int |
| RoleId | The Id of the role. | True | int |
| UserId | The Id of the user. | True | int |
| Parameter | Description | Required | Type |
|---|---|---|---|
| Id | This is the internal / database Id. Typically, this value will be set by the application |
- | int |
| DisplayName | The DisplayName of the role. | True | string |
Our governance module in HelloID provides reconciliation for Provisioning.
Reconciliation is aimed to ensure that the state of rights and permissions in target systems is as intended.
Users might change roles, leave the organization, or require adjustments to their access rights over time. Applications will be updated, and errors may occur that also impact access rights. As a result, there can be a misalignment between the entitlements that should have been granted according to HelloID and the actual access rights within the target application.
Reconciliation works by importing account and permission data from the target system and comparing this data against the state of entitlements in HelloID. Any issues — such as unexpected accounts, missing permissions, or mismatching account statuses — are listed and can be resolved.
