Trying to automatically sign in using Parse


#1

Hi!

I’ve been writing an app for college, and got stuck with user login based on Parse. I’m able to detect if an user is saved, but for some reason I can’t redirect the app to a specific state outside the login page.

app.js:

angular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider

  .state('login', {
  url: '/login',
  templateUrl: 'templates/login.html',
  controller: 'LoginCtrl'
})

.state('tab', {
  url: "/tab",
  abstract: true,
  templateUrl: "templates/tabs.html"
})

.state('tab.patients', {
    url: '/patients',
    views: {
      'tab-patients': {
        templateUrl: 'templates/tab-patients.html',
        controller: 'PatientsCtrl'
      }
    }
  })
  .state('tab.patient-detail', {
    url: '/patients/:patientId',
    views: {
      'tab-patients': {
        templateUrl: 'templates/patient-detail.html',
        controller: 'PatientDetailCtrl'
      }
    }
  })

.state('tab.agenda', {
  url: '/agenda',
  views: {
    'tab-agenda': {
      templateUrl: 'templates/tab-agenda.html',
      controller: 'AgendaCtrl'
    }
  }
});
$urlRouterProvider.otherwise('/login');
})
.run(function ($state, $ionicPlatform) {
Parse.initialize("appID", "jsKey");
  var currentUser = Parse.User.current();
  if (currentUser) {
    $state.go('tab.agenda');
}
});

The code executes fine without any errors, but the

$state.go('tab.agenda');

Just doesn’t do anything.

Any help would be appreciated!


#2

Is it throwing errors?


#3

No errors, and a console.log shows me that currentUser is indeed detected.


#4

Very hard to tell just from this snippet of info. Could you supply the rest of the app code (without the production Parse keys of course, I have those already myself), so also the html files and so on, either via Github or Codepen or a ZIP file or whatever?

By the way, are you running this on Firefox or on Chrome (using “ionic serve”) or on a real device (iOS or Android) ?

I’ve implemented Parse signup and login but I also ran into a few obscure problems before I got it running reliably.

Note that ‘Parse.User.current()’ is just some kind of authentication token that’s stored in your LocalStorage.

But in principle that’s is okay, this way you know there is an authenticated user without actually performing a network call to Parse (which comes with its own pitfalls, you need to configure whitelisting and/or a proxy and/or “Content-Security-Policy”).


#5

Sure, I put the code in GDrive

https://drive.google.com/file/d/0B_Y-TqlPhrDQYXQteWxqb282dWM/view?usp=sharing

And to answer your question, I’m running in Chrome using the command: ionic serve --lab, but I also made the apk for android (my target OS as of now), and same results.

Something else I should point out: I created the users using the Parse dashboard, not the app itself (because my use cases don’t need a signup form), maybe that’s a problem?

I’ve read about Parse.User.current() and Ionic docs suggests using it, so maybe I should save the data locally as you mentioned?

Thank you!


#6

Okay, found it! So what is the problem?

In your app.js you’ve added this in your ‘config’ ($stateProvider) function:

$urlRouterProvider.otherwise(’/login’);

Then in your ‘run’ function you’ve coded this:

if (currentUser) {
$state.go(‘tab.agenda’);
}

However: this doesn’t work because “$urlRouterProvider.otherwise(’…’)” overrides (takes precendence over) “$state.go(…)” in your run function. It will ignore the initial ‘$state.go’ if there’s a route override.

So, the solution is to remove the line “$urlRouterProvider.otherwise(’/login’)” and then change your run function like this:

if (currentUser) {
  $state.go('tab.agenda');
} else {
  $state.go('login');
}

Then it works perfectly.

By the way, nice app!


#7

Oh crap, I had no idea about that override!, I should’ve payed more attention to the docs then. Haha, the app looks horrible and the code is messy since I’m barely starting to code in JS, but thank you!

Does this forum has a ‘make this post the answer’ thingy?

Thanks again.


#8

Although this approach worked, I would recommend you take a look at using the resolve functionality of ui-router to manage the login state of the user.

It will give you a better structure and architecture as your application grows and the requirements change.

Starter Ionic Application Template w/Parse Integration - http://bit.ly/1BoN1am


#9

Well, I tried UI-resolve but had a bunch of problems with it.

E.g. is it possible to show a loading indicator while the promise is being resolved? Also it was messing up my view navigation/history for whatever reason. But admittedly I was a beginner in Ionic and Angular when I tried it.

Anyway, I see that opinions are divided on it:

For now I just encapsulate all my functionality in services and factories and resolve stuff in the “init” of my controllers.

But I do want try the route resolve thing again because it does have advantages.


#10

the link i provided is a complete working example for you to review.

the issues they were discussing were related to loading data before a view, we are using the resolve to determine if you should render the view… completely different use cases


