Automated tests with pytest
# support-questions
a
Automated tests with pytest
Hi there, I am doing automated testing with pytest and want to programmatically assign sessions to users, how can i do this? I looked into https://github.com/supertokens/supertokens-python/blob/master/tests/Django/test_django.py#L103 but can't wrap my head around how to reimplement due to a number of helper functions present in each test suite. I am using django.test.Client and in views i user verify_session().
r
Hey @ara.ara unfortunately, there isn’t a a very easy way to do this other than how it’s mentioned in the test case
You need to extract and attach the response cookies yourself
So calling sign in api would attach cookies to the response
Which you can extract. And then attach them to the cookies headers when making an api call that requires a session
a
so, in another words, i should really look under the hood of how things work to do this?
r
Well.. it’s not too much that you need to get into the details of
a
to test how sessions and pemissions work and so on
r
Just extract cookies from@the response and attach them to the subsequent requests
a
Hello again). So, if I understood it correctly, upon login a user receives supertoken session and related roles. Now, to test permissions I need to log in via supertokens and receive its cookie, so that I can attach it further. The question is -> How to login to an existing user with passwordless login? What args should I pass so that i am logged in as specific existing user with related permissions in cookies? currently i do like this, as shown in supertoken tests: ` my_middleware = middleware(create_new_session_view) request_login = async_rf.get('/login', {???}) <- this line temp = my_middleware(request_login) response = await temp cookies = get_cookies(response) return cookies `further i attach this cookies with client.cookies = returned cookies but still have 401 response from view
r
what is the value of
cookies
that you are returning?
a
{'sAccessToken': {'value': 'token', 'name': 'sAccessToken', 'expires': 'Wednesday, May 04, 2022 18:05:17', 'path': '/', 'comment': '', 'domain': 'supertokens.io', 'max-age': '', 'secure': None, 'httponly': True, 'version': '', 'samesite': 'lax'}, 'sRefreshToken': {'value': 'token', 'name': 'sRefreshToken', 'expires': 'Friday, August 12, 2022 17:05:17', 'path': '/api/auth/session/refresh', 'comment': '', 'domain': 'supertokens.io', 'max-age': '', 'secure': None, 'httponly': True, 'version': '', 'samesite': 'lax'}, 'sIdRefreshToken': {'value': 'ae378169-9995-46d4-9b2a-34d86b4290da', 'name': 'sIdRefreshToken', 'expires': 'Friday, August 12, 2022 17:05:17', 'path': '/', 'comment': '', 'domain': 'supertokens.io', 'max-age': '', 'secure': None, 'httponly': True, 'version': '', 'samesite': 'lax'}}
r
right. And how are you attaching these cookies?
a
client.cookies = these cookies
client is django.test.Client
the deal is that these cookies should contain 'roles', as we assign roles to a session in our override_functions
@rp but override_function is not even triggered
r
what are you overriding?
Can I see your code for the override? And which API are you calling and expecting the override function to be called?
a
r
And which API are you calling?
a
you mean inside my override?
r
i mean which API are you calling from the frontend which should call your override?
or which API are you calling from your test in which you are expecting the override function to be called?
a
in tests this is everything I do
middleware, create_new_session_view is from supertoknes tests
r
Can I see the code for /login API?
a
as far as i know login is performed only via supertokens phone auth
passwordless
Copy code
recipe_list=[
            session.init(
                override=session.InputOverrideConfig(functions=override_functions),
                cookie_domain=cookie_domain,
            ),
from init
this is the only place where override_functions is called
or should I explicitly call override_funcitons when signing in in tests?
r
so the tests call /login API. We don't have a /login API. That means, you must have implemented the /login API. Is that true?
a
nope, i don't have login api. login is performed via supertokens only
r
request_login = async_rf.get('/login', {???})
-> this is calling the /login API right? I am very sure we don't such an API
a
ohh , nope, i just copied this line from supertoken tests
r
right yea.. so maybe you should make it query one of the APIs exposed by the SDK's middleware for login
and then the override will get called
a
currently this middleware is used in tests
from supertokens_python.framework.django.django_middleware import middleware
according to docs with passwordless i need to use /auth/signinup/code
with phoneNumber or email as payload
but sill no success
Copy code
from supertokens_python.recipe.session.asyncio import (create_new_session)
from supertokens_python.framework.django.django_middleware import middleware


async def create_new_session_view(request: HttpRequest):
    await create_new_session(request, 'user_id')
    return JsonResponse({'foo': 'bar'})

@pytest.fixture
async def profile_permission(async_rf, client, instances):
    workplace, organization, profile = instances

    my_middleware = middleware(create_new_session_view)

    request_login = async_rf.post('/auth/signinup/code', {'phoneNumber': profile.phone_number})

    temp = my_middleware(request_login)  # A coroutine
    response = await temp

    cookies = get_cookies(response)
    return cookies
r
For passwordless, the first step is to create the code which you are doing by calling
/auth/signinup/code
. The second step is to consume the code for which you need to call
/auth/signinup/code/consume
- which will create a session and call the override you had defined
a
but in response i do not have
preAuthSessionId status devideId flowtype
, just plain cookie with some header
r
That api call should respond with all the necessary info. Cause you make that api call via postman and see the response?
a
i think the problem is somewhere in my code perhaps, though i can't see exactly what is this problem
r
I’m not sure either. Can you print out the value of request_login?
Also, if you can open an issue about this on our GitHub, we can help you with some sample test case which takes you through the whole flow of testing the sign in
a
yep, sure
{'headers': {'Content-Type': 'application/json', 'front-token': 'token', 'Access-Control-Expose-Headers': 'front-token,id-refresh-token,anti-csrf', 'id-refresh-token': 'token', 'anti-csrf': 'token'}, '_charset': None, '_resource_closers': [], '_handler_class': None, 'cookies': , 'closed': False, '_reason_phrase': None, '_container': [b'{"foo": "bar"}']}
token is a generated token, too long to fit, thats why renamed
r
I think you are using the creat_new_session view that’s setting this
Don’t use that.
a
yea, you are right, I use create_new_session here
r
You shouldn’t
That’s probably what’s setting the cookies
a
this is implementation of create_new_session
Copy code
async def create_new_session_view(request: HttpRequest):
    await create_new_session(request, 'user_id')
    return JsonResponse({'foo': 'bar'})
if I should not use it, then what middleware i should use?
r
Just use some empty function like:
Copy code
async def custom_response_view(_: HttpRequest):
    pass
And then do:
Copy code
my_middleware = middleware(custom_response_view)
a
It returns None
r
what is the valuye of
response
?
a
None
@kakashi_44 can help with this..
a
the only difference I see is that i am not implementing
init
inside tests and
start_st()
functions. However, I am using locally running supertokens docker container and think that it should not affect the test. Am I wrong?
r
I don't think it should make a diff. But @kakashi_44 can help
k
Hey @ara.ara , is
async_rf
an object of
RequestFactory
?
a
almost, Async RequestFactory
k
okay, let me try running tests using
AsyncRequestFactory
a
i guess the problem might be in a setup of my postgres and supertoken docker containers
if it is not in the code
k
to check the setup, try calling /hello API. If supertokens core is successfully connected to postgres, it should return
Hello
(success) response
a
it returns success
but maybe it is not properly connecting to db
just guessing
k
while doing
init
, are you passing
mode
as
asgi
?
r
@ara.ara Can you show us your entire test file? It's much easier to debug when we can see all the code..
a
this is all the code related to this test
r
can you show us how you have initial;ised supertokens in the test?
it would be really helpful to see all the code.. otherwise we cannot help
a
i have not init supertokens it tests. I just went along with the init defined in the project
just change env variable to those of my docker
r
can i see that init code?
if you don't want to share code, I understand.
But then you will have to figure out the solution yourself
you can see and understand how we write tests
k
have you done passwordless init? the api route corresponds to the passwordless recipe
a
yep, it passwordless
@kakashi_44 Looks like I am getting an access token. How can I get access token payload in my tests? I want to make sure that my override function is working and correct roles are returned in the access token
@execreate
e
hey everyone! let me give more details on our setup and what we are trying to achieve We are writing python unit tests for our code and we need to figure out a way to login a test user into the system. Here is our override function for user roles:
Copy code
python
from typing import Union, Dict, Any

from asgiref.sync import sync_to_async
from django.apps import apps
from supertokens_python.recipe.session.interfaces import RecipeInterface


@sync_to_async
def get_all_profile_roles(user_id):
    profile_role_model = apps.get_model('profile', 'ProfileRole')
    return list(profile_role_model.objects.filter(profile__supertokens_user_id=user_id))

def override_functions(original_implementation: RecipeInterface):
    original_implementation_create_new_session = original_implementation.create_new_session

    async def create_new_session(request: Any, user_id: str,
                                 access_token_payload: Union[None, Dict[str, Any]],
                                 session_data: Union[None, Dict[str, Any]], user_context: Dict[str, Any]):

        if session_data is None:
            session_data = {}

        if access_token_payload is None:
            access_token_payload = {}

        access_token_payload["roles"] = {}
        for profile_role in await get_all_profile_roles(user_id):
            if profile_role.at not in access_token_payload["roles"]:
                access_token_payload["roles"][profile_role.at] = [profile_role.role]
            else:
                access_token_payload["roles"][profile_role.at].append(profile_role.role)

        return await original_implementation_create_new_session(request, user_id, access_token_payload,
                                                                session_data, user_context)

    original_implementation.create_new_session = create_new_session
    return original_implementation
Here is how we initialize supertokens:
Copy code
python
from supertokens_python import init, InputAppInfo, SupertokensConfig
from supertokens_python.recipe import passwordless, session
from supertokens_python.recipe.passwordless import ContactPhoneOnlyConfig

from apps.mysupertokens.otp_delivery import send_text_message
from apps.mysupertokens.overrides import override_functions

def init_supertokens(site_url,
                     website_domain,
                     website_base_path,
                     connection_uri,
                     api_key):
    # ensure cookie domain is '.example.com' if api is hosted on a subdomain
    cookie_domain = site_url.removeprefix("http://").removeprefix("https://")
    if len(cookie_domain.split(".")) > 2:
        cookie_domain = "." + ".".join(cookie_domain.split(".")[-2:])

    init(
        app_info=InputAppInfo(
            app_name="app",
            api_domain=site_url,
            api_base_path="/auth",
            website_domain=website_domain,
            website_base_path=website_base_path,
        ),
        supertokens_config=SupertokensConfig(
            connection_uri=connection_uri,
            api_key=api_key,
        ),
        framework='django',
        recipe_list=[
            session.init(
                override=session.InputOverrideConfig(functions=override_functions),
                cookie_domain=cookie_domain,
            ),
            passwordless.init(
                flow_type="USER_INPUT_CODE",
                contact_config=ContactPhoneOnlyConfig(
                    create_and_send_custom_text_message=send_text_message
                )
            )
        ],
        mode='wsgi'
    )
If I understand it correctly, @ara.ara is struggling to get the access token payload in the tests to check the roles
r
when you call the /auth/signinup/code POST API call from the test, what is the output?
a
My current tests look like this:
Copy code
python

from django.http import HttpRequest, HttpResponse, JsonResponse
from supertokens_python.framework.django.django_middleware import middleware

async def create_new_session_view(_: HttpRequest):
    pass

@pytest.mark.asyncio
@pytest.fixture
async def profile_permission(async_rf, rf, client, instances):
    workplace, organization, profile = instances

    my_middleware = middleware(create_new_session_view)

    request_login = async_rf.post('/auth/signinup/code', {'phoneNumber': profile.phone_number})

    temp = my_middleware(request_login)  # A coroutine
    response = await temp

    cookies = get_cookies(response)
    return cookies
and the response is still None
r
in your test, you should initialise supertokens
as the first step
or at the top level of this file somewhere
a
aren't they initialized at the project level? init which is inside settings?
r
that is for when the app is running. Not when the tests are running
for tests, you need to initilise it again in the test file
a
Ok, I added init supertokens at the start, but still None response
Copy code
python
async def create_new_session_view(_: HttpRequest):
    pass

@pytest.mark.asyncio
@pytest.fixture
async def profile_permission(async_rf, rf, client, instances):
    workplace, organization, profile = instances

    init_supertokens(SITE_URL,
                     SUPERTOKENS_WEBSITE_DOMAIN,
                     SUPERTOKENS_WEBSITE_BASE_PATH,
                     SUPERTOKENS_CONNECTION_URI,
                     SUPERTOKENS_API_KEY)

    my_middleware = middleware(create_new_session_view)

    request_login = async_rf.post('/auth/signinup/code', {'phoneNumber': profile.phone_number})

    temp = my_middleware(request_login)  # A coroutine
    response = await temp

    cookies = get_cookies(response)
    return cookies
r
does the init have passwordless.init inside it?
r
Can you enable debug logging and then run this test?
Add
SUPERTOKENS_DEBUG=1
env var when you run pytest
a
Copy code
com.supertokens {"t": "2022-05-05T07:30:56.181Z", "sdkVer": "0.6.5", "message": "Started SuperTokens with debug logging (supertokens.init called)", "file": "supertokens.py:178"}

com.supertokens {"t": "2022-05-05T07:30:56.181Z", "sdkVer": "0.6.5", "message": "app_info: {
    "api_base_path": "/api/auth",
    "api_domain": "http://0.0.0.0:8000",
    "api_gateway_path": "",
    "app_name": "app",
    "framework": "django",
    "mode": "asgi",
    "website_base_path": "/auth",
    "website_domain": "http://localhost:3000"
}", "file": "supertokens.py:179"}

com.supertokens {"t": "2022-05-05T07:30:56.181Z", "sdkVer": "0.6.5", "message": "framework: django", "file": "supertokens.py:180"}

com.supertokens {"t": "2022-05-05T07:30:56.182Z", "sdkVer": "0.6.5", "message": "session init: anti_csrf: VIA_TOKEN", "file": "recipe/session/recipe.py:74"}

com.supertokens {"t": "2022-05-05T07:30:56.182Z", "sdkVer": "0.6.5", "message": "session init: cookie_domain: supertokens.io", "file": "recipe/session/recipe.py:76"}

com.supertokens {"t": "2022-05-05T07:30:56.182Z", "sdkVer": "0.6.5", "message": "session init: cookie_same_site: lax", "file": "recipe/session/recipe.py:79"}

com.supertokens {"t": "2022-05-05T07:30:56.182Z", "sdkVer": "0.6.5", "message": "session init: cookie_secure: False", "file": "recipe/session/recipe.py:80"}

com.supertokens {"t": "2022-05-05T07:30:56.182Z", "sdkVer": "0.6.5", "message": "session init: refresh_token_path: /api/auth/session/refresh ", "file": "recipe/session/recipe.py:81"}

com.supertokens {"t": "2022-05-05T07:30:56.182Z", "sdkVer": "0.6.5", "message": "session init: session_expired_status_code: 401", "file": "recipe/session/recipe.py:82"}
Copy code
com.supertokens {"t": "2022-05-05T07:31:04.642Z", "sdkVer": "0.6.5", "message": "middleware: Started", "file": "supertokens.py:339"}

com.supertokens {"t": "2022-05-05T07:31:04.642Z", "sdkVer": "0.6.5", "message": "middleware: Not handling because request path did not start with config path. Request path: /auth/signinup/code", "file": "supertokens.py:347"}

com.supertokens {"t": "2022-05-05T07:31:04.812Z", "sdkVer": "0.6.5", "message": "middleware: Started", "file": "supertokens.py:339"}

com.supertokens {"t": "2022-05-05T07:31:04.812Z", "sdkVer": "0.6.5", "message": "middleware: Not handling because request path did not start with config path. Request path: ", "file": "supertokens.py:347"}

com.supertokens {"t": "2022-05-05T07:31:04.916Z", "sdkVer": "0.6.5", "message": "middleware: Started", "file": "supertokens.py:339"}
Copy code
com.supertokens {"t": "2022-05-05T07:31:04.916Z", "sdkVer": "0.6.5", "message": "middleware: Not handling because request path did not start with config path. Request path: /organizations/9a840be7-ebe8-42f5-bd56-13f16ca9fcd1/workplace/6/staff_on_leave", "file": "supertokens.py:347"}

com.supertokens {"t": "2022-05-05T07:31:04.917Z", "sdkVer": "0.6.5", "message": "getSession: Started", "file": "recipe/session/recipe_implementation.py:134"}

com.supertokens {"t": "2022-05-05T07:31:04.917Z", "sdkVer": "0.6.5", "message": "getSession: rid in header: False", "file": "recipe/session/recipe_implementation.py:138"}

com.supertokens {"t": "2022-05-05T07:31:04.917Z", "sdkVer": "0.6.5", "message": "getSession: request method: GET", "file": "recipe/session/recipe_implementation.py:139"}

com.supertokens {"t": "2022-05-05T07:31:04.917Z", "sdkVer": "0.6.5", "message": "getSession: Value of doAntiCsrfCheck is: False", "file": "recipe/session/recipe_implementation.py:162"}

com.supertokens {"t": "2022-05-05T07:31:04.917Z", "sdkVer": "0.6.5", "message": "errorHandler: Started", "file": "supertokens.py:393"}

com.supertokens {"t": "2022-05-05T07:31:04.917Z", "sdkVer": "0.6.5", "message": "errorHandler: Error is from SuperTokens recipe. Message: invalid jwt", "file": "supertokens.py:394"}

com.supertokens {"t": "2022-05-05T07:31:04.917Z", "sdkVer": "0.6.5", "message": "errorHandler: Checking recipe for match: session", "file": "supertokens.py:403"}

com.supertokens {"t": "2022-05-05T07:31:04.918Z", "sdkVer": "0.6.5", "message": "errorHandler: Matched with recipeID: session", "file": "supertokens.py:406"}

com.supertokens {"t": "2022-05-05T07:31:04.918Z", "sdkVer": "0.6.5", "message": "errorHandler: returning TRY_REFRESH_TOKEN", "file": "recipe/session/recipe.py:152"}

com.supertokens {"t": "2022-05-05T07:31:04.918Z", "sdkVer": "0.6.5", "message": "Sending response to client with status code: 401", "file": "utils.py:111"}
here are supertoken logs from test
r
You are setting the api_base_path to
/api/auth
, but you are querying
/auth/..
. That's why the middleware is not handling the request
a
so i changed that, and the response is still None
r
Can you send the debug logs again for the new code?
a
yea, np
sec
Copy code
com.supertokens {"t": "2022-05-05T07:36:35.772Z", "sdkVer": "0.6.5", "message": "Started SuperTokens with debug logging (supertokens.init called)", "file": "supertokens.py:178"}

com.supertokens {"t": "2022-05-05T07:36:35.772Z", "sdkVer": "0.6.5", "message": "app_info: {
    "api_base_path": "/api/auth",
    "api_domain": "http://0.0.0.0:8000",
    "api_gateway_path": "",
    "app_name": "app",
    "framework": "django",
    "mode": "asgi",
    "website_base_path": "/auth",
    "website_domain": "http://localhost:3000"
}", "file": "supertokens.py:179"}

com.supertokens {"t": "2022-05-05T07:36:35.772Z", "sdkVer": "0.6.5", "message": "framework: django", "file": "supertokens.py:180"}

com.supertokens {"t": "2022-05-05T07:36:35.773Z", "sdkVer": "0.6.5", "message": "session init: anti_csrf: VIA_TOKEN", "file": "recipe/session/recipe.py:74"}

com.supertokens {"t": "2022-05-05T07:36:35.773Z", "sdkVer": "0.6.5", "message": "session init: cookie_domain: supertokens.io", "file": "recipe/session/recipe.py:76"}

com.supertokens {"t": "2022-05-05T07:36:35.773Z", "sdkVer": "0.6.5", "message": "session init: cookie_same_site: lax", "file": "recipe/session/recipe.py:79"}

com.supertokens {"t": "2022-05-05T07:36:35.773Z", "sdkVer": "0.6.5", "message": "session init: cookie_secure: False", "file": "recipe/session/recipe.py:80"}

com.supertokens {"t": "2022-05-05T07:36:35.773Z", "sdkVer": "0.6.5", "message": "session init: refresh_token_path: /api/auth/session/refresh ", "file": "recipe/session/recipe.py:81"}

com.supertokens {"t": "2022-05-05T07:36:35.773Z", "sdkVer": "0.6.5", "message": "session init: session_expired_status_code: 401", "file": "recipe/session/recipe.py:82"}
Copy code
com.supertokens {"t": "2022-05-05T07:36:44.190Z", "sdkVer": "0.6.5", "message": "middleware: Started", "file": "supertokens.py:339"}

com.supertokens {"t": "2022-05-05T07:36:44.190Z", "sdkVer": "0.6.5", "message": "middleware: requestRID is: None", "file": "supertokens.py:352"}

com.supertokens {"t": "2022-05-05T07:36:44.190Z", "sdkVer": "0.6.5", "message": "middleware: Checking recipe ID for match: session", "file": "supertokens.py:370"}

com.supertokens {"t": "2022-05-05T07:36:44.190Z", "sdkVer": "0.6.5", "message": "middleware: Not handling because no recipe matched", "file": "supertokens.py:379"}

response output in test -  None
com.supertokens {"t": "2022-05-05T07:36:44.362Z", "sdkVer": "0.6.5", "message": "middleware: Started", "file": "supertokens.py:339"}

com.supertokens {"t": "2022-05-05T07:36:44.362Z", "sdkVer": "0.6.5", "message": "middleware: Not handling because request path did not start with config path. Request path: ", "file": "supertokens.py:347"}

com.supertokens {"t": "2022-05-05T07:36:44.466Z", "sdkVer": "0.6.5", "message": "middleware: Started", "file": "supertokens.py:339"}

com.supertokens {"t": "2022-05-05T07:36:44.466Z", "sdkVer": "0.6.5", "message": "middleware: Not handling because request path did not start with config path. Request path: /organizations/9dd33b6a-f91d-4800-9686-5c235bff14c9/workplace/15/staff_on_leave", "file": "supertokens.py:347"}

com.supertokens {"t": "2022-05-05T07:36:44.467Z", "sdkVer": "0.6.5", "message": "getSession: Started", "file": "recipe/session/recipe_implementation.py:134"}

com.supertokens {"t": "2022-05-05T07:36:44.467Z", "sdkVer": "0.6.5", "message": "getSession: rid in header: False", "file": "recipe/session/recipe_implementation.py:138"}
Copy code
com.supertokens {"t": "2022-05-05T07:36:44.467Z", "sdkVer": "0.6.5", "message": "getSession: request method: GET", "file": "recipe/session/recipe_implementation.py:139"}

com.supertokens {"t": "2022-05-05T07:36:44.467Z", "sdkVer": "0.6.5", "message": "getSession: Value of doAntiCsrfCheck is: False", "file": "recipe/session/recipe_implementation.py:162"}

com.supertokens {"t": "2022-05-05T07:36:44.468Z", "sdkVer": "0.6.5", "message": "errorHandler: Started", "file": "supertokens.py:393"}

com.supertokens {"t": "2022-05-05T07:36:44.468Z", "sdkVer": "0.6.5", "message": "errorHandler: Error is from SuperTokens recipe. Message: invalid jwt", "file": "supertokens.py:394"}

com.supertokens {"t": "2022-05-05T07:36:44.468Z", "sdkVer": "0.6.5", "message": "errorHandler: Checking recipe for match: session", "file": "supertokens.py:403"}

com.supertokens {"t": "2022-05-05T07:36:44.468Z", "sdkVer": "0.6.5", "message": "errorHandler: Matched with recipeID: session", "file": "supertokens.py:406"}

com.supertokens {"t": "2022-05-05T07:36:44.469Z", "sdkVer": "0.6.5", "message": "errorHandler: returning TRY_REFRESH_TOKEN", "file": "recipe/session/recipe.py:152"}

com.supertokens {"t": "2022-05-05T07:36:44.469Z", "sdkVer": "0.6.5", "message": "Sending response to client with status code: 401", "file": "utils.py:111"}
r
it seems like you are not doing passwordless.init in the init function
a
one sec, i changed a bit init
Copy code
com.supertokens {"t": "2022-05-05T07:41:32.992Z", "sdkVer": "0.6.5", "message": "Started SuperTokens with debug logging (supertokens.init called)", "file": "supertokens.py:178"}

com.supertokens {"t": "2022-05-05T07:41:32.992Z", "sdkVer": "0.6.5", "message": "app_info: {
    "api_base_path": "/auth",
    "api_domain": "http://0.0.0.0:8000",
    "api_gateway_path": "",
    "app_name": "medhub",
    "framework": "django",
    "mode": "wsgi",
    "website_base_path": "",
    "website_domain": "http://localhost:3000"
}", "file": "supertokens.py:179"}

com.supertokens {"t": "2022-05-05T07:41:32.992Z", "sdkVer": "0.6.5", "message": "framework: django", "file": "supertokens.py:180"}

com.supertokens {"t": "2022-05-05T07:41:32.993Z", "sdkVer": "0.6.5", "message": "session init: anti_csrf: NONE", "file": "recipe/session/recipe.py:74"}

com.supertokens {"t": "2022-05-05T07:41:32.993Z", "sdkVer": "0.6.5", "message": "session init: cookie_domain: 0.0", "file": "recipe/session/recipe.py:76"}

com.supertokens {"t": "2022-05-05T07:41:32.993Z", "sdkVer": "0.6.5", "message": "session init: cookie_same_site: lax", "file": "recipe/session/recipe.py:79"}

com.supertokens {"t": "2022-05-05T07:41:32.993Z", "sdkVer": "0.6.5", "message": "session init: cookie_secure: False", "file": "recipe/session/recipe.py:80"}

com.supertokens {"t": "2022-05-05T07:41:32.993Z", "sdkVer": "0.6.5", "message": "session init: refresh_token_path: /auth/session/refresh ", "file": "recipe/session/recipe.py:81"}

com.supertokens {"t": "2022-05-05T07:41:32.993Z", "sdkVer": "0.6.5", "message": "session init: session_expired_status_code: 401", "file": "recipe/session/recipe.py:82"}
Copy code
com.supertokens {"t": "2022-05-05T07:41:42.097Z", "sdkVer": "0.6.5", "message": "middleware: Started", "file": "supertokens.py:339"}

com.supertokens {"t": "2022-05-05T07:41:42.098Z", "sdkVer": "0.6.5", "message": "middleware: Not handling because request path did not start with config path. Request path: /api/auth/signinup/code", "file": "supertokens.py:347"}

response output in test -  None
com.supertokens {"t": "2022-05-05T07:41:42.278Z", "sdkVer": "0.6.5", "message": "middleware: Started", "file": "supertokens.py:339"}

com.supertokens {"t": "2022-05-05T07:41:42.278Z", "sdkVer": "0.6.5", "message": "middleware: Not handling because request path did not start with config path. Request path: ", "file": "supertokens.py:347"}

com.supertokens {"t": "2022-05-05T07:41:42.345Z", "sdkVer": "0.6.5", "message": "middleware: Started", "file": "supertokens.py:339"}

com.supertokens {"t": "2022-05-05T07:41:42.345Z", "sdkVer": "0.6.5", "message": "middleware: Not handling because request path did not start with config path. Request path: /organizations/c411f5b8-6fab-4c1e-a0a0-b55fa3c6b8c7/workplace/21/staff_on_leave", "file": "supertokens.py:347"}

com.supertokens {"t": "2022-05-05T07:41:42.346Z", "sdkVer": "0.6.5", "message": "getSession: Started", "file": "recipe/session/recipe_implementation.py:134"}

com.supertokens {"t": "2022-05-05T07:41:42.346Z", "sdkVer": "0.6.5", "message": "getSession: rid in header: False", "file": "recipe/session/recipe_implementation.py:138"}

com.supertokens {"t": "2022-05-05T07:41:42.346Z", "sdkVer": "0.6.5", "message": "getSession: request method: GET", "file": "recipe/session/recipe_implementation.py:139"}

com.supertokens {"t": "2022-05-05T07:41:42.346Z", "sdkVer": "0.6.5", "message": "getSession: Value of doAntiCsrfCheck is: False", "file": "recipe/session/recipe_implementation.py:162"}
Copy code
com.supertokens {"t": "2022-05-05T07:41:42.346Z", "sdkVer": "0.6.5", "message": "errorHandler: Started", "file": "supertokens.py:393"}

com.supertokens {"t": "2022-05-05T07:41:42.347Z", "sdkVer": "0.6.5", "message": "errorHandler: Error is from SuperTokens recipe. Message: invalid jwt", "file": "supertokens.py:394"}

com.supertokens {"t": "2022-05-05T07:41:42.347Z", "sdkVer": "0.6.5", "message": "errorHandler: Checking recipe for match: session", "file": "supertokens.py:403"}

com.supertokens {"t": "2022-05-05T07:41:42.347Z", "sdkVer": "0.6.5", "message": "errorHandler: Matched with recipeID: session", "file": "supertokens.py:406"}

com.supertokens {"t": "2022-05-05T07:41:42.347Z", "sdkVer": "0.6.5", "message": "errorHandler: returning TRY_REFRESH_TOKEN", "file": "recipe/session/recipe.py:152"}

com.supertokens {"t": "2022-05-05T07:41:42.347Z", "sdkVer": "0.6.5", "message": "Sending response to client with status code: 401", "file": "utils.py:111"}
So, after I changed init the base path went back to
/auth
r
so now you should query /auth/signinup/code
a
and the response turned from
None
to bad request
<HttpResponse status_code=400, "application/json; charset=utf-8"> {'headers': {'Content-Type': 'application/json; charset=utf-8'}, '_charset': None, '_resource_closers': [], '_handler_class': None, 'cookies': <SimpleCookie: >, 'closed': False, '_reason_phrase': None, '_container': [b'{"message":"Please provide exactly one of email or phoneNumber"}'], 'status_code': 400}
r
right! at least now the API is getting called
a
yeah)
r
You might want to add content-type: application/json in your post request
a
do i need to send new logs with this error message?
r
no i think if you set the correct content-type header in your request, it will work
a
status":"GENERAL_ERROR","message":"Phone number is invalid"
this is because generated phone number is random
if i pass in legit phone number will it send message to that phone?
r
Depends on what you have implemented in
send_text_message
function
a
is there a way to avoid this?
r
when you initialise it in the test, you can always just make
send_text_message
into an empty function.
a
@rp where do I get linkCode for POST /auth/signinup/code/consume?
if I understood it correctly, after
POST /auth/signiniup/code
i should
POST /auth/signinup/code/consume
to trigger
override functions
from here https://discord.com/channels/603466164219281420/969488117474132038/971666992224415784 and get a user role in cookies
at the moment from my
POST /auth/signinup/code
i receive this output {'headers': {'Content-Type': 'application/json; charset=utf-8'}, '_charset': None, '_resource_closers': [], '_handler_class': None, 'cookies': , 'closed': False, '_reason_phrase': None, '_container': [b'{"status":"OK","deviceId":"eQtggkr2pDmaPeCNBKOlGD+lMUitynJ7NLh4Knm0Cu8=","preAuthSessionId":"HhC4t0sQNUrWeOXGk0SIjFUtG4qXv-WIwvHL5H07JN4=","flowType":"USER_INPUT_CODE"}'], 'status_code': 200} note that cookies are empty at this point
r
yes
> note that cookies are empty at this point This is expected.
a
ohh, ok, thats good
so, where do i get
linkCode
required in consume API?
r
It's sent to the
send_text_message
function.
a
so, what type of format should I use to pass in OTP, it is not shown in swagger docs? currently i pass in like this -->
Copy code
python
request_login = async_rf.post('/auth/signinup/code/consume',
                             {'preAuthSessionId': pre_auth_session_id,
                              'linkCode': "735o/fUDXS/+zwSL5qsT9klwKxRCtoNsUdnNc7GExDM=+772686"},
                               content_type='application/json')
