_invokeWithStreamedResponse doesn't include auth information
Bug report
- [x] I confirm this is a bug with Supabase, not with my own application.
- [x] I confirm I have searched the Docs, GitHub Discussions, and Discord.
Describe the bug
I'm new to supabase so apologies if this is not a bug. When using _invokeWithStreamedResponse the supabaseClient on the backend is unable to assume the auth role of the user making the request. I believe this is because of the different URLSession being used, as described in the function documentation, but it wasn't clear if this is a known side affect.
Is there a recommended workaround when using this function? Should I pass the user_id in the function body and assume an admin level role in the backend, or is that an anti-pattern with supabase?
To Reproduce
Steps to reproduce the behavior, please provide code snippets or a repository:
- Make a request with the regular invoke function
- Observe that user level auth header is sent
- Make the same request using _invokeWithStreamedResponse
- Observe that user level auth header is not sent
Expected behavior
Get the same authorization scopes as using the regular invoke function
Screenshots
System information
version 2.24.1
Additional context
Add any other context about the problem here.
Hi, thanks for reporting this, indeed it is a side effect of not using the same URLSession, but this is also considered a bug, as there is a workaround I can do in code to make it work, I'll push a PR with the fix ASAP.
Thanks again.
hey @grdsdev ive been using your commit in my builds and its generally working but sometimes my app will start to get 401 errors from supabase (jwt invalid apparently?). im not sure if its related to using streaming or not, my only edge function is using streaming.
my signup methods are to do either
let session = try await supabase.auth.signInWithIdToken(
credentials: OpenIDConnectCredentials(
provider: .google,
idToken: idToken,
accessToken: accessToken
)
)
or
let session = try await supabase.auth.signInWithIdToken(
credentials: .init(
provider: .apple,
idToken: idToken
)
)
and then on subsequent app restarts I check for auth state changes which is what is logging me back in
.task {
for await state in supabase.auth.authStateChanges {
switch state.event {
case .initialSession:
await handleLoginAuthState(state)
case .signedIn:
await handleLoginAuthState(state)
case .signedOut:
await handleLogout(state)
case .passwordRecovery, .tokenRefreshed, .userUpdated, .userDeleted, .mfaChallengeVerified:
break
}
}
}
Not sure if I am supposed to be holding onto the jwt in keychain myself or prompting any refreshes on my own before calling the edge function (am new to supabase). Just wanted to call out this error though. Its hard to reproduce but once it starts happening (getting 401 responses) it happens on every request. I am logged-in though when this is happening
just updating - when I went to use my app this morning I was still signed in but got another 401 error when invoking the edge function. Then I quit the app and reopened it and the 401 errors went away
@bbauman1 thanks for trying that commit.
Not sure if I am supposed to be holding onto the jwt in keychain myself or prompting any refreshes on my own before calling the edge function (am new to supabase). No need to hold jwt, SDK should handle everything internally
Back on my PR, there are still some issues with that implementation, that's why it still marked as draft, I'm working on some edge cases.
@grdsdev appreciate the update. do you think there will be a fix out this week? I'm also fine with a temporary unofficial workaround that I have to apply myself. just wondering because I'm trying to ship an app using streaming this week and want to know if I should set up a different server provider with that timeline
@bbauman1 as a workaround, you can manually update functions's auth info before calling your edge function, as:
let accessToken = try await supabase.auth.session.accessToken
supabase.functions.setAuth(accessToken) // Make sure to `setAuth` before calling `_invokeWithStreamedResponse` method.
let response = supabase.functions._invokeWithStreamedResponse(...)
This will make sure that _invokeWithStreamedResponse method grab the latest access token to invoke function.
perfect thank you!
This fix unfortunately isn't as straight forward as I first imagined, that is one of the main reasons why the method is still marked as experimental, the way networking layer is structured in the lib, it doesn't account for streamed responses, so for that specific method I create a new URLSession and do all from scratch (without using HTTPClient, which every other API call uses).
To fix this the proper way, we need to add support for streaming responses to the HTTPClient, which will demand more time.
As for now, if anyone faces this issue, please use the workaround at https://github.com/supabase/supabase-swift/issues/634#issuecomment-2586755998 temporarily, until this gets properly fixed.
Thanks.
Thanks for the workaround. Was losing my hair over this.
@bbauman1 as a workaround, you can manually update functions's auth info before calling your edge function, as:
let accessToken = try await supabase.auth.session.accessToken supabase.functions.setAuth(accessToken) // Make sure to
setAuthbefore calling_invokeWithStreamedResponsemethod. let response = supabase.functions._invokeWithStreamedResponse(...) This will make sure that_invokeWithStreamedResponsemethod grab the latest access token to invoke function.
There's a missing label on the second line, the exact workaround is:
let accessToken = try await supabase.auth.session.accessToken
supabase.functions.setAuth(token: accessToken) // Make sure to `setAuth` before calling `_invokeWithStreamedResponse` method.
let response = supabase.functions._invokeWithStreamedResponse(...)