Authentication issues with kratos
I am currently struggling with getting kratos to work. Requests from flask to kratos's settings endpoint return 'unauthorized' even tho I am passing the session cookie of a user that has been redirected to flask using the recovery link. I mostly followed the docs of the python sdk:
# Configure API key authorization: sessionToken
configuration = ory_kratos_client.Configuration(
host = "http://localhost",
api_key = {
'X-Session-Token': 'YOUR_API_KEY'
}
)
# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed
# configuration.api_key_prefix['X-Session-Token'] = 'Bearer'
# Enter a context with an instance of the API client
with ory_kratos_client.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = ory_kratos_client.PublicApi(api_client)
id = 'id_example' # str | ID is the Settings Flow ID The value for this parameter comes from `flow` URL Query parameter sent to your application (e.g. `/settings?flow=abcde`).
try:
# Get Settings Flow
api_response = api_instance.get_self_service_settings_flow(id)
I implemented it like this:
@kratos_is_ready
def get_settings(self, flow_id, session_key):
## Add user session key to kratos api object
self.kratos_config.api_key = {
"X-Session-Token": session_key
}
self.kratos_config.api_key_prefix["X-Session-Token"] = "Bearer"
kratos = ory_kratos_client.ApiClient(self.kratos_config)
kratos_admin = ory_kratos_client.PublicApi(kratos)
try:
settings_flow_object = kratos_admin.get_self_service_settings_flow(flow_id)
except ApiException as e:
raise BackendConnectionError(e.reason)
except MaxRetryError as error:
raise BackendConnectionError(str(error.reason))
password_method = settings_flow_object.methods["password"]
And I have written a test which expects this:
def test_get_service_variables(httpserver):
kratos_url = httpserver.url_for("")
httpserver.expect_request("/health/ready").respond_with_json({})
httpserver.expect_request(
"/self-service/settings/flows",
query_string=f"id=fake-flow-id",
headers={"X-Session-Token": "Bearer fake-session-key"},
).respond_with_json(
{
"id": "fake-flow-id",
"type": "browser",
"expires_at": "2021-03-17T16:09:37.457574217Z",
"issued_at": "2021-03-17T15:09:37.457574217Z",
"request_url": "http://ignored:4433/self-service/settings/browser",
<---------------- some other lines that are not important here --------------->
}
)
login_flow = LoginFlow(kratos_url)
settings_form_data = login_flow.get_settings(
"fake-flow-id", "fake-session-key"
)
assert settings_form_data.csrf_token == "insecure_csrf_token"
assert settings_form_data.identity == "test@tester.org"
The test passes so everything seems good, right?
testing with the actual server
However the API returns an unauthorized when I try the code against the real server.
using the recovery function i get a link via email, which looks like this:
http://127.0.0.1:4433/self-service/recovery/methods/link?token=M3FV0w95kDIurOGpzW9zK24iLEgDqpoT
just as expected, when i click on the link the kratos endpoint creates a session (cookie is created) and redirects to my /settings endpoint. my settings endpoint then extracts the session cookie and triggers the get_settings
function from above by doing this:
if "ory_kratos_session" in request.cookies:
kratos_session = request.cookies.get("ory_kratos_session")
form_data = kratos.get_settings(
flow_id=flow, session_key=kratos_session
)
I already debugged through the whole python sdk and I can't spot anything obvious. While reading through the code of kratoss sdk I found some things that confuse me.
First this line:
# Uncomment below to setup prefix (e.g. Bearer) for API key, if needed
# configuration.api_key_prefix['X-Session-Token'] = 'Bearer'
Do I need this or not?
Then there is another one in the implementation of Configuration
https://github.com/ory/sdk/blob/master/clients/kratos/python/ory_kratos_client/configuration.py:
:Example:
API Key Authentication Example.
Given the following security scheme in the OpenAPI specification:
components:
securitySchemes:
cookieAuth: # name for the security scheme
type: apiKey
in: cookie
name: JSESSIONID # cookie name
You can programmatically set the cookie:
conf = ory_kratos_client.Configuration(
api_key={'cookieAuth': 'abc123'}
api_key_prefix={'cookieAuth': 'JSESSIONID'}
)
The following cookie will be added to the HTTP request:
Cookie: JSESSIONID abc123
"""
But looking at the code around line 331 we find:
def auth_settings(self):
"""Gets Auth Settings dict for api client.
:return: The Auth Settings information dict.
"""
auth = {}
if 'X-Session-Token' in self.api_key:
auth['sessionToken'] = {
'type': 'api_key',
'in': 'header',
'key': 'X-Session-Token',
'value': self.get_api_key_with_prefix('X-Session-Token')
}
return auth
So i assume X-Session-Token
is the only header that works.
Also i think an exception should be thrown here if authentication is required instead of silenty ignoring other auuth headers.
anyways, cookie auth doesn't seem to be supported anymore. And maybe this is where I am doing something wrong?
the auth cookie which I am using (ory_kratos_session
) looks like this:
I would really appreciate if someone could have a look at this. I have the feeling I am missing something obvious.