C8Y Microservice Python User-Session issues

Detailed explanation of the problem:

I currently need to integrate Microservice Role permissions and Device permissions checks.
Since I use the C8Y-Python API, here is an example

import c8y_api.app
import c8y_api.model
from flask import Flask, request, json, jsonify
import requests

print("Start Service")
rest = Flask(__name__)
# initialize cumulocity
c8y = c8y_api.app.MultiTenantCumulocityApp()

print("CumulocityApp initialized.")

@rest.route('/')
def default_route():
    """Default route"""
    user_c8y = c8y.get_user_instance(request.headers)
    return jsonify (user_c8y.users.get_current()), 200

[2024-09-19 12:17:21,125] ERROR in app: Exception on / [GET]
Traceback (most recent call last):
File “/app/main.py”, line 22, in default_route
return jsonify (user_c8y.users.get_current()), 200
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.12/site-packages/c8y_api/model/administration.py”, line 1056, in get_current
user = CurrentUser.from_json(self.c8y.get(‘/user/currentUser’))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/usr/local/lib/python3.12/site-packages/c8y_api/_base_api.py”, line 133, in get
raise ValueError(f"Unable to perform GET request. Status: {r.status_code} Response:\n" + r.text)
ValueError: Unable to perform GET request. Status: 401 Response:
{“error”:“security/Unauthorized”,“message”:“Invalid credentials! : Bad credentials”,“info”:“Getting Started - Cumulocity IoT Guides”}

The Authorization method on our Tenant is OAI-Secure.

Do I need to set special Permissions in the cumulocity.json of the Microservice? Or is there any other good way to test the device Permissions of the user. Against a specivic device?

Should work that way and actually does for me, using OAI-Secure as well.

I changed one line only, not using jsonify:
return user_c8y.users.get_current().to_full_json(), 200

Did you deploy this or ran it locally?

Hi, I deployed it.

I just now made a minimal example. Still no Luck.

Minimal Example.zip (1.9 KB)

custom_microservice.log (2.0 KB)

https://www.cumulocity.com/api/core/#operation/getCurrentUserResource

The roles ROLE_USER_MANAGEMENT_OWN_READ OR ROLE_SYSTEM must be in the manifest of your microservice.

But in this case I use the authentication and permission of the user that made the request to the microservice, or don’t I?

If i would use the bootstrap or a tenant auth, then the manifest permissions should be in use.

I found some interesting behavior, I have two tenants, one testtenant on .eu-latest.cumulocity.com, there everything works as it should even with OAI-Secure, and our management tenant where it does not. Any hints where I could find the problem?

Yes, in your case the user’s roles and permissions are relevant. Access to own profile should be a standard permission but obviously you should verify that.

I personally tested on eu-latest.cumulocity.com as well. I presume the headers come in an unexpected format.
Could you pop in this piece of code:

@rest.route('/')
def default_route():
    """Default route"""
    user_c8y = c8y.get_user_instance(request.headers)
    r = {
        "user": user_c8y.users.get_current().to_full_json(),
        "headers": {k: v.split()[0] for k,v in request.headers}
    }
    return r, 200

and send the output? Please obfuscate personal data you don’t want to share.

eu latest Basic auth

{"headers":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip,","Accept-Language":"de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7","Authorization":"Basic","Cache-Control":"max-age=0","Connection":"close","Cookie":"apt.uid=*obfuscated-cookie*;","Host":"minimal-example-scope-*obfuscated-tenantID*.cumulocity-trial-prod.svc.cluster.local:80","Sec-Ch-Ua":"\"Google","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"Windows\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"none","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0","X-Forwarded-For":"*obfuscated-IP*","X-Forwarded-Host":"*obfuscated-host*.eu-latest.cumulocity.com:444","X-Forwarded-Proto":"https","X-Real-Ip":*obfuscated-IP*,"X-Request-Id":*obfuscated-RequestID*},"user":{"email":*obfuscated-Email*,"enabled":true,"firstName":"L.","lastName":"W.","lastPasswordChange":"2023-07-04T08:16:56.097Z","sendPasswordResetEmail":true,"shouldResetPassword":false,"twoFactorAuthenticationEnabled":false,"userName":*obfuscated-Email*}}