where
772686
is OTP sent to send_text_message
last logs before an error
Copy code
com.supertokens {"t": "2022-05-05T14:02:49.649Z", "sdkVer": "0.6.5", "message": "middleware: Started", "file": "supertokens.py:339"}

com.supertokens {"t": "2022-05-05T14:02:49.650Z", "sdkVer": "0.6.5", "message": "middleware: requestRID is: None", "file": "supertokens.py:352"}

com.supertokens {"t": "2022-05-05T14:02:49.650Z", "sdkVer": "0.6.5", "message": "middleware: Checking recipe ID for match: session", "file": "supertokens.py:370"}

com.supertokens {"t": "2022-05-05T14:02:49.650Z", "sdkVer": "0.6.5", "message": "middleware: Checking recipe ID for match: passwordless", "file": "supertokens.py:370"}

com.supertokens {"t": "2022-05-05T14:02:49.650Z", "sdkVer": "0.6.5", "message": "middleware: Matched with recipe ID: passwordless", "file": "supertokens.py:377"}

com.supertokens {"t": "2022-05-05T14:02:49.650Z", "sdkVer": "0.6.5", "message": "middleware: Request being handled by recipe. ID is: /signinup/code/consume", "file": "supertokens.py:383"}

com.supertokens {"t": "2022-05-05T14:02:49.662Z", "sdkVer": "0.6.5", "message": "errorHandler: Started", "file": "supertokens.py:393"}

