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 MindSphere. MindSphere grants access tokens to clients using OAuth 2.0 authorization framework.

Token Request

MindSphere 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 as expected by MindSphere:

1
2
3
4
5
6
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.
MindSphere supports only client_credentials.
client_assertion_type Defines the assertion type (assertions are defined in RFC 7521).
MindSphere 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 MindSphere 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.

Below is an example value of the parameter client_assertion:

1
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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{
  "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). MindSphere expects the client JWT 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:

1
2
3
4
{
  "typ": "JWT",
  "alg": "RS256"
}

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 MindSphere. The table below describes the claims and their expected values.

Parameter Description Expected Value
iss issuer claim agentId
sub subject claim agentId
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 be equal to "southgate"
jti json token ID claim JWT ID, a unique identifier

An example body object is provided below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "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.

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, MindSphere 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. MindSphere 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, MindSphere 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.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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 MindSphere itself With this token, the agent is allowed to consume MindSphere services.
token_type Type of token MindSphere 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:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
    "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"
}

Any questions left?

Ask the community


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