supabase-flutter icon indicating copy to clipboard operation
supabase-flutter copied to clipboard

PostgrestException(message: JWT expired, code: PGRST301, details: Unauthorized, hint: null)

Open EddyHezarian opened this issue 1 year ago • 13 comments

I have a Flutter app with Supabase backend. when I want to read data from tables for the first 3 times calling the database it's all good working but after 3 times I am facing this error :

PostgrestException(message: JWT expired, code: PGRST301, details: Unauthorized, hint: null) the way I implement reading data is

 Future<List<BrandModel>> getBrands() async {
  List<BrandModel> data = [];
  try {
    var response = await supabase.from('brand').select();
    data = (response as List).map((e) => BrandModel.fromJson(e)).toList();
  } catch (e) {
    print(e);
  }
  return data;
}

the main function is :

Future<void> main() async {
  //? widget bindings
  WidgetsFlutterBinding.ensureInitialized();
  //? initializing supa
  await Supabase.initialize(
      url: AppConsts.supaBaseURL, anonKey: AppConsts.anonKey);
}

EddyHezarian avatar Jan 05 '24 11:01 EddyHezarian

@dshukertjr the RLS policy of the table is also off and I have No Idea How to figure this out. can you suggest a solution for this?

EddyHezarian avatar Jan 05 '24 11:01 EddyHezarian

@EddyHezarian How exactly are you calling the getBrands () function? Do you call it on a button press?

dshukertjr avatar Jan 05 '24 11:01 dshukertjr

@dshukertjr its a future builder in screen :

FutureBuilder(
future: brandsApiProvider.getBrands(),
builder: (context, AsyncSnapshot<List<BrandModel>> snapshot) {
  if (snapshot.hasData) {
    return ListView.builder(
        itemCount: snapshot.data!.length,
        itemBuilder: (context, index) {
          var currentDataIndex = snapshot.data![index];
          return BrandCard(currentDataIndex: currentDataIndex);
        });
  } else {
    return Container();
  }
}),

EddyHezarian avatar Jan 05 '24 11:01 EddyHezarian

You mentioned

after 3 times

, but with a FutureBuilder, how do you call it three times?

dshukertjr avatar Jan 05 '24 11:01 dshukertjr

no when i mentioned 3 times i mean after implemented this screen , after each restarts this getBrands() got called and after third Restart App getBrands() throw this error : PostgrestException(message: JWT expired, code: PGRST301, details: Unauthorized, hint: null)

i changed the getBrands() like this :

  Future<List<BrandModel>> getBrands() async {
    List<BrandModel> data = [];
    try {
      var response = await supabase.from('brand').select();
      data = (response as List).map((e) => BrandModel.fromJson(e)).toList();
    } catch (e) {
      if (e is PostgrestException && e.code == 'PGRST301') {
          try {
              await supabase.auth.refreshSession(); // Attempt to refresh the session
              var response = await supabase.from('brand').select();
              data = (response as List).map((e) => BrandModel.fromJson(e)).toList();
          } catch (e) {
              print(e)
          }
      } 
    }
    return data;
  }

EddyHezarian avatar Jan 05 '24 11:01 EddyHezarian

it works but is it an efficient way to implement this?

EddyHezarian avatar Jan 05 '24 11:01 EddyHezarian

@EddyHezarian Hmm, interesting. You shouldn't have to refresh the session manually like that. I wonder what is causing the token to expire.

Roughly how long does it take from calling getBrands() the first time to calling getBrands() the third time where it fails? Also, have you changed your Access token (JWT) expiry time from your Supabase dashboard? If so, what is it set to?

BTW, could you format your code block you share? It helps us read the code easier.

dshukertjr avatar Jan 05 '24 11:01 dshukertjr

It may be the case that the function is being called too many times and the server is denying the request after a few times. Can you try the following snippet:

// in a StatefulWidget
late final brandsFuture = brandsApiProvider.getBrands();

FutureBuilder(
future: brandsFuture,
builder: (context, AsyncSnapshot<List<BrandModel>> snapshot) {
  if (snapshot.hasData) {
    return ListView.builder(
        itemCount: snapshot.data!.length,
        itemBuilder: (context, index) {
          var currentDataIndex = snapshot.data![index];
          return BrandCard(currentDataIndex: currentDataIndex);
        });
  } else {
    return Container();
  }
}),

This should call getBrands only when the widget is first built, not every time the build function is called. See the FutureBuilder documentation for more info.

bdlukaa avatar Jan 07 '24 14:01 bdlukaa

What was the working solution @EddyHezarian ? I have the same issue and it is not going away when refreshing the session (next request is still throwing a PGRST301 error), even though the access key is properly refreshed.

flogy avatar Jan 25 '24 15:01 flogy

@flogy Could you provide a sample code that is causing the error?

dshukertjr avatar Jan 26 '24 02:01 dshukertjr

What was the working solution @EddyHezarian ? I have the same issue and it is not going away when refreshing the session (next request is still throwing a PGRST301 error), even though the access key is properly refreshed.

Same issue

elliottetzkorn avatar Feb 22 '24 04:02 elliottetzkorn

What was the working solution @EddyHezarian ? I have the same issue and it is not going away when refreshing the session (next request is still throwing a PGRST301 error), even though the access key is properly refreshed.

After refreshing session its all good for me. Provide your code to better understand your issue.

EddyHezarian avatar Feb 22 '24 11:02 EddyHezarian

We have shipped an update that will hopefully make things better with this issue. If you could try out v2.5.0 of supabase_flutter and see if things are better, that would be great.

dshukertjr avatar Apr 09 '24 13:04 dshukertjr