com.supertokens {"t": "2022-05-05T14:02:49.663Z", "sdkVer": "0.6.5", "message": "errorHandler: Error is from SuperTokens recipe. Message: SuperTokens core threw an error for a POST request to path: /recipe/signinup/code/consume with status code: 400 and message: Input encoding error in linkCode
", "file": "supertokens.py:394"}
r
for OTP, you should not pass linkCode to the consume API
a
'Please provide one of (linkCode) or (deviceId+userInputCode) and not both'
r
Pass deviceId and userInputCode
where userInputCode is the OTP
a
ohh, sorry, i misunderstood due to this error message)
r
right ok
a
i think it finally worked) huge thanks
r
awesome!!!
a
now I only need to figure out how to test my views with those cookies, using request factory or django's test Client)
Now, as I am trying to test the view with django.test.Client I still receive 401.
Copy code
python
async def create_new_session_view(_: HttpRequest):
    pass

async def profile_permission(admin_client, async_rf, rf, client, instances):

    # Unrelated code here

    # Finish sign in/up process with passwordless
    my_middleware = middleware(create_new_session_view)

    request_login = async_rf.post('/auth/signinup/code/consume',
                                  {'preAuthSessionId': pre_auth_session_id,
                                   'deviceId': device_id,
                                   'userInputCode': otp},
                                  content_type='application/json')

    temp = my_middleware(request_login)  # A coroutine
    response = await temp
    cookies = get_cookies(response) # get_cookies() is from Supertokens tests
    return cookies