eu latest OAI

{"headers":{"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding":"gzip,","Accept-Language":"de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7","Authorization":"Basic","Connection":"close","Cookie":"apt.uid=*obfuscated-cookie*;","Host":"minimal-example-scope-*obfuscated-tenantID*.cumulocity-trial-prod.svc.cluster.local:80","Sec-Ch-Ua":"\"Google","Sec-Ch-Ua-Mobile":"?0","Sec-Ch-Ua-Platform":"\"Windows\"","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"none","Sec-Fetch-User":"?1","Upgrade-Insecure-Requests":"1","User-Agent":"Mozilla/5.0","X-Forwarded-For":*obfuscated-IP*,"X-Forwarded-Host":"*obfuscated-host*.eu-latest.cumulocity.com:444","X-Forwarded-Proto":"https","X-Real-Ip":*obfuscated-IP*,"X-Request-Id":*obfuscated-RequestID*},"user":{"email":*obfuscated-Email*,"enabled":true,"firstName":"L.","lastName":"W.","lastPasswordChange":"2023-07-04T08:16:56.097Z","sendPasswordResetEmail":true,"shouldResetPassword":false,"twoFactorAuthenticationEnabled":false,"userName":*obfuscated-Email*}}

While Creating the data you wanted I discoverd the following behavior on all my tenants (eu-latest and production):

  1. Scenario:
    Basic Auth, you are logged in to C8Y, otherwise clean cookies/headers, within your browser and change to the URL of the microservice:
    → You get a login Request
    → The service works after that
    → Result: Proper Response with user Data
  2. Scenario:
    OAI Secure, you are logged in to C8Y, otherwise clean cookies/headers, within your browser and change to the URL of the microservice:
    → Result: Error “not Authorized”
  3. Scenario:
    OAI Secure, you are not logged in to C8Y and set the URL of the microservice:
    → You get a login Request
    → The service works after that
    → Result: Proper Response with user Data
  4. Scenario:
    OAI Secure, you are logged in to C8Y, after doing Scenario 3, within your browser and change to the URL of the microservice:
    → Result: Proper Response with user Data

The only real problem seems to be that in Scenario 2 I don’t get prompted to log in for the microservice.

BTW sorry that it took me so long to reply to this, I really appreciate the support!

Lucas,

a interesting problem, just wanted to confirm that I’m looking into this. Currently no update from my end, though.

1 Like

There is a new release, version 2.1.2 of the Python API that should fix it.

1 Like

The behavior didn’t change.
There is just a new Exception.

Traceback (most recent call last):
  File "/usr/local/lib/python3.13/site-packages/flask/app.py", line 1473, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python3.13/site-packages/flask/app.py", line 882, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/usr/local/lib/python3.13/site-packages/flask/app.py", line 880, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.13/site-packages/flask/app.py", line 865, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/app/main.py", line 17, in default_route
    userdata = user_c8y.users.get_current().to_full_json()
               ~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/usr/local/lib/python3.13/site-packages/c8y_api/model/administration.py", line 1056, in get_current
    user = CurrentUser.from_json(self.c8y.get('/user/currentUser'))
                                 ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.13/site-packages/c8y_api/_base_api.py", line 171, in get
    raise UnauthorizedError(self.METHOD_GET, self.base_url + resource)
c8y_api._base_api.UnauthorizedError: ('GET', 'http://cumulocity:8111/user/currentUser')

Might be because of missing permissions.

Does the same microservice work when you directly access it with Basic Auth, e.g. via Postman?

With Basic Auth it works. It seems like the C8Y backend doesn’t accept the usage of the OAI session for a call from the service.

It does, but I had a lot of trouble testing it as well.
When using OAI secure the inbound request needs to have cookies which contain an auth token and a matching XSRF token. I was able to mimic the behaviour by copying the entire request from the Browser’s dev console to Postman.

If you still think there is a bug in the Python API, I’d recommend to report that at Issues · SoftwareAG/cumulocity-python-api (github.com)

https://github.com/Cumulocity-IoT/cumulocity-python-api/issues/61