#11

That template looks nice, would’ve saved me a lot of work, thank you.

As with ui-router, it does seem more scalable, I’ll look into it.


#12

Okay thanks! Yes I understand it now.

My use case was indeed related to loading remote data.

When implementing my Parse integration I did coincidentally look at your code on Github and used quite a lot of it.

I didn’t use the UI-router/resolve technique to enforce that the user is logged in but that’s a thing I definitely want to add.

Good stuff.


#13

hey we all learn from each other and from helping others, that’s what makes this a great comminity @leob @carlosandrex


#14

Absolutely, we wouldn’t get very far without helping and sharing.


#15

Well I implemented your technique with the route resolve but I had to change 2 or 3 things to get it working properly in my situation:

  1. I’ve removed my "$urlRouterProvider.otherwise()’.
    Apparently it wasn’t needed and it seemed to get in the way.
    (I’m determining my app start page programmatically based on where my user is in the sign-on flow).

  2. The previous item (getting rid of the ‘otherwise’) is probably just my app’s peculiarity but this one is more interesting.

    I had to add an ‘event.preventDefault’ in the ‘$stateChangeError’, to keep it from loading my login page TWICE. So I had to change it like this:

    // if the error is "noUser" then go to login state
    if (error && error.error === "noUser") {
      event.preventDefault();   // <<== ADDED THIS
      $state.go('login');
    }
    

    This is also mentioned here:

    http://stackoverflow.com/questions/22936865/handling-error-in-ui-routers-resolve-function-aka-statechangeerror-passing-d

  3. To implement your technique, I added a new abstract state ‘auth’.

    However I already had an abstract state ‘app’ which manages the main menu (side menu).

    The new ‘auth’ state goes below the ‘app’ state with a dummy (empty) ion-view like this:

    // all children of ‘app.auth’ need a valid user that has been fetched successfully
    .state(‘app.auth’, {
    url: “/auth”,
    abstract: true,
    template: ‘’,
    resolve: {
    user: function (UserService) {
    return UserService.checkUser();
    }
    }
    })

    Now my pages stopped displaying because apparently it couldn’t find the menu nav view anymore in the child states.

    To fix this, in the child routes I had to specify in which abstract state the menu view is defined using ‘@’ notation:

    views: {
    ‘menuContent@app’: { // <<== UI-router 'absolute view naming’
    templateUrl: …,
    controller: …
    }
    }


#16

Not sure what version of my code you are using, some of changes you mentioned were implemented already? Also If you look at the issues and comments, you will see that others have successfully utilized the starter-template with no issues at all.

PR from over a month ago

I would also question your perspective that there are “Issues” in the repo, you just chose to implement your authentication in a different manner that did not match my implementation.

I would also suggest that github has an issues ability where PRs can be submitted and accepted, which you can also see i respond to and accept. I put this code out to help the community, and adding all of this information here instead of the repo defeats the purpose of open sourcing it.

I would encourage you to take another look at the source code and move your comments/feedback to the repo so others can get the same benefits that you have received.


#17

Yes that’s it, I used a 2 or 3 months old version of your repo, so it didn’t have the “event.preventDefault()” fix yet.

P.S. I didn’t mean to say there were issues in the repo or whatever, apologies if it came across like that, probably my clumsy wording.


#18

Well, I’ve discovered a new problem, and although I haven’t tested it with ui-router, it apparently is something a lot of people don’t know how to solve yet

The user object is saved as long as the user doesn’t dismiss the app from the multitasking menu, and this is expected since Android works like that. However this makes this solution impractical. But this is a Parse issue, not Ionic’s.

According to what I’ve been reading, the solution is to use a session key, but Parse documentation isn’t clear about how to retrieve they key when I don’t have a user, as it is in this case.

If someone has tested this, it would be nice to know how to solve it, but as it is I think this thread has all the Ionic-related answers we need!


#19

@carlosandrex think you should post a more specific question since it sounds like you have a specific use case to reproduce the error?

I am not following exactly what the issue is here? If you have a reference to “a lot of people don’t know how to solve yet” that would help to make trying to resolve this a bit easier


#20

Well, the error is in line of what is said here:

https://disqus.com/home/discussion/parse-blog/bring_your_own_login_parse_blog/

Probably I just explained it badly (english isn’t my first language, sorry). As we have discussed, the idea is to keep the user logged in every time he launches the app, and only show the login form when he logged out or when he is launching the app for the first time.

The solution provided by leob works when the user doesn’t close the app completely, but if the user removes the app from the multitasking menu or reboots the phone, the Parse.User.current() object is deleted, making the app go to the login view again.

So a solution to the problem would be to use the session key provided by Parse, but as the comments in the link show, there is no clear way to access the key without having an user object beforehand.