Closed dmayle closed 3 years ago
Hi @dmayle ๐
Thanks for opening an issue and sorry for the delayed response. Generally, when I'm not using firebase_auth, I have been gravitating toward just having a UserRepository which exposes methods such as signIn
, signOut
, and a user getter like:
Stream<User> get user;
I would recommend using something like fresh_dio or fresh_graphql to manage the auth tokens and refresh and would try to keep my blocs as feature-driven as possible. This usually means rather than having a UserBloc
which manages the current user, I would have feature-blocs which require user data request the user data from the UserRepository
. The caching of the user can be done at the UserRepository
and in my opinion this simplifies/standardizes the data flow and dependency graph because you don't end up having many interbloc-dependencies. Hope that helps!
Closing for now but feel free to comment with additional questions and I'm happy to continue the conversation ๐
@felangel no worries on the timing... especially since it's the holidays. Thanks for getting back to me!
I'm actually working with GRPC, where auth interceptors are attached at the level of the client, which is typed per-service. This means that I have to choose between either storing the interceptor (and token) in a data package which understands all of the GRPC services I use, or I have to have dependency between two repositories.
Also important, I think, is that I have a logged out user experience, so the app changes (for example, navigation, user menu, features). If I don't have a UserBloc, than each of my feature blocs have to maintain the user state in order to adapt to the user being logged in or not.
What I've started building so far is a UserBloc with UserRepository and a MessagesRepository (example feature repository). When MessagesRepository wants to create a new MessagesClient, it asks UserRepository for the UserInterceptor, and attaches it to the client. The two downsides to this approach so far are the strict dependency, and the difficulty of getting a message to the UserBloc from the feature repositories when an auth error is returned. Ideally, the UserRepository would allow the UserBloc to attach a callback to the UserInterceptor so that I wouldn't need to have an out-of-band response, something like chaining it to the ResponseFuture, though I haven't tried that yet.
@felangel I'm trying to get my head wrapper around your statement:
This usually means rather than having a
UserBloc
which manages the current user, I would have feature-blocs which require user data request the user data from theUserRepository
.
Would it make sense to than have, for example, a LoginBloc
for the login menu, and then a MessagesBloc
without having the explicit UserBloc
?
Would it make sense to than have, for example, a LoginBloc for the login menu, and then a MessagesBloc without having the explicit UserBloc?
Yup ๐
Further along in my code... @felangel How do you handle logout then? By piping through the UserRepository to any feature that needs logout UI? Or by making any feature that needs logout UI include logout in the feature bloc?
@dmayle I am working on something similar currently but I do not know how I can do the following:
I am struggling with that since a few days already, started over a few times but could not find a solution yet. Maybe you could give me a hint?
@cnpog if you store your jwt in a Bloc, than you can switch to HydratedBloc, which will automatically persist the state for you, you just have to define fromJson and toJson methods.
In the architecture of bloc, 'repository' is a model-specific library, so you wouldn't store data persistently in the repository, though I suppose you could provide methods to load it to or from hive
(a dart/flutter in-app database library).
In terms of persistence, I'm going back and forth between @felangel 's suggestion to eliminate the UserBloc
, which would require me to manage persistence at the repository layer (e.g. having the user getter Stream load first from hive, and then persist on each update), or having an actual UserBloc
based on HydratedBloc which would handle all of that for me, but then require piping the User into all of my feature repositories so that I can instantiate grpc clients with the auth interceptor.
For managing tokens, I would highly recommend taking a look at fresh.
@felangel My reading of fresh is that it handles OAuth2 token when using dio or graphql, which makes it mostly useful for third-party authentication. @cnpog may or may not be exposing OAuth2 tokens over GRPC, and I definitely am not using OAuth2, mine is a first-party auth only.
@dmayle fresh is not limited to OAuth2 and can support custom auth solutions as well ๐
@cnpog I just added hive to my UserRepository and it took something like 13 lines of code to persist, retrieve and update the token/user.
The storage is fairly simple however I'm guessing you'll also need to send the token with each outbound request.
Is your feature request related to a problem? Please describe. I'm trying to understand the management of storage and caching in a bloc-based application on Flutter, specifically for the login use case. Both the Login and Firebase Login fail to address this in the examples, and so I'm still trying to understand how to properly use Blocs.
In the Login example, there is no connection between the auth repository and the user repository, so following an auth state change in the repository, the auth bloc queries the user repository, which magically generates the User object out of thin air. I fail to see how an actual auth repository would make user details available to the app. Does the auth repository only provide some sort of auth token, and then the user repository use that to query the user? If so, how is the token passed between the two, push to user repository, or pull from auth repository? If the auth repository generates the user, than how is that user passed?
For the firebase login example, the single auth repository avoids the question by relegating all storage of the User object to the firebase package. The user thus ends up being stored in the state of the auth bloc and the firebase package. It's not clear if that means that I should be caching the user object in the auth repository as well as in the auth bloc state, or if that's just an artifact of how the firebase package works.
I think the idea of having state machines in blocs is wonderful, but it seems like the state management is actually being hidden inside of an external package, and not actually handled by the auth bloc in this case. It's just not clear to me yet how to properly handle this.
Describe the solution you'd like I would like some clarity as to how to store this information when actually implementing something similar. In the example would be idea, because it would help all users, but just a response on this issue is enough to provide clarity.