def test_permission(profile_permission, admin_client, instances):
    workplace, organization, profile = instances

    admin_client.session.cookies = profile_permission
    response = admin_client.get(reverse('staff-on-leave-list', kwargs={'organization_pk':                                                    organization.id, 'workplace_pk': workplace.id}))

    print(response.status_code)
As i use pytest,
profile_permission
is initialized automatically as it is a pytest fixture.
I retrieve cookies from the
POST /auth/signinup/code/consume
request with get_cookies function I got from Supertoken tests
and attach it to
django.test.Client
which is
admin_client
in our case
Copy code
com.supertokens {"t": "2022-05-06T07:13:35.652Z", "sdkVer": "0.6.5", "message": "middleware: Started", "file": "supertokens.py:339"}

com.supertokens {"t": "2022-05-06T07:13:35.652Z", "sdkVer": "0.6.5", "message": "middleware: Not handling because request path did not start with config path. Request path: /organizations/926c4daf-0242-4148-96b3-9c749ca51048/workplace/9/staff_on_leave", "file": "supertokens.py:347"}

com.supertokens {"t": "2022-05-06T07:13:35.653Z", "sdkVer": "0.6.5", "message": "getSession: Started", "file": "recipe/session/recipe_implementation.py:134"}

com.supertokens {"t": "2022-05-06T07:13:35.653Z", "sdkVer": "0.6.5", "message": "getSession: rid in header: False", "file": "recipe/session/recipe_implementation.py:138"}

