Authorization Model¶
Effective permission = role permissions ∩ token scopes
An operator with an ads:read-only token can only read.
Roles¶
Role |
Permissions |
|---|---|
|
Read variables, browse symbols, device info, read files |
|
+ Write variables, upload files |
|
+ Start/stop/reset PLC |
Scopes¶
Scope |
Grants |
|---|---|
|
All read operations |
|
Write operations (variables, files) |
|
PLC lifecycle control |
Policies¶
Policy |
Requirements |
|---|---|
|
|
|
|
|
|
Per-PLC Access Control¶
Add a plc_access claim to the JWT with an array of allowed PLC aliases (see PLC access must be restrict... (REQ_SEC_PLC_ACCESS)):
{
"plc_access": ["Line1", "TestRig"]
}
If
plc_accessis present, the user can only access listed PLCsIf
plc_accessis absent, the user can access no PLC (default deny)plc_access: ["*"]reaches every PLC — how a service account that genuinely needs the whole fleet says soAccessing a denied PLC returns
403withPLC_ACCESS_DENIED
A token the IdP never restricted is not thereby a token authorised for everything. Read the other way round: without default-deny, a compromised token that happens to carry no claim holds unrestricted access to every configured PLC.
The rule is the same everywhere the claim is read — per-PLC routes, the fleet-wide alarm routes, and the MCP tools and resources.
A claim that is present but unreadable (malformed JSON, null) always denies, whatever the
default is. The default governs absence only: an unreadable claim means the IdP meant to
restrict the token and adsify cannot tell how.
Migrating a deployment whose IdP does not issue the claim¶
Breaking change. Before this, an absent plc_access claim granted access to every PLC.
Tokens without the claim now receive 403 PLC_ACCESS_DENIED.
Add a plc_access claim to your tokens — see Keycloak for a
protocol mapper. Until that is in place, a deployment can restore the previous behaviour:
{
"Authorization": {
"PlcAccessDefault": "allow"
}
}
Value |
An absent |
|---|---|
|
The caller reaches no PLC |
|
The caller reaches every configured PLC |
allow logs a warning at every startup. It is a migration aid, not a supported end state.