C8Y Microservice | Python Flask | Authentication issues

Problem Statement

I have tried multiple authentication methos within my Microservice with mixed results.

  • For Subscriptions I can get it to work with Auth in the header (base64 encoded) requests.post(subscription_url, json=subscription_data, headers=headers)

  • For Tokens I can get it to work with Auth directly (basic username, password) requests.post(token_url, json=token_data, auth=(username, password))

  • For Quering the database I can only get it to work with my personal credentials in the header (base64 encoded), 403 with C8Y ENV credentials response = requests.get(url=f"{BASE_URL}/inventory/managedObjects/{id}", headers={"Accept": "application/json","Content-Type": "application/json","Authorization": f"Basic [personal Base64 Credentials]"})

Outcome

I am after:

  • a clear guidance to centralize the auth methods
  • an understanding of the auth architecture and process for microservices
  • links to relevant documentation that clearly states the required auth method for:
    • Subscription
    • Token
    • Standard DB Query

Example Code:

Ideal Auth Method:

tenant = os.getenv('C8Y_TENANT')
user = os.getenv('C8Y_USER')
password = os.getenv('C8Y_PASSWORD')
BASE_URL = "http://cumulocity:8111"
username = f"{tenant}/{user}"
credentials = f"{username}:{password}"
encoded_credentials = base64.b64encode(credentials.encode()).decode()

headers = {
    "Accept": "application/json",
    "Content-Type": "application/json",
    "Authorization": f"Basic {encoded_credentials}",
}

Subscription (Pass)

response = requests.post(subscription_url, json=subscription_data, headers=headers)

Token (Pass)

response = requests.post(token_url, json=token_data, auth=(username, password))

Managed Object

Auth in Header Personal Credentials (Pass)

response = requests.get(url=f"{BASE_URL}/inventory/managedObjects/{id}", headers={"Accept": "application/json","Content-Type": "application/json","Authorization": f"Basic [personal Base64 Credentials]"})

Auth in Header (Fail)

response = requests.get(url=f"{BASE_URL}/inventory/managedObjects/{id}", headers={"Accept": "application/json","Content-Type": "application/json","Authorization": f"Basic encoded_credentials"})

Response (info URL provided is not valid)

{
  "error": "security/Forbidden",
  "message": "Access is denied",
  "info": "https://www.cumulocity.com/guides/reference/rest-implementation//#a-name-error-reporting-a-error-reporting"
}

Auth Directly (Fail)

response = requests.get(url=f"{BASE_URL}/inventory/managedObjects/{id}",auth=(username, password))

Response (info URL provided is not valid)

{
  "error": "security/Forbidden",
  "message": "Access is denied",
  "info": "https://www.cumulocity.com/guides/reference/rest-implementation//#a-name-error-reporting-a-error-reporting"
}

Solved with Roles

cumulocity.json

{
    "apiVersion": "1",
    "version": "0.0.1",
    "provider": {
        "name": "Cumulocity"
    },
    "isolation": "PER_TENANT",
    "requiredRoles": [
        "ROLE_NOTIFICATION_2_ADMIN",
        "ROLE_INVENTORY_ADMIN",
        "ROLE_INVENTORY_CREATE",
        "ROLE_INVENTORY_READ",
        "ROLE_MANAGED_OBJECT_ADMIN",
        "ROLE_MANAGED_OBJECT_CREATE",
        "ROLE_MANAGED_OBJECT_READ",
        "ROLE_MEASUREMENT_ADMIN",
        "ROLE_MEASUREMENT_READ",
        "ROLE_ALARM_ADMIN",
        "ROLE_ALARM_READ",
        "ROLE_DEVICE_CONTROL_READ",
        "ROLE_EVENT_ADMIN",
        "ROLE_EVENT_READ",
        "ROLE_GENERIC_MQTT_ADMIN",
        "ROLE_IDENTITY_READ"
    ],
    "roles": [
    ]
}

To get back to your initial question. PER_TENANT micro services authenticate themselves at Cumulocity Core using credentials injected into the running container. See also General aspects - Cumulocity IoT Guides

It gets more complicated for multi tenant microservices - there you’d have to extract the access credentials from the inbound requests into Flask in your example.

As you are using Python, you may have a look at this framework c8y-api · PyPI which handles all these topics nicely and provides a pythonic interface to the REST API.
Disclaimer: I’m the author of this project.

2 Likes