com.supertokens {"t": "2022-05-06T07:13:35.653Z", "sdkVer": "0.6.5", "message": "getSession: request method: GET", "file": "recipe/session/recipe_implementation.py:139"}

com.supertokens {"t": "2022-05-06T07:13:35.654Z", "sdkVer": "0.6.5", "message": "getSession: Value of doAntiCsrfCheck is: False", "file": "recipe/session/recipe_implementation.py:162"}

com.supertokens {"t": "2022-05-06T07:13:35.654Z", "sdkVer": "0.6.5", "message": "errorHandler: Started", "file": "supertokens.py:393"}
Copy code
com.supertokens {"t": "2022-05-06T07:13:35.654Z", "sdkVer": "0.6.5", "message": "errorHandler: Error is from SuperTokens recipe. Message: invalid jwt", "file": "supertokens.py:394"}

com.supertokens {"t": "2022-05-06T07:13:35.654Z", "sdkVer": "0.6.5", "message": "errorHandler: Checking recipe for match: session", "file": "supertokens.py:403"}

com.supertokens {"t": "2022-05-06T07:13:35.654Z", "sdkVer": "0.6.5", "message": "errorHandler: Matched with recipeID: session", "file": "supertokens.py:406"}

com.supertokens {"t": "2022-05-06T07:13:35.654Z", "sdkVer": "0.6.5", "message": "errorHandler: returning TRY_REFRESH_TOKEN", "file": "recipe/session/recipe.py:152"}

com.supertokens {"t": "2022-05-06T07:13:35.655Z", "sdkVer": "0.6.5", "message": "Sending response to client with status code: 401", "file": "utils.py:111"}
These are the latest logs
r
Can you print out the value of
admin_client.session.cookies
?
a
hmm, as i am trying to print admin_client.session.cookies it raises an error
AttributeError: 'SessionStore' object has no attribute 'cookies'
if i set admin_client.cookies and try to print it it raises
AttributeError: 'dict' object has no attribute 'key'
r
Can you do
pip install git+https://github.com/supertokens/supertokens-python.git@8133d969683d243eb9aeb65102bc45b8a2045df4
in your project and run the test with that?
and then show me the new debug logs
a
ok, will try that
r
(edited the message above. Please see new pip command)
a
should i just run my py tests?
i think the debug logs are the same
r
Can you send the debug logs please?
i only added one new log.. so it may easy to miss out
a
Copy code
DEBUG    com.supertokens:supertokens.py:339 {"t": "2022-05-06T07:46:44.379Z", "sdkVer": "0.6.5", "message": "middleware: Started", "file": "supertokens.py:339"}
DEBUG    com.supertokens:supertokens.py:347 {"t": "2022-05-06T07:46:44.380Z", "sdkVer": "0.6.5", "message": "middleware: Not handling because request path did not start with config path. Request path: ", "file": "supertokens.py:347"}
DEBUG    com.supertokens:supertokens.py:339 {"t": "2022-05-06T07:46:44.424Z", "sdkVer": "0.6.5", "message": "middleware: Started", "file": "supertokens.py:339"}
DEBUG    com.supertokens:supertokens.py:352 {"t": "2022-05-06T07:46:44.424Z", "sdkVer": "0.6.5", "message": "middleware: requestRID is: None", "file": "supertokens.py:352"}
DEBUG    com.supertokens:supertokens.py:370 {"t": "2022-05-06T07:46:44.424Z", "sdkVer": "0.6.5", "message": "middleware: Checking recipe ID for match: session", "file": "supertokens.py:370"}
DEBUG    com.supertokens:supertokens.py:370 {"t": "2022-05-06T07:46:44.424Z", "sdkVer": "0.6.5", "message": "middleware: Checking recipe ID for match: passwordless", "file": "supertokens.py:370"}
DEBUG    com.supertokens:supertokens.py:377 {"t": "2022-05-06T07:46:44.425Z", "sdkVer": "0.6.5", "message": "middleware: Matched with recipe ID: passwordless", "file": "supertokens.py:377"}
DEBUG    com.supertokens:supertokens.py:383 {"t": "2022-05-06T07:46:44.425Z", "sdkVer": "0.6.5", "message": "middleware: Request being handled by recipe. ID is: /signinup/code", "file": "supertokens.py:383"}
DEBUG    com.supertokens:utils.py:121 {"t": "2022-05-06T07:46:44.460Z", "sdkVer": "0.6.5", "message": "Sending response to client with status code: 200", "file": "utils.py:121"}
r
you are still running the older vertsion of the SDK
please make sure that you are running the version from the pip install
it should say sdkVer: 0.7.0
a
ok
Copy code
DEBUG    com.supertokens:supertokens.py:339 {"t": "2022-05-06T07:53:34.670Z", "sdkVer": "0.7.0", "message": "middleware: Started", "file": "supertokens.py:339"}
DEBUG    com.supertokens:supertokens.py:347 {"t": "2022-05-06T07:53:34.671Z", "sdkVer": "0.7.0", "message": "middleware: Not handling because request path did not start with config path. Request path: ", "file": "supertokens.py:347"}
DEBUG    com.supertokens:supertokens.py:339 {"t": "2022-05-06T07:53:34.708Z", "sdkVer": "0.7.0", "message": "middleware: Started", "file": "supertokens.py:339"}
DEBUG    com.supertokens:supertokens.py:352 {"t": "2022-05-06T07:53:34.708Z", "sdkVer": "0.7.0", "message": "middleware: requestRID is: None", "file": "supertokens.py:352"}
DEBUG    com.supertokens:supertokens.py:370 {"t": "2022-05-06T07:53:34.708Z", "sdkVer": "0.7.0", "message": "middleware: Checking recipe ID for match: session", "file": "supertokens.py:370"}
DEBUG    com.supertokens:supertokens.py:370 {"t": "2022-05-06T07:53:34.708Z", "sdkVer": "0.7.0", "message": "middleware: Checking recipe ID for match: passwordless", "file": "supertokens.py:370"}
DEBUG    com.supertokens:supertokens.py:377 {"t": "2022-05-06T07:53:34.708Z", "sdkVer": "0.7.0", "message": "middleware: Matched with recipe ID: passwordless", "file": "supertokens.py:377"}
DEBUG    com.supertokens:supertokens.py:383 {"t": "2022-05-06T07:53:34.708Z", "sdkVer": "0.7.0", "message": "middleware: Request being handled by recipe. ID is: /signinup/code", "file": "supertokens.py:383"}
DEBUG    com.supertokens:utils.py:126 {"t": "2022-05-06T07:53:34.743Z", "sdkVer": "0.7.0", "message": "Sending response to client with status code: 200", "file": "utils.py:126"}
DEBUG    com.supertokens:supertokens.py:388 {"t": "2022-05-06T07:53:34.743Z", "sdkVer": "0.7.0", "message": "middleware: Ended", "file": "supertokens.py:388"}
Copy code
DEBUG    com.supertokens:supertokens.py:339 {"t": "2022-05-06T07:53:34.743Z", "sdkVer": "0.7.0", "message": "middleware: Started", "file": "supertokens.py:339"}
DEBUG    com.supertokens:supertokens.py:352 {"t": "2022-05-06T07:53:34.743Z", "sdkVer": "0.7.0", "message": "middleware: requestRID is: None", "file": "supertokens.py:352"}
DEBUG    com.supertokens:supertokens.py:370 {"t": "2022-05-06T07:53:34.744Z", "sdkVer": "0.7.0", "message": "middleware: Checking recipe ID for match: session", "file": "supertokens.py:370"}
DEBUG    com.supertokens:supertokens.py:370 {"t": "2022-05-06T07:53:34.744Z", "sdkVer": "0.7.0", "message": "middleware: Checking recipe ID for match: passwordless", "file": "supertokens.py:370"}
DEBUG    com.supertokens:supertokens.py:377 {"t": "2022-05-06T07:53:34.744Z", "sdkVer": "0.7.0", "message": "middleware: Matched with recipe ID: passwordless", "file": "supertokens.py:377"}
DEBUG    com.supertokens:supertokens.py:383 {"t": "2022-05-06T07:53:34.744Z", "sdkVer": "0.7.0", "message": "middleware: Request being handled by recipe. ID is: /signinup/code/consume", "file": "supertokens.py:383"}
DEBUG    com.supertokens:utils.py:126 {"t": "2022-05-06T07:53:34.794Z", "sdkVer": "0.7.0", "message": "Sending response to client with status code: 200", "file": "utils.py:126"}
DEBUG    com.supertokens:supertokens.py:388 {"t": "2022-05-06T07:53:34.794Z", "sdkVer": "0.7.0", "message": "middleware: Ended", "file": "supertokens.py:388"}
r
there should be more logs
a
i send only the tail
ill send full now
Copy code
com.supertokens {"t": "2022-05-06T07:53:24.602Z", "sdkVer": "0.7.0", "message": "Started SuperTokens with debug logging (supertokens.init called)", "file": "supertokens.py:178"}

