🤓Add optional key_words:access_token to creat_client let auth user easier without signing in
Is your feature request related to a problem? Please describe. here's about my understanding on recent issues and my explanation on solutions in PR #656 #658 #663 #645
in cannot authenticate user #645 in short, i think it shows up for doing nothing on refresh the token passed to postgrest,it fixed in v2.3.3 by updating
def _listen_to_auth_events(self, event: AuthChangeEvent, session: Session | None):
"""listen to auth events and update auth token"""
access_token = self._access_token
if event in ["SIGNED_IN", "TOKEN_REFRESHED", "SIGNED_OUT"]:
# reset postgrest and storage instance on event change
self._postgrest = None
self._storage = None
self._functions = None
access_token = session.access_token if session else self._access_token
self._auth_token = self._create_auth_header(access_token)
,self._auth_token would be update on new events and also as the new header of postgrest etc
in failed to get_session after create_client by accees_token as supabase_key #663
firstly , client.auth.get_session() would retrun None ,because in AsyncGoTrueClient , get_session() only works proper as we saved session in self._storage,which means we need technically client.auth.sign _in_xxx() or client.auth.set_session()
async def get_session(self) -> Union[Session, None]:
...
current_session: Union[Session, None] = None
if self._persist_session:
maybe_session = await self._storage.get_item(self._storage_key)
....
else:
current_session = self._in_memory_session
if not current_session:
return None
...
secondly, i will fail quickly on calling postgrest via create_client passed accees_token as supabase_key
for two reasons,
- called
client._auth_token = await client._get_token_header()on createing client(u do not signed in at that moment) so that theclient.auth.get_session()inclient._get_token_header()must return None,soacees_tokenset None forpostgrestunless u sign in later - we called
client.init,and it setapiKeyin headers asaccess_tokenthat got after user signed in ,and is not valided forapiKeyafter tests
# in `client.init`,
...
options.headers.update(self._get_auth_headers())
self.options = options
...
def _get_auth_headers(self) -> Dict[str, str]:
"""Helper method to get auth headers."""
return {
"apiKey": self.supabase_key,
"Authorization": f"Bearer {self.supabase_key}",
}
in Clients should take an optional supabase_token (workaround provided) #658
i do agree that should take an optional supabase_token ,we can not auth only by access_token (actually in code,it means not able to set self._auth_token by passing access_token explicitly now)
my solutions on just passing token without changing supabase-py to auth user after tests
should do create_client with normal key and url firstly
- pass
access_tokenmanually like
self.auth.postgrest.auth(access_token)
# and then do crud operations ...
- call
self.auth.set_session()manually like
# firstly should get aceess_token and refresh_token from front_end
self.auth.set_session(aceess_token,refresh_token )
my solutions on changeing code of supabase-py in PR #656
- add optional
access_tokenkey words and set is as default key, if None, set supabase_key asself. _access_token, and it will default bt passed toself._create_auth_header()
# in init
def __init__(
self,
supabase_url: str,
supabase_key: str,
access_token: Union[str, None] = None,
options: ClientOptions = ClientOptions(storage=AsyncMemoryStorage()),
):
# ....
# only can be set by init instance
self._access_token = access_token if access_token else supabase_key
# will be modified by auth state change
self._auth_token = self._create_auth_header(self._access_token)
# ...
# listen to event to refresh access_token if we explicitly sign in
def _listen_to_auth_events(self, event: AuthChangeEvent, session: Session | None):
"""listen to auth events and update auth token"""
access_token = self._access_token
if event in ["SIGNED_IN", "TOKEN_REFRESHED", "SIGNED_OUT"]:
# reset postgrest and storage instance on event change
self._postgrest = None
self._storage = None
self._functions = None
access_token = session.access_token if session else self._access_token
self._auth_token = self._create_auth_header(access_token)
- add
self._update_auth_headers()to updateself.options.headersand it also will be setted asheadersthat passes topostgrest
def _update_auth_headers(self) -> None:
"""Helper method to get auth headers."""
new_headers = {
"apiKey": self.supabase_key,
**self._auth_token,
}
self.options.headers.update(**new_headers)
@property
def postgrest(self):
if self._postgrest is None:
self._update_auth_headers()
self._postgrest = self._init_postgrest_client(
rest_url=self.rest_url,
headers=self.options.headers,
schema=self.options.schema,
timeout=self.options.postgrest_client_timeout,
)
return self._postgrest
in the end u will find def _get_token_header(self): no where to use
in short add optional aceess_token as auth_token in first init client, and update auth_token by listen to events, if access_token is None, default same to supabase_key