Service Applications (Service Access for Confidential Applications)
This topic describes how a confidential external application with no user interaction integrates with an ERP.net Instance ID to obtain an access token for calling instance APIs using the Client Credentials flow.
The following scenario is covered:
- The external application is a confidential application
- The application has a backend component capable of securely storing a secret
- The application does not present a user interface
- The application authenticates as a service identity
- The application uses the Client Credentials authorization flow
- The application obtains an access token to call instance APIs directly
The Instance ID is the identity provider of a specific ERP.net instance. Service applications use it to authenticate and obtain access tokens without user involvement, scoped to the permissions configured for the trusted application.
For example, if your ERP.net instance is located at:
https://demodb.my.erp.net
then the corresponding Instance ID endpoints are located at:
https://demodb.my.erp.net/id
Prerequisites
Your ERP.net instance must have a trusted application defined with the configuration below.
Note
The values shown below are examples only and must be replaced with values that match your application.
Typical values:
- ApplicationUri:
my-service-app-id - SystemUser:
<an-internal-erp-user>
Note
The application owner must generate a random client secret, compute its Base64-encoded SHA-256 hash, and submit the hashed value via an internal ticket to erp.net so the Trusted Application can be registered and the configuration activated.
| Attribute | Value | Comment |
|---|---|---|
| Name | My Service Application | Used only for user-friendly identification. |
| ApplicationUri | my-service-app-id | The unique identifier of the application. |
| IsEnabled | true | Enables the trusted application. |
| SystemUserAllowed | true | Allows the application to authenticate as a service. |
| SystemUser | <an-internal-erp-user> |
The internal user used for service authentication. |
| ImpersonateAsInternalUserAllowed | true | Allows authentication using an internal user. |
| ClientType | Confidential | Indicates that the application can securely store a secret. |
| ApplicationSecretHash | <base64(sha256(your-client-secret))> |
The hashed client secret used during authentication. |
| Scope | read or read update |
Use read for read-only access; include update only if the application must create, modify, or delete data |
All other attributes can keep their default values and are not relevant for this scenario.
Implementation
This section demonstrates acquiring an access token using the Client Credentials flow.
1. Request an access token
The application sends a direct request to the Instance ID token endpoint.
POST /id/connect/token HTTP/1.1
Host: demodb.my.erp.net
Content-Type: application/x-www-form-urlencoded
client_id=my-service-app-id&
client_secret=<PLAIN_CLIENT_SECRET>&
grant_type=client_credentials&
scope=read%20update
Note
The client_secret is sent as a plain (unhashed) value and must exactly match the secret configured for the trusted application.
2. Receive the token response
If the request is successful, the Instance ID returns an access token.
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjVCMjc5MjBFNjUzREQ3QUM2N0QyRjY0QjMyQTE3OTkyIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE3NjY1MDUxNDcsImV4cCI6MTc2NjUwODc0NywiaXNzIjoiaHR0cHM6Ly9lMS1kZXYubG9jYWwvaWQiLCJhdWQiOlsiRG9tYWluQVBJIiwiVGFibGVBUEkiLCJPTEFQIiwiQXBwU2VydmVyIiwiaHR0cHM6Ly9lMS1kZXYubG9jYWwvaWQvcmVzb3VyY2VzIl0sImNsaWVudF9pZCI6IlBLIiwiY2xpZW50X3N5c3RlbV91c2VyIjoiYWRtaW4iLCJjbGllbnRfc3lzdGVtX3VzZXJfdHlwZSI6IkludGVybmFsVXNlciIsImNsaWVudF9kYiI6IkUxX0RFVi0xIFRlc3QiLCJqdGkiOiI4RDBCNzA1NTczQTFBOThGREJBREZGMENEN0Y3RUVCNCIsImlhdCI6MTc2NjUwNTE0Nywic2NvcGUiOlsicmVhZCIsInVwZGF0ZSJdfQ.MRvH-EtWu-PWDhjDDn73OVQwH29DZ_RRu6XheFsoRxImyjLQRIU7-S1GuyTnnqPyXEEkGkKTu_s3IEwxGORgY48jLH3l1juJDt8_JvcyJlIdhVZSZNC1Bpft_K1NJswJ6QmJ6bWgev7cqHaxM3p7AEEPjkSmnAjdBCz7ItMV93Yio5kCRBmP9DQUoxtL0webG7zV_f5uOkt8xhbUVHpdU9FQY-XLf_heLJv_81vvpf39kPxD4WTZRly8X_mNdlqi0DxiFXK3TFOnbdoLKxeljke8jV0t-agaHWcZ4B-SMQ77falwtFaxrEzXDY4g-iUg2kl_tABOUxzoqyFkGZm3DA",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "read update"
}
The application can now use the access token to call instance APIs.
Example (Domain) API request:
GET /api/domain/odata/Crm_Sales_Customers?$top=10 HTTP/1.1
Host: demodb.my.erp.net
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjVCMjc5MjBFNjUzREQ3QUM2N0QyRjY0QjMyQTE3OTkyIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE3NjY1MDUxNDcsImV4cCI6MTc2NjUwODc0NywiaXNzIjoiaHR0cHM6Ly9lMS1kZXYubG9jYWwvaWQiLCJhdWQiOlsiRG9tYWluQVBJIiwiVGFibGVBUEkiLCJPTEFQIiwiQXBwU2VydmVyIiwiaHR0cHM6Ly9lMS1kZXYubG9jYWwvaWQvcmVzb3VyY2VzIl0sImNsaWVudF9pZCI6IlBLIiwiY2xpZW50X3N5c3RlbV91c2VyIjoiYWRtaW4iLCJjbGllbnRfc3lzdGVtX3VzZXJfdHlwZSI6IkludGVybmFsVXNlciIsImNsaWVudF9kYiI6IkUxX0RFVi0xIFRlc3QiLCJqdGkiOiI4RDBCNzA1NTczQTFBOThGREJBREZGMENEN0Y3RUVCNCIsImlhdCI6MTc2NjUwNTE0Nywic2NvcGUiOlsicmVhZCIsInVwZGF0ZSJdfQ.MRvH-EtWu-PWDhjDDn73OVQwH29DZ_RRu6XheFsoRxImyjLQRIU7-S1GuyTnnqPyXEEkGkKTu_s3IEwxGORgY48jLH3l1juJDt8_JvcyJlIdhVZSZNC1Bpft_K1NJswJ6QmJ6bWgev7cqHaxM3p7AEEPjkSmnAjdBCz7ItMV93Yio5kCRBmP9DQUoxtL0webG7zV_f5uOkt8xhbUVHpdU9FQY-XLf_heLJv_81vvpf39kPxD4WTZRly8X_mNdlqi0DxiFXK3TFOnbdoLKxeljke8jV0t-agaHWcZ4B-SMQ77falwtFaxrEzXDY4g-iUg2kl_tABOUxzoqyFkGZm3DA
Security and implementation considerations
- This flow is intended for machine-to-machine communication.
- No user authentication or interaction occurs.
- The client secret must never be exposed outside the backend.
- Tokens are scoped and limited to the permissions configured for the application.