API Facade and Security
- Tutorial
This document introduces the RestFrontage entry points for authenticating a client, viewing or modifying security, filtering OData objects, and using Razor tools.
For the conceptual model and decision rules, see Immersive Security Overview.
Authenticating an API Client
Get a token
POST {{baseUrl}}/api/Security/User/Authenticate
Content-Type: application/json
{
"AccountName": "utilisateur",
"Password": "mot-de-passe"
}
Alias accepted: POST /api/Security/Users/Authenticate.
Simplified answer:
{
"Domain": "DC=Immersive, DC=GraphicStream, DC=fr",
"Token": "eyJ...",
"AuthenticatedUser": {
"Identifier": 123,
"DistinguishedNameValue": "AN=utilisateur, OU=Users, ..."
}
}
The customer then transmits:
Authorization: Bearer {{token}}
Logins and password must be sent only over HTTPS.
Token cycle
| Word and road | Function |
|---|---|
POST /api/Security/Token/Renew |
Renew a valid token |
GET or POST /api/Security/Token/Validate |
Verify that the pipeline accepts the token |
GET or POST /api/Security/Whoami |
Get the name of the current identity |
Primary Security Endpoints
All roads below have the prefix /api/Security.
Rights Control and Administration
| Word and road | Usage | Recommended Status |
|---|---|---|
GET /Users/{askerId}/Rights/{flags}/SecurizedObjects/{type}/{objectId} |
Testing a mask on an object | History; does not include shares |
GET /Rights?securedObjectIdentifier=...&securedObjectTypeIdentifier=... |
Read an object's explicit rights | Can be used for administration |
POST /Rights |
Apply a collection of rights via ISecurityAdministrationService |
Recommended Writing Point |
DELETE /Rights?rightIdentifiers=... |
Remove rights by ID | History; to migrate to the central façade |
GET /Permissions |
Read configured permissions | Recommended for building an interface without hard-coded flags |
Example of the application of a right:
POST {{baseUrl}}/api/Security/Rights
Authorization: Bearer {{token}}
Content-Type: application/json
[
{
"Identifier": 0,
"DomainSecurityObjectIdentifier": 145,
"SecurizedObjectIdentifier": 902,
"SecurizedObjectType": 15,
"AuthorizedFlags": 3,
"DeniedFlags": 0
}
]
Rules of POST /Rights :
- the actor is always read from the authenticated context;
- Cerebrate and root administrators can administer all rights;
- otherwise the actor and the beneficiaries must belong to the current organization;
- a beneficiary
BuiltInis protected; - the actor must be able to administer each target object;
- flags must match the configured permissions;
AuthorizedFlags = 0andDeniedFlags = 0abolishes the corresponding duty;- Multiple queries for the same triplet are normalized before writing.
Error responses:
400- Invalid data or flags401: absence of authenticated identity;403: unauthorized actor or poor organization;409: Data inconsistency or conflict, such as old duplicate rights.
Shares
| Word and road | Usage |
|---|---|
POST /SharedAccess |
Create a share that AccessLinkIdentifier footnote one TinyUrl existing |
POST /SharedAccess/Aggregate |
Explicitly create an aggregate and specify the TinyUrl at the root level |
POST /SharedAccess/{grantIdentifier}/Revoke |
Revoke sharing without deleting the TinyUrl |
Example of internal sharing for a week:
POST {{baseUrl}}/api/Security/SharedAccess/Aggregate
Authorization: Bearer {{token}}
Content-Type: application/json
{
"TinyUrlIdentifier": "equipment-902-share",
"Grant": {
"BeneficiaryDomainSecurityObjectIdentifier": 245,
"ExternalRecipientEmail": null,
"ValidFromUtc": "2026-08-18T10:00:00Z",
"ExpiresAtUtc": "2026-08-25T10:00:00Z",
"Rights": [
{
"SecurizedObjectIdentifier": 902,
"SecurizedObjectType": 18,
"AuthorizedFlags": 1
},
{
"SecurizedObjectIdentifier": 84,
"SecurizedObjectType": 15,
"AuthorizedFlags": 1
},
{
"SecurizedObjectIdentifier": 31,
"SecurizedObjectType": 48,
"AuthorizedFlags": 1
}
]
}
}
For an external recipient, fill in ExternalRecipientEmail and leave BeneficiaryDomainSecurityObjectIdentifier à null. Exactly one of the two fields must be defined.
Simplified answer:
{
"grantIdentifier": 12,
"publicIdentifier": "...",
"externalAccessToken": "retourné uniquement pour un partage externe",
"tinyUrlIdentifier": "equipment-902-share",
"tinyUrlPath": "/?TU=equipment-902-share",
"externalAccessPath": "/Shared?token=...&tinyUrl=equipment-902-share"
}
The server does not infer any dependencies. Beholder, Forge or the Player must send the complete list of objects needed to open the target.
There is no dedicated REST route for the share list yet. This list is currently consumed by the partial Razor via GetSharedAccessGrantsAsync.
Minimum call of the sharing modal:
/Security/Modals/SharedAccess
?SecurizedObjectIdentifier={identifier}
&SecurizedObjectType={type}
&TinyUrlIdentifier={tinyUrlExistante}
Directory
The façade also exhibits the following families:
| Family | Main roads |
|---|---|
| Resolution | GET /DomainObjects/{dnOuId}, POST /Discover |
| Navigation | GET /DomainObjects/{dn}/Members, GET /Explorer/{dn}/Search/{criteria} |
| Affiliations | GET, POST, PUT, DELETE /DomainObjects/{dn}/Membership |
| Group Members | PUT, POST, DELETE /Groups/{dn}/Members |
| Hierarchy | GET /DomainObjects/{dn}/GroupMembershipHierarchy |
| Expandable Fields | GET, PUT, POST /DomainObjects/{dn}/Fields |
| Users | POST /Users, PUT /Users, PATCH /Users({id}), DELETE /Users/{id} |
| Groups | POST /Groups, PUT /Groups, PATCH /Groups({id}), DELETE /Groups/{id} |
| OR | POST /OrganizationalUnits, PUT /OrganizationalUnits, PATCH /OrganizationalUnits({id}), DELETE /OrganizationalUnits/{id} |
| Organizational reconstruction | POST /OrganizationalUnits/{id}/SubDomain/Rebuild |
These directory routes are partly historical and do not all pass through ISecurityAdministrationService. Any new mutation must use the transversal façade or extend it.
Secure OData
ODataCrudController<TEntity> Enforces security only if the Immersive type has permissions configured.
For a secure type:
GETfilters theIQueryablebefore the OData options are applied.GET(key)and the navigations check the visibility of the parent;PUTandPATCHrequire the controller's permission to edit;DELETErequires the controller's permission to delete;POSTrequires Cerebrate, a root administrator, or theAdministratorsThe target subdomain.- After creation, the automatic rights are applied by the administration façade.
| Entity set | Type | Reading | Modification | Deletion |
|---|---|---|---|---|
/odata/Maps |
15 |
ReadMap |
ModifyMap |
FullControlMap |
/odata/Equipments |
18 |
ReadEquipment |
ModifyEquipment |
FullControlEquipment |
/odata/Workspaces |
37 |
ReadWorkspace |
ModifyWorkspace |
FullControlWorkspace |
/odata/SupervisionScopes |
48 |
ReadSupervisionScope |
ModifySupervisionScope |
FullControlSupervisionScope |
Example:
GET {{baseUrl}}/odata/SupervisionScopes?$filter=Name eq 'Yvetot'&$select=Identifier,Name
Authorization: Bearer {{token}}
Accept: application/json
The security filter and the OData filter are composed and then executed by the business base. The controller does not have to download the entire collection before filtering.
Useful RestFrontage Pages
Main entrances
| Path | Function | Access |
|---|---|---|
/Security |
Current Domain Dashboard | Authenticated user |
/Security/Explorer |
Browse ORs, users, groups, and devices | Authenticated user |
/Security/UsersAndGroups |
Find and administer users and groups | Authenticated user; mutations controlled by their handlers |
/Security/RightsTester |
Explaining an effective right | Authenticated user and current organization |
/Security/Tools |
Security audits and repairs | Cerebrate only |
Rights Tester
The tester allows you to choose:
- a user or group;
- A recognized object type.
- An object belonging to the current organization.
- A permission configured for this type.
It produces a AuditAccess which details the membership paths that have allowed or denied access. The catalog detects types from the DbSet<TEntity> whose entity implements IIdentifiedObject and has a ImmersiveObjectAttribute.
Current limits: Selection capped at 5,000 objects per type and no internal shares in the report.
Cerebrate Tools
The page /Security/Tools is accessible only by Cerebrate. It should not be used as a common administration interface.
Singleness of Rights
The tool displays:
- the number of rights;
- duplicate groups;
- the presence of the unique SQL index.
The current manual cleanup keeps the most recent row of the chosen group. Migration 20260815_EnforceUniqueRights follows a more conservative strategy: it first merges all allowed and forbidden flags before removing the old lines.
Structure of organizations
The audit verifies for each organization:
- ORs
Groups,UsersandDevices; - Groups
Administrators,UsersandSharers; - The user
{Organisation}_Admin; - Standard memberships
- conventional permission groups already recognized.
Reparation calls for EnsureSubDomainDefaultsAsync and preserves existing data.
Automatic fees
The audit compares the expected and registered entitlements for:
- maps;
- equipment;
- workspaces;
- the scope of supervision.
It can repair an object or a selection. The repair only adds the missing auto flags.
Points of vigilance and tightening to be expected
High priority
GET /api/Security/ResetDomaincurrently carries[AllowAnonymous]. Even if it doesn't change the rights, it causes a global reload and invalidation; it must be protected or moved to the recovery console.DELETE /api/Security/Rightsdeletes directly inSecurityModelwithout going through the authorization ofISecurityAdministrationService. It must be replaced byPOST /Rightswith zero flags or by a dedicated central method.- Internal shares are not yet viewed by
ObjectSecurityService. They are persisted but do not yet provide access in OData. - The historic test
HaveRightand Razor tester useSecurityUtilsdirectly and do not see the shares.
Consistency priority
- Gradually centralize the transfer of users, groups, OR and memberships.
- Add an authenticated organization selection route that is suitable for stateless Bearer customers, or include a controlled organization context in the API contract.
- Expose a share list route if WebGL clients need to administer shares without Razor partisanship.
- Extend automatic entitlement auditing when new types are plugged into the pipeline.
- Eventually retire historical aliases after client migration.
Add a new secure type
To extend the façade without creating a service by type:
- declare the type in
ImmersiveObjectType; - Apply
ImmersiveObjectAttributeon the entity; - register its
Permissionin the field and the instantObjectsToPermissions; - derive the controller from
ODataCrudController<TEntity>; - provide
ReadPermission,UpdatePermissionandDeletePermission; - inject existing cross-cutting services;
- ensure that
ISubDomainServicecan find the organization of the entity; - add its
RightsDefaultPatternif necessary; - Add the type to the automatic entitlement audit.
- Test read, create, edit, delete, ban, Cerebrate, and multi-instance.
Reference Files
| Subject | File |
|---|---|
| REST Controller | Controllers/SecurityController.cs |
| Session Controller/Organization | Controllers/SessionController.cs |
| JWT Pipeline | Commons/Immersive.RestFrontage.Components/Security/TokenParserMiddleware.cs, TokenCheckMiddleware.cs |
| Generic OData Controller | corporate/Commons/GraphicStream.Server.Components/Controllers/ODataCrudController.cs |
| Tester Catalog | Security/SecurizedObjectCatalog.cs |
| Tester | Pages/Security/RightsTester.cshtml.cs |
| Tools | Pages/Security/Tools |
| Sharing modal | Pages/Security/Modals/SharedAccess.cshtml and .cshtml.cs |
| External Page | Pages/Shared/Index.cshtml and .cshtml.cs |