Skip to content

Acquiring an Agent Access Token

Each onboarded agent is required to get an access token in order to use any services offered by Insights Hub. Insights Hub grants access tokens to clients using OAuth 2.0 authorization framework.

Token Request

Insights Hub receives token requests (HTTP POST) from clients and returns access tokens (HTTP 200 OK). Before granting an access token, the Access Token Service performs the following checks:

  1. The token request contains mandatory and expected headers.
  2. The client is a known client (exists in the database).
  3. The client status is ONBOARDED.

Granting of access tokens is performed by the token endpoint of the Agent Management Service. This is a sample request for a client access token:

POST /api/agentmanagement/v3/oauth/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: localhost: 8061
content-length: 510
Connection: keep-alive
grant_type=client_credentials&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhN2VhYjA5MTEzOTg0OGJiOWU0Mzg5ZDIzZTkzNzhiOCIsInN1YiI6ImE3ZWFiMDkxMTM5ODQ4YmI5ZTQzODlkMjNlOTM3OGI4IiwiYXVkIjpbInNvdXRoZ2F0ZSJdLCJpYXQiOjE1MTM2OTAzNzQsIm5iZiI6MTUxMzY5MDM3NCwiZXhwIjoxNTE0Mjk1MTc0LCJqdGkiOiI1NzIzZDg2NWNlNTMxMzM1Iiwic2NoZW1hcyI6WyJ1cm46c2llbWVuczptaW5kc3BoZXJlOnYxIl0sInRlbiI6ImNvbm50ZW5hbnRvbmVxYSJ9.cli8RKPIUCt7VcP1yGjJvTA8dMR+kdBHotrLDvo4PqXCd1h71jlT3TPSoEAUjbwlm5YTu8RjJwq3cKobq18U7z7AVo+BmSopUIAO/ugyXWHNTpde0V9u8eDMVa3csZrOFwocQVTkI9zehXV81x++fI4hyLF004tUukLNsSglwYiogslhuK1qL8kmdgmTbdAu+ABE68VZe83pyyPJ2iHXZ83oxNElFx/lRZvzQC8B91yRQVO6+kUkDTj9wSr/Mf59DZ1HjpvZn1G0tYt43S4CEjtJSMfseg+kBx+uoqjSYOIVnq5bQYP+UctJRAMtUZJfgInltQGUKXvnqiKo9+RMaeeJTveNvq8EcHHHzeTuEwH3w9cUWGAk8zXax/T6kmTNorMBqQrATG3/UXyhI22x9ji8cY/NCydPVAK8t6+kSDn90znhJ3+S5ubTpx4frv8GG6CbxSaRh5SVJQdg2mG3XJcupxHljj9kQJmnEaP5kepU1LDp5GEHVhfRb0AKpWCG
Parameter Description
grant_type Defines the OAuth 2.0 access token flow requested by the client.
Insights Hub supports only client_credentials.
client_assertion_type Defines the assertion type (assertions are defined in RFC 7521).
Insights Hub supports only the assertion type jwt-bearer urn:ietf:params:oauth:client-assertion-type:jwt-bearer (for details refer to RFC 7523, Section 2.2 "Using JWTs for Client Authentication").
client_assertion Contains the assertion (self-signed JWT) signed (by the client) with client secret or client public key depending on the client's security profile.

The request body parameters grant_type, client_assertion_type, client_assertion body and the headers Host, and Content-Type are mandatory. If one of them is not present, an HTTP response 400 Bad Request is returned. For more information on what was wrong refer to the logs. As imposed by Siemens security rules, no details are provided in the response returned to the agent.

Client requests contain a self-signed JWT, conveyed with the body parameter client_assertion. This client assertion is required to be signed (with client credentials):

  • If a public key is provided to Insights Hub during registration, the client assertion needs to be signed with the private key.
  • If a shared secret is used during registration, the client needs to sign the client assertion with the shared secret.
  • If mqtt broker is used for registration, the client needs to sign the client assertion with the private counterpart of public key residing in device certificate. For more information, refer to Connecting to OPC UA PubSub Service via MQTT.

Below is an example value of the parameter client_assertion:

client_assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhN2VhYjA5MTEzOTg0OGJiOWU0Mzg5ZDIzZTkzNzhiOCIsInN1YiI6ImE3ZWFiMDkxMTM5ODQ4YmI5ZTQzODlkMjNlOTM3OGI4IiwiYXVkIjpbInNvdXRoZ2F0ZSJdLCJpYXQiOjE1MTM2OTAzNzQsIm5iZiI6MTUxMzY5MDM3NCwiZXhwIjoxNTE0Mjk1MTc0LCJqdGkiOiI1NzIzZDg2NWNlNTMxMzM1Iiwic2NoZW1hcyI6WyJ1cm46c2llbWVuczptaW5kc3BoZXJlOnYxIl0sInRlbiI6ImNvbm50ZW5hbnRvbmVxYSJ9.cli8RKPIUCt7VcP1yGjJvTA8dMR+kdBHotrLDvo4PqXCd1h71jlT3TPSoEAUjbwlm5YTu8RjJwq3cKobq18U7z7AVo+BmSopUIAO/ugyXWHNTpde0V9u8eDMVa3csZrOFwocQVTkI9zehXV81x++fI4hyLF004tUukLNsSglwYiogslhuK1qL8kmdgmTbdAu+ABE68VZe83pyyPJ2iHXZ83oxNElFx/lRZvzQC8B91yRQVO6+kUkDTj9wSr/Mf59DZ1HjpvZn1G0tYt43S4CEjtJSMfseg+kBx+uoqjSYOIVnq5bQYP+UctJRAMtUZJfgInltQGUKXvnqiKo9+RMaeeJTveNvq8EcHHHzeTuEwH3w9cUWGAk8zXax/T6kmTNorMBqQrATG3/UXyhI22x9ji8cY/NCydPVAK8t6+kSDn90znhJ3+S5ubTpx4frv8GG6CbxSaRh5SVJQdg2mG3XJcupxHljj9kQJmnEaP5kepU1LDp5GEHVhfRb0AKpWCG

This is the decoded version of the client assertion:

{
  "alg": "RS256",
  "typ": "JWT"
}
{
  "iss": "a7eab091139848bb9e4389d23e9378b8",
  "sub": "a7eab091139848bb9e4389d23e9378b8",
  "aud": [
    "southgate"
  ],
  "iat": 1513690374,
  "nbf": 1513690374,
  "exp": 1514295174,
  "jti": "5723d865ce531335",
  "schemas": [
    "urn:siemens:mindsphere:v1"
  ],
  "ten": "conntenantoneqa"
}

Self-Signed JWT

JWT is a compact claims representation format intended for space constrained environments such as HTTP authorization headers and URI query parameters (RFC-7519). A client creates the JWT and calculates the signature using its credentials (obtained during registration, shared secret or private key). The client JWT is expected to be in following format:

  • BASE64URL(UTF8( Header )) + '.' + BASE64URL( Payload ) + '.' + BASE64URL( Signature ).

The Header must have typ and alg claims. typ claim must be set to JWT. The alg claim must be set based on client's security profile.

  • If client security profile is SHARED_SECRET, alg must be HS256.
  • If client security profile is RSA_3072 alg must be RS256.

A sample header object is presented below:

{
  "typ": "JWT",
  "alg": "RS256"
}
  • If client security profile is CACertifiedX509, the required header must contain the following parameters:

    • alg: RS256 algorithm is mandatory.
    • x5c: Value of that claim must satisfy the following conditions: Please see also X509 Client JWT Requirements.
      • Contains Device Certificate as first certificate in chain
      • Contains TenantCA Certificate
      • Contains maximum 3 certificates
      • Certificates must be in order (eg: [Device Certificate, Device Certificate Issuer, ..])
      • All certificates in the chain must be valid
    • typ: value must be "JWT".

A sample header object is as below:

{
    "alg":"RS256", 
    "x5c": ["MIICYzCCAcygAwIBAgIBADANB..."],
    "typ":"JWT"
}

Payload

The payload must contain iss, sub, ten, schemas, iat, exp, aud, and jti claims. The nbf claim is optional. If there are other claims present than the ones listed, they are ignored by.

  • If client security profile is SHARED_SECRET or RSA_3072, iss and sub must be agent id.
  • If client security profile is CACertifiedX509, iss and sub must be mqtt client id.

The table below describes the claims and their expected values.

Parameter Description Expected Value
iss issuer claim RSA_3072, SHARED_SECRET: agentId. CACertifiedX509: mqtt client id
sub subject claim RSA_3072, SHARED_SECRET: agentId. CACertifiedX509: mqtt client id
ten tenant claim tenantId
schemas schemas claim schemas array [ "urn:siemens:mindsphere:v1" ]
iat issuedAt claim epoch time in seconds when client issued the JWT
nbf not before claim epoch time in seconds, identifies the time before which the JWT MUST NOT be accepted for processing.
exp expires claim epoch time in seconds JWT token will remain valid for
aud audience claim audience, must contain "southgate"
jti json token ID claim JWT ID, a unique identifier

An example body object is provided below:

{
  "iss": "d9341cbff41144abaf690b22e16b87d2",
  "sub": "d9341cbff41144abaf690b22e16b87d2",
  "aud": [
    "southgate"
  ],
  "iat": 1532690970,
  "nbf": 1532690970,
  "exp": 1533295770,
  "jti": "ed35d144e2742f93",
  "schemas": [
    "urn:siemens:mindsphere:v1"
  ],
  "ten": "connint1"
}

Signature

The JWT signature is generated using the signing input. Signing input is calculated using the following algorithm:

  • ASCII(BASE64URL(UTF8( Header )) + '.' + BASE64URL( Payload ))

It is mandatory to provide the alg (algorithm) header parameter in the JOSE Header. Its value must be equal to the algorithm for calculating the JWT signature given above.

JWT must be signed with client secret. Signing keys (client secret) can vary depending on the agent security profile:

  • SHARED_SECRET: JWT is signed with client secret that is provided by /register | /register/<client_id> endpoint
  • RSA_3072: JWT is signed with the private part of the client's RSA key for which the public part was provided at /register | /register/<client_id>
  • CACertifiedX509: JWT is signed with the private counterpart of public key residing in device certificate, which is issued by the CA certificate. This CA certificate will be uploaded by TenantAdmin.

Construction of Self-Signed JWT

After header, payload and signature are created, the client can create the self-signed JWT as follows:

  • BASE64URL(UTF8( Header )) || '.' || BASE64URL( Payload ) || '.' || BASE64URL( Signature )

For additional details, refer to RFC-7515.

Upon receiving a client access token request on the token endpoint, Insights Hub checks the incoming request for the following:

Check Error case
Request contains Host header. Returns an HTTP response 400 Bad Request with the following body:

{"error": "invalid_request"}.
Request contains Content-Type header and content type equals application/x-www-form-urlencoded (case-insensitive). Returns an HTTP response 400 Bad Request with the following body:

{"error": "invalid_request"}
Request contains grant_type body parameter and grant type equals client_credentials (case-insensitive). Returns an HTTP response 400 Bad Request to the agent with the following body:

{"error": "invalid_request"}
Request contains client_assertion_type body parameter and client assertion type equals urn:ietf:params:oauth:client-assertion-type:jwt-bearer (case-insensitive). Returns an HTTP response 400 Bad Request with the following body:

{"error": "invalid_request"}
Request contains client_assertion body parameter and its value is present. Returns an HTTP response 400 Bad Request with the following body:

{"error": "invalid_request"}

Note

Pragma and Cache-Control headers are mandatory in the error response as stated in RFC 6749, Section 5 "Issuing An Access Token".

If all checks above succeed, the request is validated. Insights Hub continues to verify the request parameters:

Check Error case
The method TokenRequestJwtValidatorService.validateJwt is called with the parameter client_assertion.

The method checks, if the provided client assertion is a valid JWT.

The method also checks, if the following JWT payload claims are present and their values are as expected:

iss: Check, if claim is present.
sub: Check, if claim is present.
aud: Check, if claim is present and equal to southgate.
iat: Check, if claim is present and is before or equal to current time.
nbf: Optional claim. Check, if claim is present and is before or equal to current time.
exp: Check, if claim is present and expired time is not before the current time.
schemas: Check, if header is present and equal to urn:siemens:mindsphere:v1 (since sent from client).
If JWT validation fails, an InvalidJwtException is thrown.

A log is generated containing a message explaining why the JWT validation failed.

The exception is handled with an HTTP response 400 Bad Request with the following body:

{"error": "invalid_grant"}
The method TokenRequestJwtValidatorService.checkJwtSignature is called with the client assertion and the agent entity record.

The method checks, if the JWT signature is correct and the stored secret for the agent is not expired.
If the client secret is expired, an AgentSecretExpiredException is thrown and mapped to an HTTP response 400 Bad Request with the following body:

{"error": "invalid_grant"}

If the JWT signature is not correct, an InvalidSignatureException is thrown and mapped to an HTTP response 400 Bad Request with the following body:

{"error": "invalid_client"}
The method AgentEntityRetrievalService.retrieveAgentEntity is called with the client assertion.

The method extracts the sub claim from the client assertion and queries the agent entity database for the related record and retrieves it.
If no agent entity record is found for the extracted sub claim, an AgentNotFound is thrown.

If an agent entity record is found, but the agent entity's onboarded status is not ONBOARDED, an AgentNotOnboardedException is thrown.

Both exceptions are handled with an HTTP response 400 Bad Request with the following body:

{"error": "invalid_client"}
The method AccessTokenService.createAccessToken is called with the agent ID and tenant ID.

The method creates a signed JWT (signed with the private key of the Agent Management) and returns an access token.
If the controller is unable to access its private key, an NoSuchAlgorithmException, IOException or InvalidKeySpecificationException is thrown.

All exceptions are handled with an HTTP response 500 Internal Error.