com.supertokens {"t": "2022-05-06T07:53:24.602Z", "sdkVer": "0.7.0", "message": "app_info: {
    "api_base_path": "/auth",
    "api_domain": "http://0.0.0.0:8000",
    "api_gateway_path": "",
    "app_name": "medhub",
    "framework": "django",
    "mode": "wsgi",
    "website_base_path": "",
    "website_domain": "http://localhost:3000"
}", "file": "supertokens.py:179"}

com.supertokens {"t": "2022-05-06T07:53:24.602Z", "sdkVer": "0.7.0", "message": "framework: django", "file": "supertokens.py:180"}

com.supertokens {"t": "2022-05-06T07:53:24.602Z", "sdkVer": "0.7.0", "message": "session init: anti_csrf: NONE", "file": "recipe/session/recipe.py:74"}

com.supertokens {"t": "2022-05-06T07:53:24.603Z", "sdkVer": "0.7.0", "message": "session init: cookie_domain: 0.0", "file": "recipe/session/recipe.py:76"}

com.supertokens {"t": "2022-05-06T07:53:24.603Z", "sdkVer": "0.7.0", "message": "session init: cookie_same_site: lax", "file": "recipe/session/recipe.py:79"}

com.supertokens {"t": "2022-05-06T07:53:24.603Z", "sdkVer": "0.7.0", "message": "session init: cookie_secure: False", "file": "recipe/session/recipe.py:80"}

com.supertokens {"t": "2022-05-06T07:53:24.603Z", "sdkVer": "0.7.0", "message": "session init: refresh_token_path: /auth/session/refresh ", "file": "recipe/session/recipe.py:81"}

com.supertokens {"t": "2022-05-06T07:53:24.603Z", "sdkVer": "0.7.0", "message": "session init: session_expired_status_code: 401", "file": "recipe/session/recipe.py:82"}
Copy code
com.supertokens {"t": "2022-05-06T07:53:34.670Z", "sdkVer": "0.7.0", "message": "middleware: Started", "file": "supertokens.py:339"}

com.supertokens {"t": "2022-05-06T07:53:34.671Z", "sdkVer": "0.7.0", "message": "middleware: Not handling because request path did not start with config path. Request path: ", "file": "supertokens.py:347"}

com.supertokens {"t": "2022-05-06T07:53:34.708Z", "sdkVer": "0.7.0", "message": "middleware: Started", "file": "supertokens.py:339"}

com.supertokens {"t": "2022-05-06T07:53:34.708Z", "sdkVer": "0.7.0", "message": "middleware: requestRID is: None", "file": "supertokens.py:352"}

com.supertokens {"t": "2022-05-06T07:53:34.708Z", "sdkVer": "0.7.0", "message": "middleware: Checking recipe ID for match: session", "file": "supertokens.py:370"}

com.supertokens {"t": "2022-05-06T07:53:34.708Z", "sdkVer": "0.7.0", "message": "middleware: Checking recipe ID for match: passwordless", "file": "supertokens.py:370"}

com.supertokens {"t": "2022-05-06T07:53:34.708Z", "sdkVer": "0.7.0", "message": "middleware: Matched with recipe ID: passwordless", "file": "supertokens.py:377"}

com.supertokens {"t": "2022-05-06T07:53:34.708Z", "sdkVer": "0.7.0", "message": "middleware: Request being handled by recipe. ID is: /signinup/code", "file": "supertokens.py:383"}

com.supertokens {"t": "2022-05-06T07:53:34.743Z", "sdkVer": "0.7.0", "message": "Sending response to client with status code: 200", "file": "utils.py:126"}

com.supertokens {"t": "2022-05-06T07:53:34.743Z", "sdkVer": "0.7.0", "message": "middleware: Ended", "file": "supertokens.py:388"}

com.supertokens {"t": "2022-05-06T07:53:34.743Z", "sdkVer": "0.7.0", "message": "middleware: Started", "file": "supertokens.py:339"}
Copy code
com.supertokens {"t": "2022-05-06T07:53:34.743Z", "sdkVer": "0.7.0", "message": "middleware: requestRID is: None", "file": "supertokens.py:352"}

com.supertokens {"t": "2022-05-06T07:53:34.744Z", "sdkVer": "0.7.0", "message": "middleware: Checking recipe ID for match: session", "file": "supertokens.py:370"}

com.supertokens {"t": "2022-05-06T07:53:34.744Z", "sdkVer": "0.7.0", "message": "middleware: Checking recipe ID for match: passwordless", "file": "supertokens.py:370"}

com.supertokens {"t": "2022-05-06T07:53:34.744Z", "sdkVer": "0.7.0", "message": "middleware: Matched with recipe ID: passwordless", "file": "supertokens.py:377"}

com.supertokens {"t": "2022-05-06T07:53:34.744Z", "sdkVer": "0.7.0", "message": "middleware: Request being handled by recipe. ID is: /signinup/code/consume", "file": "supertokens.py:383"}

com.supertokens {"t": "2022-05-06T07:53:34.794Z", "sdkVer": "0.7.0", "message": "Sending response to client with status code: 200", "file": "utils.py:126"}

com.supertokens {"t": "2022-05-06T07:53:34.794Z", "sdkVer": "0.7.0", "message": "middleware: Ended", "file": "supertokens.py:388"}
r
there should be more logs.. from the API that returns a 401
a
Copy code
com.supertokens {"t": "2022-05-06T07:58:52.550Z", "sdkVer": "0.7.0", "message": "middleware: Started", "file": "supertokens.py:339"}

com.supertokens {"t": "2022-05-06T07:58:52.550Z", "sdkVer": "0.7.0", "message": "middleware: Not handling because request path did not start with config path. Request path: /organizations/46ee7e8b-7c31-4101-8c32-dddbc87ee179/workplace/9/staff_on_leave", "file": "supertokens.py:347"}

com.supertokens {"t": "2022-05-06T07:58:52.551Z", "sdkVer": "0.7.0", "message": "getSession: Started", "file": "recipe/session/recipe_implementation.py:134"}

com.supertokens {"t": "2022-05-06T07:58:52.551Z", "sdkVer": "0.7.0", "message": "getSession: rid in header: False", "file": "recipe/session/recipe_implementation.py:138"}

com.supertokens {"t": "2022-05-06T07:58:52.551Z", "sdkVer": "0.7.0", "message": "getSession: request method: GET", "file": "recipe/session/recipe_implementation.py:139"}

com.supertokens {"t": "2022-05-06T07:58:52.552Z", "sdkVer": "0.7.0", "message": "getSession: Value of doAntiCsrfCheck is: False", "file": "recipe/session/recipe_implementation.py:162"}

com.supertokens {"t": "2022-05-06T07:58:52.552Z", "sdkVer": "0.7.0", "message": "errorHandler: Started", "file": "supertokens.py:393"}

com.supertokens {"t": "2022-05-06T07:58:52.552Z", "sdkVer": "0.7.0", "message": "errorHandler: Error is from SuperTokens recipe. Message: invalid jwt", "file": "supertokens.py:394"}
Copy code
com.supertokens {"t": "2022-05-06T07:58:52.552Z", "sdkVer": "0.7.0", "message": "errorHandler: Checking recipe for match: session", "file": "supertokens.py:403"}

com.supertokens {"t": "2022-05-06T07:58:52.552Z", "sdkVer": "0.7.0", "message": "errorHandler: Matched with recipeID: session", "file": "supertokens.py:406"}

com.supertokens {"t": "2022-05-06T07:58:52.552Z", "sdkVer": "0.7.0", "message": "errorHandler: returning TRY_REFRESH_TOKEN", "file": "recipe/session/recipe.py:152"}

com.supertokens {"t": "2022-05-06T07:58:52.552Z", "sdkVer": "0.7.0", "message": "Sending response to client with status code: 401", "file": "utils.py:116"}
this is the response when admin_client.session.cookies = cookies i send from response
r
so the issue is that it's not able to decode the access token
probably cause of how you are setting the cookies in the request
Can you try:
Copy code
admin_client.cookies = profile_permission
a
if i set
admin_client.cookies
i receinve an error
r
hmm
im not sure how to test with django this way.. but the issue is that cookies are not being set properly in the request
a
Copy code
tests/organization/staff/test_permissions.py:120: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venv/lib/python3.9/site-packages/django/test/client.py:747: in get
    response = super().get(path, data=data, secure=secure, **extra)
