I am currently trying to use redux-persist to persist state after page refreshes. The problem is that, since my application uses OAuth flow for authentication, going to the 3rd party server for authentication persists my logged in state because leaving my website counts as a refresh.
For example, I go to my website and I am not currently logged in. So, even though I successfully log in via the 3rd party serve, when I get redirected, I am still "logged out" because I was logged out because going to the 3rd party server. Vice versa, if I am logged in and try to log out, I am still going to be logged in even though I successfully logged out.
Does anyone have any ideas how to get around this? Thanks
Redux Persist comes with whitelisting and blacklisting of the reducers. So you need to blacklist your auth reducer, or make a nested persist depending upon your use case. As mentioned in the example docs here
const persistConfig = {
key: 'root',
storage: storage,
blacklist: ['auth'] // your reducer name
};
Related
I'm managing authentication state in a readable store which is grouped with a promise that resolves when the auth state is known (either signed in or out). The store is set internally via onAuthStateChange.
I'm trying to access this auth state from the server (+layout.server.ts and +page.server.ts) so that I can redirect the user to a sign-in page if they aren't authenticated, or load data from the database if (and only if) they are. No matter what I try, whenever I access this store from the server, its value is always null. I think this is because Firebase is only supposed to run on the client, although I'm not sure. Is there any way I can access this store from the server, or change my implementation so that Firebase runs in the server and passes auth state to the client? This blog post explains pretty much exactly what I want to do, but the solution here seems more complicated than it needs to be.
I've tried moving Firebase initialization code to the server (in both hooks.server.ts and +layout.server.ts), but there's no way for me to pass the auth object to the client because it can't be serialized (I get an error explaining this when I try to return it from a load function in +page.server.ts). I've also tried to handle authentication only using client-side code, but the server is responsible for loading data from the database, so in this case there's no way for me to verify a valid authentication state from the server.
author of the blog post you linked here.
You are correct in that the firebase client is only supposed to run on the client. If you want to access the Firebase services server-side you're supposed to use Firebase admin.
In my post I'm working around these limitations that firebase sets and unfortunately you do need that much code.
You are forced to do all the authentication in the frontend (firebase-client) and only afterwards you can inform the backend of the user info. (through the cookie)
Before I wrote this the ideal solution I had in my head was this:
Send the user to a login page that lists all the possible login providers.
The user chooses one and logs in.
the user gets redirected to a callback page where we can do something with the auth data in the backend.
I do think that something like this flow is possible if you're using custom tokens. But I'm sure that will be a whole load of even more complicated code.
I'm using NextAuth's EmailProvider and FaunaAdapter with a FaunaDB instance to authenticate users in my NextJS application. I'm going down the "passwordless" route with this site, and the magic links are setup nicely after finishing the bulk of this tutorial.
There are some changes I've made to it, however, as I don't want a Sign Up page, and instead want only whitelisted emails that exist in my DB to be sent links to authenticate. I've done this by hijacking the signIn callback like so:
async signIn ({ user, account, profile, email, credentials }) {
// As "signIn" gets called twice when using magic links (once for sending and once for authenticating),
// we need to check here if this is a request for a magic link, as opposed to authenticating a token.
if (email.verificationRequest) {
// This is a helper function which does a lookup for the email in the "accounts"
// table of my Fauna DB.
return checkUserEmailExistsInAccounts(account.userId)
}
return true
}
This works nicely, but I wonder if it could be improved. I want any page that is behind the authentication wall to effectively have a session or JWT token that describes the user in more detail. If, say, I want the user name to be added to the session data, I then need to somehow modify the session or jwt callbacks to add this data. But that will involve yet another call to the database, where I have already made a perfectly useful call in the signIn process. Whats more, these callbacks are made frequently - whereas the signIn callback is only made a couple of times during the authentication process.
To summarise: how can I leverage a magic link sign-in system with NextAuth where I look up allowed emails from a pre-existing Fauna accounts collection, furnish a user object with the account data and have the account data become available to every page without needing further lookups when already-authenticated?
Edit: Just a note to say that when I first started playing with the library, I was surprised that simply putting the data into my accounts collection in Fauna DB didn't somehow magically make it appear in the session info. Perhaps I am not using the Fauna Adapter correctly, and this is actually the better way to go?
Im using Flutter and Firebase. I am trying to figure out if there is a 'built in' way to detect when a Firebase account has been disabled, so that the Flutter app can react and sign out that user if they are logged in already?
I could accomplish this task by adding a 'isDisabled' property to the users document since I already listen for changes to that doc and if it becomes 'true' then log them out. This would require that two changes are made, the 'isDisabled' is set on user doc and account is marked disabled under Authentication.
It just seemed like there might be a more direct way to accomplish this task.
There is not really a more direct way. Firebase Auth is not "realtime". When an account is disabled, the SDK does not know about it immediately. In fact, the user's auth token will stay valid for up to another hour after the time it was disabled. When the token finally expires, the SDK will no long be able to refresh it, and the user will become signed out. Your code will then see that the user is signed out, and they will not be able to sign in again.
The documentation of Firebase Auth for iOS regarding the addAuthStateDidChangeListener function states:
Registers a block as an "auth state did change" listener. To be invoked when:
The block is registered as a listener,
A user with a different UID from the current user has signed in, or
The current user has signed out.
The block is invoked immediately after adding it according to it’s standard invocation semantics, asynchronously on the main thread.
The first point above and the last paragraph confuse me. If I add a listener immediately after having initialized Firebase, is it possible for the listener to be called before Firebase Auth have restored the user from a previous session?
In such case, how can I distinguish if the call of the listener with a null user occurs because there is no user or because the user has not been restored yet?
It is indeed possible for the auth state listener to be called before the authentication state has been restored, in which case the listener will be called with a "user is not signed in" state initially, before being called with a "user is signed in".
The Android SDK these days actually hides that first auth state, but I don't think the iOS SDK does the same. You might want to give it a try though. Simple sign in to the app, close the app, wait for at least an hour (so that your access token has expired) and start the app again.
I'm not sure how most developers handle this, but I know of these two ways:
Make the redirect to the sign-in page explicit. So if you get an auth state without a user, show a link to the sign-in page. In the scenario with the initial "not signed in" that means the user will see this page for a moment, until the sign-in state is restored.
Make the redirect wait for the refresh. This is essentially just a time-out with something like "detecting sign-in state" or something similar. You might want to include a explicit redirect link in there too, for users who know they won't be signed in.
I agree that neither if these is ideal in a mobile app, so I am curious to see what others do.
I have a web site in google cloud. I use Identity-Aware Proxy (IAP) to protect it.
When a request comes in IAP checks if I'm authorized and then responds with either
if I'm authorized: the response from the resource I requested
if not: a 302 or 401 response depending on if it thinks it is an ajax call or not
Now I want to use Google Sign-In for Websites on top of this.
This might seem redundant since I am already logged in by the time I would see the buttons but I want to use it as a way to log out or change user account.
Now what I have tried to do is:
var auth2 = gapi.auth2.init({
client_id: 'mykey.apps.googleusercontent.com',
})
auth2.isSignedIn.get()
// false, I was hoping that I could somehow piggyback on my existing session
auth2.signIn()
// shows a pop up, allowing me to select an account
auth2.isSignedIn.get()
// true
currentUser.getBasicProfile()
// works
This seems to work except it shows me a login box even when I'm already logged in which is suspicious.
Add to this that when I do
auth2.disconnect()
auth2.isSignedIn.get()
// false
and then do a full refresh then I still get my resources from IAP proving that I'm still logged in there.
Questions:
How can I get this to work?
Is this even the correct way to do it?
I don't know much about Sign-In for Websites, but there's another option that might work for you. Have you considered adding a link to /_gcp_iap/clear_login_cookie to provide a "change user account" option? (It's documented here: https://cloud.google.com/iap/docs/special-urls-howto )
If the user is logged in with multiple Google accounts, that will bounce them back to the account picker. Today, if they're only logged in with one account, it will just send them right back into the application -- but there's an enhancement rolling out next week which will display the account picker even for the single-account case, giving the user a chance to sign in with an additional account.
Hope that helps!
--Matthew, IAP engineering lead