felangel / bloc

A predictable state management library that helps implement the BLoC design pattern
https://bloclibrary.dev
MIT License
11.8k stars 3.39k forks source link

[Question] Sending application authorization state to application bloc.. #1074

Closed mflesh1 closed 4 years ago

mflesh1 commented 4 years ago

I have been using the bloc plugin so far and really enjoy its simplicity but I have a problem I need to solve and was wondering if there are any best practices.

I have an application that requires user authentication and once authenticated a token is issued to the user that then is passed on all future REST API requests a pretty common pattern.

At any point the token can become invalidated which would in turn cause the API to respond with that the request is unauthorized.

I initially built the app patterned after the flutter_login example with an Auth bloc surrounding the App. After the user logs in each screen has a similar pattern.

  1. A Bloc is used to manage state and events for EACH screen, bloc receives a LoadData event.
  2. Bloc handles load data event by calling a repository (interface).
  3. Repository retrieves data, in this instance the implementation calls a REST API.

This REST API can return a 401 if the given token in invalid at which point the user should be "logged out" of the application.

It would seem for this to work the 401 result would have to be passed back up to the Screen level so it could then be handled (log out).

This would need to be done for every screen bloc, this seems redundant and I am just wondering if there is a better practice.

felangel commented 4 years ago

Hi @mflesh1 👋 Thanks for opening an issue and for the positive feedback! I really appreciate it 🙏

Regarding your question, I believe it is similar to https://github.com/felangel/bloc/issues/766#issuecomment-571811625.

I'm currently working on revamping the login example to better demonstrate how to handle this specific use-case elegantly and you should expect to have an updated example within the next few days 👍

mflesh1 commented 4 years ago

@felangel thanks for the response, looking forward to the updated example.

In regards to the reference #766 (comment) that would require passing the authentication bloc to ALL of the blocs which I was hoping to avoid.

felangel commented 4 years ago

@mflesh1 in an attempt to simplify token refresh I have created package:fresh (still highly experimental).

You can check out the example for a complete sample application using fresh which integrates with the jsonplaceholder API.

Let me know if that helps 👍

mflesh1 commented 4 years ago

@felangel thank you for the example, although this is a bit more complex for my needs it did point me down the right path.

felangel commented 4 years ago

Awesome! Glad it helped 😊 Closing for now but feel free to comment with additional questions and I’m happy to continue the conversation 👍

JohnGalt1717 commented 4 years ago

For anyone else that might be facing this here's what my solution was that simplifies refreshing tokens based on a timer etc by making it always on demand and allowing a single point to update the tokens and then share them to N number of clients (micro services that all require the auth token against the IdP)

If you create your clients so that instead of just taking a String Token they take a String Function() getToken you can then pass the function to the clients which will get it on a per call basis. Then in your clients write your interceptor so that it will call this on every call to get the token to be put into the header or wherever.

On a gRPC client for example you can do this like this:

    final callOptions = token != null
        ? CallOptions(providers: [
            (Map metaData, String uri) {
              metaData["Authorization"] = "Bearer ${token()}";
            }
          ])
        : null;

then create your wrapped client like this: system = SystemCtlClient(channel, options: callOptions);

By doing so, every call will always use the most recent token and you don't have to worry about recreating the clients from the RepositoryProvider. (it would still be nice to be able to invalidate a RepositoryProvider so that it forced a new creation and disposed the old one)

rohankandwal commented 2 years ago

The example link is no longer valid @felangel can you please update it?

felangel commented 2 years ago

The example link is no longer valid @felangel can you please update it?

yup sorry about that -- just updated the link 👍