venv/lib/python3.9/site-packages/django/test/client.py:396: in get
    return self.generic('GET', path, secure=secure, **{
venv/lib/python3.9/site-packages/django/test/client.py:473: in generic
    return self.request(**r)
venv/lib/python3.9/site-packages/django/test/client.py:704: in request
    environ = self._base_environ(**request)
venv/lib/python3.9/site-packages/django/test/client.py:336: in _base_environ
    'HTTP_COOKIE': '; '.join(sorted(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

.0 = <dict_valueiterator object at 0x7fd7b9909db0>

        'HTTP_COOKIE': '; '.join(sorted(
>           '%s=%s' % (morsel.key, morsel.coded_value)
            for morsel in self.cookies.values()
        )),
        'PATH_INFO': '/',
        'REMOTE_ADDR': '127.0.0.1',
        'REQUEST_METHOD': 'GET',
        'SCRIPT_NAME': '',
        'SERVER_NAME': 'testserver',
        'SERVER_PORT': '80',
        'SERVER_PROTOCOL': 'HTTP/1.1',
        'wsgi.version': (1, 0),
        'wsgi.url_scheme': 'http',
        'wsgi.input': FakePayload(b''),
        'wsgi.errors': self.errors,
        'wsgi.multiprocess': True,
        'wsgi.multithread': False,
        'wsgi.run_once': False,
        **self.defaults,
        **request,
    }
E   AttributeError: 'dict' object has no attribute 'key'
a
mm, so I should try to do the request with request factory and not django.test.Client?
r
that's one way that we have done it
im not sure how to do it with django.test.Client
a
should this function be called each time a new session is activated?
r
activated as in created a new session?
a
activated as triggered the function
yep
r
yea.. it will be called each time
a
so I placed one print statement and this function is called only at the begiinning once
Copy code
python
# Finish sign in/up process with passwordless
    request_consume = async_rf.post('/auth/signinup/code/consume',
                                  {'preAuthSessionId': pre_auth_session_id,
                                   'deviceId': device_id,
                                   'userInputCode': otp},
                                  content_type='application/json')

    temp = my_middleware(request_consume)  # A coroutine
    response = await temp
    cookies = get_cookies(response)
    print("RESPONSE AFTER API CONSUME - ", response, response.__dict__, '\n\n', cookies)

    request_view = async_rf.get(reverse('staff-on-leave-list', kwargs={'organization_pk': organization.id,
                                                                 'workplace_pk': workplace.id}))

    request_view.COOKIES["sRefreshToken"] = cookies['sRefreshToken']['value']
    request_view.COOKIES["sIdRefreshToken"] = cookies['sIdRefreshToken']['value']
    # request_view.META['HTTP_ANTI_CSRF'] = response.headers['anti-csrf']

    temp = my_middleware(request_view)  # A coroutine
    response = await temp
made my test like in supertokens, but still got None response
note
my_middleware = middleware(create_new_session_view)
is using such create_new_session
Copy code
python
async def create_new_session_view(_: HttpRequest):
    pass
as you told me yesterday, that it should not return anything
r
what is the value of
cookies
?
a
Copy code
{'sAccessToken': {'value': 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsInZlcnNpb24iOiIyIn0%3D.eyJzZXNzaW9uSGFuZGxlIjoiZmM0YmNiN2ItNzQ1MS00YTE0LThlOTYtMjNmNmI1YzQ0OGRiIiwidXNlcklkIjoiMGYwMDdiMTUtODgzMS00MjhmLWEzZGUtNzFlNDI5M2RkNWVhIiwicmVmcmVzaFRva2VuSGFzaDEiOiI5MzA0ZjU3MzM4YTNmYzQ0NTA1OTVmNGExYzk3NTRkZmE3ZjQ1ZjEwNDQ2Yjc1ODEyY2ZjYmQ1ODliNjg4NGY1IiwidXNlckRhdGEiOnsicm9sZXMiOiJzdHJpbmcifSwiZXhwaXJ5VGltZSI6MTY1MTgzNDUzMzY0NSwidGltZUNyZWF0ZWQiOjE2NTE4MzA5MzM2NDUsImxtcnQiOjE2NTE4MzA5MzM2NDR9.UiiuAtKkhIp3p%2B/l%2Bwx6hA%2B5vRbnZmKiIftALuRM5Jp4WPXGA8WHwNesMTgNNiQeYOiFhoI/VQXSgTvJsk5AnFr4kZzajLIYYmlyctW%2BJbGDOuIrd4rw3sEO7t%2BNY0YwXD%2BziBTOjfYNwvQe2jYa4ARsleK2YSssu0M6nkntNEVE0ahfaYyHNUb6HhHX/q6vxzyYvnSdGqHRFxmfFkC2bhA6Vw0tK2PlRrmuC0Ba%2B07C1kPYs8uew/5u2YM%2B0wRSv9H%2BUtZRJI625Dvyl09w3z2hQlo8WVnAFKrbrp/g70Fm9wU0Pveuvz8MQ2/phUTN5Uhux6lAqdil96LAu5RLFQ%3D%3D', 'name': 'sAccessToken', 'expires': 'Friday, May 06, 2022 15:55:34', 'path': '/', 'comment': '', 'domain': '0.0', 'max-age': '', 'secure': None, 'httponly': True, 'version': '', 'samesite': 'lax'}, 'sRefreshToken': {'value': 'vWDmcvhvM03NCqgpbU4kTpqsn0csGXBIL%2BknX/gNeNX9q%2BxL17EKnMK6utYyRO9qv5aXtAJk%2BLG/VsWzM/Ajb19j3yVIACCA3zbnJdwnnaK/LeuKok8tpFp0paHStBbI4JznTAgBEUbt%2BZ1Er8H9fOMxs%2BnRaNmiuf4PWQJeDpcdgv2OZ2aVfudNzDiPBzkYii6bcAJmUs4x4nFr5PB7ctsI01EzxegKNJmoWgfgoxT9QxCXZHQk04xWYVkFNHag%2BJoghAEYrMNNYMtHzhHy.4326b2b8a7714a00f67fedf543aa1cc125f4ea8ecdb89270a166a4b7e8a3e052.V2', 'name': 'sRefreshToken', 'expires': 'Sunday, August 14, 2022 14:55:34', 'path': '/auth/session/refresh', 'comment': '', 'domain': '0.0', 'max-age': '', 'secure': None, 'httponly': True, 'version': '', 'samesite': 'lax'}, 'sIdRefreshToken': {'value': '87d11d50-4ca4-4eb6-9bdf-e7975f42633c', 'name': 'sIdRefreshToken', 'expires': 'Sunday, August 14, 2022 14:55:34', 'path': '/', 'comment': '', 'domain': '0.0', 'max-age': '', 'secure': None, 'httponly': True, 'version': '', 'samesite': 'lax'}}
r
ok so that works as expected
im really not sure what is going wrong with setting cookies in the request
a
maybe is should do something with the middleware?
r
i don't know
@kakashi_44 might be able to help.
a
btw, this is only the cookie, without the payload that POST consume sent
maybe I should put cookies in headers?
@kakashi_44 can you help out with this issue? We have session based authentication. How do you think I can tests view permissions, i.e. whether the view have properly configured permissions, userA has acces to viewA, but userB has not and view is indeed restricting userB from viewA? What I am trying to do now is to authenticate as an existing user who has the right permission and access specific view. But I can not attach cookie, which is being generated during the passwordless login. my init - https://discord.com/channels/603466164219281420/969488117474132038/971667000843706368 my override - https://discord.com/channels/603466164219281420/969488117474132038/971666992224415784 my verify_session is used as
@method_decorator(verify_session(), name='dispatch')
for class based views my test function - https://discord.com/channels/603466164219281420/969488117474132038/972035702084743179
what is interesting is that based on how i attach cookie, i receive different error messages from logger, e.g., with
admin_client.cookies
the loggs are like this
Copy code
com.supertokens {"t": "2022-05-06T13:03:43.519Z", "sdkVer": "0.7.0", "message": "getSession: Value of doAntiCsrfCheck is: False", "file": "recipe/session/recipe_implementation.py:162"}

...

com.supertokens {"t": "2022-05-06T13:03:43.519Z", "sdkVer": "0.7.0", "message": "errorHandler: Error is from SuperTokens recipe. Message: invalid jwt", "file": "supertokens.py:394"}

...
com.supertokens {"t": "2022-05-06T13:03:43.520Z", "sdkVer": "0.7.0", "message": "errorHandler: returning TRY_REFRESH_TOKEN", "file": "recipe/session/recipe.py:152"}

com.supertokens {"t": "2022-05-06T13:03:43.520Z", "sdkVer": "0.7.0", "message": "Sending response to client with status code: 401", "file": "utils.py:116"}
with
admin_client.session.cookies
i get something like this
Copy code
...
com.supertokens {"t": "2022-05-06T13:23:15.053Z", "sdkVer": "0.7.0", "message": "getSession: UNAUTHORISED because idRefreshToken from cookies is undefined", "file": "recipe/session/recipe_implementation.py:146"}

com.supertokens {"t": "2022-05-06T13:23:15.053Z", "sdkVer": "0.7.0", "message": "errorHandler: Error is from SuperTokens recipe. Message: Session does not exist. Are you sending the session tokens in the request as cookies?", "file": "supertokens.py:394"}

...
com.supertokens {"t": "2022-05-06T13:23:15.054Z", "sdkVer": "0.7.0", "message": "errorHandler: returning UNAUTHORISED", "file": "recipe/session/recipe.py:147"}

com.supertokens {"t": "2022-05-06T13:23:15.054Z", "sdkVer": "0.7.0", "message": "Sending response to client with status code: 401", "file": "utils.py:116"}
i might have expressed not clearly enough to describe the issue, so feel free to ask for more context if something isn't clear
k
Hey @ara.ara , can you share more detail regarding
Copy code
python
reverse('staff-on-leave-list', kwargs={'organization_pk': organization.id,
                                                                 'workplace_pk': workplace.id})
how is the view function defined?
a
yep, here it is
Copy code
python
@method_decorator(verify_session(), name='dispatch')
class StaffOnLeaveViewSet(mixins.CreateModelMixin, mixins.DestroyModelMixin,
                          viewsets.ReadOnlyModelViewSet):
    serializer_class = StaffOnLeaveSerializer

    def get_queryset(self):
        return StaffOnLeave.objects.filter(is_active=True, organization_id=self.kwargs.get("workplace_pk"))

    def get_permissions(self):
        if self.action in ['destroy', 'create']:
            self.permission_classes = [StaffOnLeaveCreatePermission]
        else:
            self.permission_classes = []
        return super().get_permissions()

    @extend_schema(
        summary="List all staff on leave",
    )
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)

    # other view methods
    )
k
are you importing verify_session from syncio or asyncio?
a
Copy code
python
from supertokens_python.recipe.session.framework.django.syncio import verify_session
s
@ara.ara is your app in sync mode and tests in async?
a
only one test case is async
a fixture to be exact, it is the place where i get supertokens session cookies ( where I do POST signinup and consume)
s
okay
a
current test and logs:
Copy code
python
def test_client(cookies, client, instances):
    organization, profile = instances

    client.cookies = SimpleCookie(cookies)

    response = client.get(reverse('staff-on-leave-list', 
                                  kwargs={'organization_pk': organization.id}))
Finally managed to attach cookies to django test client like this
Copy code
python
    client.cookies = SimpleCookie()

    client.cookies["sAccessToken"] = cookies['sAccessToken']['value']
    client.cookies["sRefreshToken"] = cookies['sRefreshToken']['value']
    client.cookies["sIdRefreshToken"] = cookies['sIdRefreshToken']['value']

    response = client.get(url)
the problem was this function
Copy code
python
def get_cookies(response: HttpResponse) -> Dict[str, Any]:
    cookies: Dict[str, Any] = {}
    for key, morsel in response.cookies.items():
        cookies[key] = {
            'value': morsel.value,
            'name': key
        }
        for k, v in morsel.items():
            if (k in ('secure', 'httponly')) and v == '':
                cookies[key][k] = None
            elif k == 'samesite':
                if len(v) > 0 and v[-1] == ',':
                    v = v[:-1]
                cookies[key][k] = v
            else:
                cookies[key][k] = v
    return cookies
that generated redundant values and nesting like there https://discord.com/channels/603466164219281420/969488117474132038/971397399367012352
though, cookies should have just been attached right from the response in this format
Copy code
<SimpleCookie: sAccessToken='token' sIdRefreshToken='token' sRefreshToken='token'>
so, getting rid of
get_cookies
function and getting cookies from
cookies = response.cookies
we can just
client.cookeis = cookies
There seems to have another minor issue I can't see to resolve. Why
POST /signinup/consume
returns user_id different from which I logged in?
Copy code
python
def cookies(admin_client, async_rf, rf, client, profile):

    def create_new_session_view(_: HttpRequest):
        pass
    
    print("MY USER ID", profile.supertokens_user_id, profile.phone_number)

    # Start passwordless login
    my_middleware = middleware(create_new_session_view)
    request_login = rf.post('/auth/signinup/code', 
                            {'phoneNumber': profile.phone_number},   content_type='application/json')
    response = my_middleware(request_login)

    print("SIGNINUP ", response._container)

    assert response.status_code == status.HTTP_200_OK

    response_content = json.loads(response._container[0].decode('utf-8'))
    device_id = response_content['deviceId']
    pre_auth_session_id = response_content['preAuthSessionId']

    with open(TEST_OTP_LOCATION_PATH, "r") as otp_file:
        otp = otp_file.read()

    # Finish sign in/up process with passwordless
    request_consume = rf.post('/auth/signinup/code/consume',
                                   {'preAuthSessionId': pre_auth_session_id,
                                    'deviceId': device_id,
                                    'userInputCode': otp},
                                    content_type='application/json')

    response = my_middleware(request_consume)

    print("CONSUME ", response._container)

    assert response.status_code == status.HTTP_200_OK
    return response.cookies
Copy code
MY USER ID db6be225-3561-428a-93ee-7a815303cfcb +998999999999
OTP {'phone_number': '+998999999999', 'code_life_time': 900000, 'pre_auth_session_id': 'DmGNaiG-4oYBGmLCDZjyF-EveVgVKIXK8RAq_vzfaUU=', 'user_input_code': '376488', 'url_with_link_code': None}
SIGNINUP  [b'{"status":"OK","deviceId":"3rele/+Jg0Tyv8OsrlW1SZasU/LCKJRhuFmOaAVliNA=","preAuthSessionId":"DmGNaiG-4oYBGmLCDZjyF-EveVgVKIXK8RAq_vzfaUU=","flowType":"USER_INPUT_CODE"}']
CONSUME  [b'{"status":"OK","createdNewUser":false,"user":{"id":"309b83e5-8d0a-4bca-8583-7ab229df587f","time_joined":1652275238532,"phoneNumber":"+998999999999"}}']
r
are you connecting to a different core instance?
a
i am connecting to my docker running supertokens
r
and that is connected to a db? or in mem db?
cause the only explanation of what you are seeing is that it's connecting to a diff db
a
ohhh, i see
let me check
but why phone number is the same
r
You created the code with the same phone number?
a
Before signing in I created a user with this phone number
and passed it to signinup
so OTP code was generated with this phone number
r
right yea.. so that's why it's associated with that phone number
i think you might have deleted the content of the db or connected to another db.. that's why a different user id
a
i have the same issue even if I provide randomly generated phone number
how do I check what DB supertokens is connecting to?
r
Are you giving a db connection uri to the core when you run it?
a
nope
r
so then it's running an in memory db
which means each time you restart the core, it starts from a fresh db instance.
a
do i need to provide POSTGRESQL coneection uri? i did the second option
Copy code
docker run \
    -p 3567:3567 \
    -e POSTGRESQL_CONNECTION_URI="postgresql://username:pass@host/dbName" \ 
    -d registry.supertokens.io/supertokens/supertokens-postgresql

# OR

docker run \
    -p 3567:3567 \
    -e POSTGRESQL_USER="username" \
    -e POSTGRESQL_PASSWORD="password" \
    -e POSTGRESQL_HOST="host" \
    -e POSTGRESQL_PORT="5432" \
    -e POSTGRESQL_DATABASE_NAME="supertokens" \ 
    -d registry.supertokens.io/supertokens/supertokens-postgresql
https://supertokens.com/docs/passwordless/quick-setup/database-setup/postgresql#:~:text=to%200.0.0.0.-,docker%20run%20%5C,%2Dd%20registry.supertokens.io/supertokens/supertokens%2Dpostgresql,-Copy
r
yea either of them should persist data. The db it's connecting to is the one you are running as "host".
a
my implementation
Copy code
docker run --name supertokens -p 3567:3567 -e POSTGRESQL_USER=postgres -e POSTGRESQL_PASSWORD=postgres -e POSTGRESQL_DATABASE_NAME=postgres -e POSTGRESQL_HOST=host.docker.internal -e POSTGRESQL_PORT=54355 --restart=always -d registry.supertokens.io/supertokens/supertokens-postgresql
r
"host.docker.internal"
so you are running another docker image for postgres?
a
yes
r
are you sharing a volume with that in your host machine?
a
my postgres
Copy code
docker run --name db_postgres -d -e POSTGRESQL_USERNAME=postgres -e POSTGRESQL_PASSWORD=postgres -e POSTGRESQL_DATABASE=postgres --restart=always -p 54355:5432 bitnami/postgresql:14
r
right.. so this won't save data on restart either
this means that each time you restart the postgresql container, it's starting from a fresh db
so again, no data is being saved
which would explain why a new user ID is being generated for the same phone number
a
but I am generating user during a test
just before calling an API
is it because the data is saved in
test_<original_table_name>
, i.e. creating test tables for tests by prefixing with
test_
and supertokens are looking into original tables and not the test ones?
r
yea. Maybe
you haven't provided any prefix to the supertokens docker command..
a
what prefix to docker? I think you misunderstood. What I meant was that tests are copying tables from db and prefix it with test_
during tests and destroy these tables after a test run is complete
r
right. So you can provide an env var in the docker run command (for supertokens) that tell it about this prefix.
POSTGRESQL_TABLE_NAMES_PREFIX
POSTGRESQL_TABLE_NAMES_PREFIX="test_"
and then it should read from the test_* tables
a
ohh
r
this is assuming that you are running a docker core just for tests
a
yep
r
if you are running the same core for tests and other env, then this won't work
a
hmm
i don't think that the problem was in prefixes. So, If i understood it correctly supertokens create additional tables. Users are saved into
passwordless_users
table and I create in
myapp_profile
table
consume returns user id from
passwordless_users
table
r
hmm. yea. that's possible.
12 Views