If all tests are successful, Insights Hub returns an HTTP response 200 OK that holds an access token. The returned access token is a signed JWT, which is valid for 1 hour by default.

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
    "access_token": "eyJraWQiOiJrZXktaWQtMSIsImFsZyI6IlJTMjU2IiwidHlwIjoiSldUIn0.eyJqdGkiOiJjYmIwMGFhZS0yMTU5LTQwNWMtOGZhZC0xOWQ1YWE4ZTQxMWMiLCJzY29wZSI6WyJ0ZW5hbnQtc2NvcGUiLCJtZHNwOmNvcmU6RGVmYXVsdEFnZW50Il0sImlzcyI6Imh0dHBzOi8vc291dGhnYXRlLmV1LWNlbnRyYWwtcmMubWluZHNwaGVyZS5pby9hcGkvYWdlbnRtYW5hZ2VtZW50LyIsInN1YiI6ImE3ZWFiMDkxMTM5ODQ4YmI5ZTQzODlkMjNlOTM3OGI4IiwiemlkIjoiYWdlbnRpYW0iLCJhdWQiOlsic291dGhnYXRlIl0sImlhdCI6MTUxMzY5MDM3NiwiZXhwIjoxNTEzNjkzOTc2LCJzY2hlbWFzIjpbInVybjpzaWVtZW5zOm1pbmRzcGhlcmU6aWFtOnYxIl0sInRlbiI6ImNvbm50ZW5hbnRvbmVxYSIsImNhdCI6ImFnZW50LXRva2VuOnYxIiwiZ3JhbnRfdHlwZSI6ImNsaWVudF9jcmVkZW50aWFscyJ9.LzuA10P8Wy_pGr3s_gCiTaCd82Oc6ts0CJNG5n8MK31Dk5bRDd9hESPP5CUQ0V3pb504HLwBSGL1dg_AF48CdI9Dv8iZ5z667ZCiWptTsfPMdCaLtEWr09S5XA1TZtnLE_hg-v5NeMOmQJOc4xK3aSBmup3Qjw_RFpIeImPHLgCdavp06pdOSIrZ8qPXhi6qBxSAqWOho0DoCAmWO8zp_d55AwdXxeVVudieG9GrQVgGhjQtoIpP8R_pZtJ1nvr6b59nazk1fGsVcGyP-mavQ3Snp9kI7EsT96EBtKLbjuUmsa2bi8ju8LRB0fo_TkIWdnU02vLDfzlx4ftETHm8zg",
    "token_type": "Bearer",
    "expires_in": 3600,
    "scope": [
        "tenant-scope",
        "mdsp:core:DefaultAgent"
    ],
    "jti": "cbb00aae-2159-405c-8fad-19d5aa8e411c"
}
Parameter Description Remarks
access_token A JWT signed by Insights Hub itself With this token, the agent is allowed to consume Industrial IoT services.
token_type Type of token Insights Hub only supports Bearer.
expires_in Validity duration of the token in seconds
scope Scopes for which this token is valid.
jti JWT Token ID

This is the decoded version of the access token:

{
    "kid": "key-id-1",
    "alg": "RS256",
    "typ": "JWT"
}
{
    "jti": "cbb00aae-2159-405c-8fad-19d5aa8e411c",
    "scope": [
        "tenant-scope",
        "mdsp:core:DefaultAgent"
    ],
    "iss": "https://southgate.eu-central-rc.mindsphere.io/api/agentmanagement/",
    "sub": "a7eab091139848bb9e4389d23e9378b8",
    "zid": "agentiam",
    "aud": [
        "southgate"
    ],
    "iat": 1513690376,
    "exp": 1513693976,
    "schemas": [
        "urn:siemens:mindsphere:iam:v1"
    ],
    "ten": "conntenantoneqa",
    "cat": "agent-token:v1",
    "grant_type": "client_credentials"
}

This is the example of JWT after decoding:

  {
    jti": "50e20c46c10c41f4832ad66cb81a7ca7",
    "sub": "training01",
    "scope": [
      "mdsp:core:Admin3rdPartyTechUser"
    ],
    "client_id": "training01",
    "cid": "training01",
    "azp": "training01",
    "grant_type": "client_credentials",
    "rev_sig": "5f2a5fe0",
    "iat": 1585309682,
    "exp": 1585311482,
    "iss": "https://academy2.piam.eu1.mindsphere.io/oauth/token",
    "zid": "academy2",
    "aud": [
      "training01"
    ],
    "ten": "academy2",
    "schemas": [
      "urn:siemens:mindsphere:iam:v1"
    ],
    "cat": "client-token:v1"
  }

Last update: December 1, 2023

Except where otherwise noted, content on this site is licensed under the Development License Agreement.