I am trying to obtain the last active url prior to logging a user out of my Angular 2 app, so that when they log back in it will redirect them to that component/page. The thing is, while I'm able to obtain the active url with this.router.routerState.snapshot['url'] -- this always provides me with the result /login -- which is the url they're re-directed to right AFTER logout takes place. What I need to do is find a way to get the url JUST PRIOR to logout happening. I tried doing this in my logout function in my authenticationService:
logout()
{
let activeUrl = this.router.routerState.snapshot['url'];
console.log(activeUrl);
this.apiService.logout();
// remove user from local storage to log user out
sessionStorage.removeItem('currentUser');
console.log('User successfully logged out');
// Trigger event
this.change.emit(this);
}
But even though the activeUrl is being console.logged before this.apiService.logout() is called (see order of operations in above function), I still end up getting /login logged to the console. Is this to be expected? How can I get around this?
EDIT: After some reflection it occurs to me now that the problem may be that I'm calling the logout() function from within the ngOnInit life cycle hook of my login component - so at that point '/login' is going to be the result regardless:
ngOnInit()
{
this.authenticationService.getActiveUrl();
this.authenticationService.logout();
}
So the pertinent question becomes: how do I capture the active URL just prior to being logged out?
In the Angular documentation it states that RouterState contains RouterStateSnapshot so to me it seems that the toString() is called when you log it to console but you still keep a reference in your activeUrl which may change.
Try:
let activeUrl = this.router.routerState.snapshot['url'].toString();
Related
so if a user logs into your app, you can check that by
firebase.auth().onAuthStateChanged((user)=>{});
but if the user is already logged in and his user has a property change, how do you see that?
in my case, I have the user verify his email address and when done, he should be able to see a change instantly on his app after verifying his email. So I am using react native, which is pretty much javascript with ES6 syntax in it and I am doing a firebase.auth().onAuthStateChanged(); but its not working, I even have a button on the screen that checks if verified like this:
if (!firebase.auth().currentUser.emailVerified) { firebase.auth().currentUser.sendEmailVerification(); }
else if (firebase.auth().currentUser.emailVerified) { this.setState({ showVerifier: false }); }
but even that isn't working, as if the firebase.auth().currentUser doesn't update if the email is verified, what can be done here?
As far as I understand your question, I would like to give you an idea.
I think onAuthStateChanged() gets triggered only when your Auth State Changes (login, logout) and not when the user properties change.
As they have mentioned in the documentation,
Adds an observer for changes to the user's sign-in state. Prior to
4.0.0, this triggered the observer when users were signed in, signed out, or when the user's ID token changed in situations such as token
expiry or password change. After 4.0.0, the observer is only triggered
on sign-in or sign-out.
function isVerified(){
var user = firebase.auth().currentUser;
if(user != null){
var status = user.emailVerified;
if(status)
{
// Verified
}else{
// Not Verified
}
}
else{
// User didn't login!
}
}
So, you have to manually check it by defining a function like above and you can call this function when the user clicks the button
If you are using react-native-firebase (highly recommended, since it is supports the latest firebase features), you can listen on user changes as stated in this doc
From the doc
Adds a listener to observe changes to the User object. This is a superset of everything from auth#onAuthStateChanged, auth#onIdTokenChanged and user changes. The goal of this method is to provide easier listening to all user changes, such as when credentials are linked and unlinked, without manually having to call User#reload.
onUserChanged(listener: AuthListenerCallback): () => void;
I have a fairly sparse "register.html" page where I read in user email and password and register via createUserWithEmailAndPassword. I have a .then() method attached, which does NOT get called. I also have a .catch() method attached, which DOES get called with an error that user is not defined.
First, user is not defined seems more appropriate for login, rather than registration. The whole point is to "define a new user!"
Secondly, I also have .onAuthStateChanged listener and that DOES show that the user status changes to logged in after registration.
Third, going to the firebase auth panel, I do see the newly created user!
Why is my catch block being called, and not the then block?
Here is my code:
auth.createUserWithEmailAndPassword(username, password)
.then(function(){
//user signed in
console.log('auth', user, 'registered');
if(location.href == document.referrer){
location.href = "/website/index.html";
}
else{ location.href = document.referrer; }
})
.catch(function(error) {
var errorCode = error.code;
var errorMessage = error.message;
$('#passwordAlert').show();
$('#passwordError').text(errorMessage);
console.log("Registeration failure:", username);
console.log('\t',errorCode);
console.log('\t',errorMessage);
//alert user
});
In my experience when trying to answer this question Firebase (web) - add user data upon signup
The .then is not called because createUserWithEmailAndPassword
Creates a new user account associated with the specified email address and password.
On successful creation of the user account, this user will also be signed in to your application.
So "this user will also be signed in to your application". It will trigger the .onAuthStateChanged and for idk reason it just make .then won't be called anymore.
The .catch is called because onAuthStateChanged is not triggered, no sign-in or sign-out is occured
After 4.0.0, the observer is only triggered on sign-in or sign-out.
I tried to add a bit more logging, as someone requested. However, I'm now getting results I expect. I know I didn't change any logic, only console.log statements. However, the problem is now resolved. Not sure if this was a temporary glitch in firebase (probably unlikely) or an error on my part (although I didn't post the message until spending a couple of hours looking at my 177 line html/js page, looking through docs, etc.).
In any case, problem resolved.
In my Angular 2 app I am trying to store the last active URL prior to a user logging out, so that that url can be re-loaded after the user re-logs in. However, this is proving problematic. Consider this logout function from my authenticationService:
logout()
{
let lastUrl = this.getActiveUrl();
console.log('Url before logout: ', lastUrl);
this.apiService.logout();
}
Notice here that "lastUrl", which calls this.getActiveUrl(), which looks like this:
getActiveUrl()
{
let activeUrl = this.router.routerState.snapshot['url'];
console.log('activeUrl: ', activeUrl);
return activeUrl;
}
...appears BEFORE this.apiService.logout(). But, nevertheless, what gets printed to the console for "lastUrl" is "/login". But that's the URL where I end up immediately after logging out.
So, help me understand this:
If this is synchronous, why doesn't the correct URL print here? What am I missing? And how can I get the active url immediately prior to logout firing and re-directing to '/login'?
EDIT: After a commenter's suggestion, I tried assigning to localStorage instead of a local variable, like so:
logout()
{
localStorage.setItem('returnUrl', JSON.stringify(this.router.routerState.snapshot['url']));
this.apiService.logout();
}
But when I dig that value out with localStorage.returnUrl, I still get "/login".
First off, many thanks to #Sam for the localStorage suggestion. I should have thought of that. So, in the end, all I needed to do was make use of RouterStateSnapshot from my canActivate() function in my AuthGuardService, and save that value to localStorage. Then I just retrieve that value to plugin in on re-authentication and re-login:
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
{
// Get route content id
let contentId = Object.getPropertyValueAtPath(route, 'data.contentId');
// Store last active URL prior to logout, so user can be redirected on re-login
localStorage.setItem('returnUrl', JSON.stringify(state.url));
// DO OTHER STUFF...
}
In my login component I just get that value to pass in...
login(response)
{
this.loading = true;
this.authenticationService.login(this.model.username, this.model.password, function (results)
{
if (results.data && results.ok === true)
{
this.returnUrl = JSON.parse(localStorage.getItem('returnUrl'));
this.router.navigate([this.returnUrl || '/']);
console.log('ReturnURL Value is: ', this.returnUrl);
this.reset();
}
else
{
this.alertService.error(null, response);
this.loading = false;
}
}.bind(this));
}
It is happening synchronously. However, you are logging an object pointer. By the time you look at the object in the console, it has changed because the route has changed.
I suggest using local storage to store the router snapshot. This will not have the same pointer issue that you see in the console.
I'm struggling to initialize my Vuex store with the account details of the logged in user from localStorage.
I've been through as many examples as I can of Auth using Nuxt, and none of them demonstrate how on the client side to pull an authToken from localStorage to re-use with subsequent API requests when the user refreshes the page or navigates to the App
Disclaimer: I'm not using Nuxt in SSR (this might affect your answer).
What is annoying is that I can actually load from localStorage and initialize my state but then it gets overwritten. I'll show you what I mean with this small code example:
buildInitialState () {
if (!process.server) {
// Query the localStorage and return accessToken, refreshToken and account details
return {accessToken: <valid-string>, refreshToken: <valid-string>, account: <valid-json-blob>}
} else {
// Return "empty map", we use the string "INVALID" for demonstration purposes.
return {accessToken: "INVALID", refreshToken: "INVALID", account: "INVALID"}
}
}
export const state = () => buildInitialState()
If I put a breakpoint on buildInitialState I can see that I correctly initialize the state with the value of localStorage, i.e. I get the accessToken and refreshToken, etc.. back.
All seems well.
But then .....in another part of the program I'm using Axois to send requests, and I use an axios interceptor to decorate the request with the accessToken. To do this I have to stick it into a plugin to get access to the store.
Something like so:
export default ({ app, store }) => {
axios.interceptors.request.use((config) => {
const accessToken = _.get(store.state, ['account', 'accessToken'])
if (accessToken) {
config.headers.common['x-auth-token'] = accessToken
}
return config
}, (error) => Promise.reject(error))
}
Here the store is closed over in the arrow function supplied to axios so when I go to send the request it sees if there is a valid accessToken, and if so then use it.
BUT, and here's the kicker, when a request is made, I look at the store.state.account.accessToken and low and behold its been reinitialized back to the value of "INVALID".
What? Que?
It's almost like the store was reinitialized behind the scenes? Or somehow the state in the plugin is "server side state"?? because if I try and log buildInitialState I don't get any messages indicating that the path that produced a map with INVALID is being run.
Basically, I don't thoroughly understand the initialization pathway Nuxt is taking here at all.
If any Nuxt masters could help me out understand this a bit more that would be great, it's turning into a bit of a show stopper for me.
Essentially! All I want to be able to do is save the user so that when they refresh their page we can keep on running without forcing them to re-login again....I thought that would be simple.
Thanks and regards, Jason.
I've solved this with a bit of experimentation and comments from other posters around what is called SSR and SPA.
Firstly, this https://github.com/nuxt/nuxt.js/issues/1500 thread really helped me and the final comment from #jsonberry steered my mind in the right direction, away from fetch and asyncData.
I finally had a bit more of an understanding of how NUXT.js was separating SSR and SPA calls.
I then tried #robyedlin suggestion of putting localStorage initialization in the created() method for my main layout/default.vue page.
While I made progress with that suggestion it turns out created() is also called SSR and I was still trying to initialize my store from credentials that weren't accessible.
Finally, moving the initialization to mounted() did the trick!
So in summary:
My account store is left alone, I don't try and initialize it when it is created (it's just overwritten at some point when the SSR stuff runs)
On mounted() in layout/defualt.vue I read from localStorage and initialize the account store so I can start making API requests with the appropriate accessToken.
That seems to have done the trick.
I am trying to do 3 simple things using JS thru a client side authorization flow :
Check if the user is logged in. If not, display the login dialog with proper scope.
If user is already logged into Facebook on the browser, then ensure the app has been authorized (if not, display the Authorize dialog).
If user has already authorized the app then ensure that the access_token on the authResponse has the right scope (permissions) required by the app. (if not, redisplay the authorize dialog for the new permissions).
I am trying to leverage js sdk for this, without wanting to write any hand-coded dialog's etc, since the sdk handles the browsers/devices nuances automagically.
I could not find this properly described anywhere either on FB documentation or otherwise. (no documentation around the fact that the permissions/scope for the app can change post authorization).
Thanks you in advance.
After an arduous attempt I have this working and here are the steps to do this with the minimum amount of code.
*The Approach *
Use fb-login-button to facilitate login. Login Button is the only thing that is displayed when the user first arrives on the page. All other forms are 'hidden', forcing the user to click on this button before being able to go forward.
setup onlogin="function();" on the fb-login-button - so that you can hide the login-button and display the rest of the forms/etc for the user to proceed on your app. Please note that onlogin is called only once (either when the user logs in or authorizes the app with the new scope) - so it is safe to always hide the login-button and display the remaining of the page/form for the user to proceed.
The most important step : use the same fb-login-button to also do the authorization with the right scope : so even if the user is logged in - force the user to click the login-button. fb-login-button in this case, will check if the scope of existing access_token (for already logged in fb user), is a subset of that requested on the fb-login-button attribute. fb-login-button will display the authorize dialog for you automatically in this case. In any case the onLogin callback is triggered (when the login-button should be hidden() and the remaining page/forms be shown()).
Subscribe to the FB.events to get the authResponses and the corresponding access_token every time it is set, which happens 2 times :
a. When the fetch is first displayed for already logged in user
b. After the user authorizes the app first time or second time with the new scope.
This allows you to keep the authResponse and access_token fresh to be used for all subsequent graph calls/etc..
*Implementation Steps *
fb-login-button must be created with onlogin callback :
<div class="fb-login-button" onLogin="onLogin();" scope="publish_actions"
data-size="large">Use This App</div>
Please note that the text does not say Login, but 'Use this App', since the button is displayed for authorization in addition to just logging in.
Subscribe to the Events.
// authRepsonse Change is needed when the user hits the UseApp button
// and permissions are re-fetched - then a new token needs to be refreshed
FB.Event.subscribe('auth.authResponseChange', handleResponseChange);
// when the user logins and all permissions are all set, clicking on the
// use-app will not do anything and authResponse will always be null.
// so this subscription is necessary
FB.Event.subscribe('auth.statusChange', handleResponseChange);
Login Function to hide Login button and display form.
function onLogin () {
hideLogin();
showMain();
}
Handle Status Changes to save Token.
// global variable that contains the access_token
// for use in graph calls/etc.
var authResponse = null;
function handleResponseChange(response) {
if (response && response.authResponse) {
authResponse = response.authResponse
if (response.status == 'connected') {
updateUserInfo(response);
return;
}
}
else {
authResponse = null;
}
hideMain();
showLogin();
}
Hope this helps and I believe that this is probably the most optimal way of handling client side authentication flow without doing any extra authorization/login dialog and let FB sdk do the trick.