11

I'm developing an Asp.net MVC + Web API + AngularJS SPA. I would like to have several types of registration/authentication:

  • own profile provider
  • external providers ie Google, FB etc.

Possible scenarios

  1. As I'm having an SPA it would be best if I could keep my user on my page while external (or internal for that matter) would be taking place. I'd display a modal layer with particular content loaded (maybe even inside an iframe). Can this be done? Online examples?

  2. Have login/registration capability implemented as usual Asp.net MVC full page reload controller/views and then redirect back to my SPA when that is successful. Also redirect to external provider if users wanted to authenticate/register using external provider.

  3. Any other possibility?

Questions

  1. How did you do this similar scenario in your SPA or how would you recommend to do it?
  2. Should I be using particular authentication patterns regarding this - for instance provide my internal authentication/registration similar to external one so SAP would always behave in the same way
  3. I will also have to authenticate my Web API calls subsequently after user athenticated themselves in the SPA. Any guidance on that?
Robert Koritnik
  • 103,639
  • 52
  • 277
  • 404

3 Answers3

12

I can only comment on my own experience, maybe it is helpful. We use the same stack as you, Asp.net MVC + Web API + AngularJS. We use server-side MVC for authentication (Microsoft.AspNet.Identity), since we are not exposing a public API at this stage and the only consumer of the API will be our SPA this works perfectly with the least amount of effort.

This also enables us to set a UserContext Angular service on the server once logged in that can be shared through your entire Angular app, the Google Doubleclick Manager guys goes into some of the benefits of this approach during there ng-conf presentation. Since Web Api supports Asp.Net Identity, authentication and authorization works seamlessly between MVC and Web Api.

To sum up the major pros and cons:

Pros:

  1. Very easy and quick to implement.
  2. Works across MVC and Web Api.
  3. Clientside code does not need to be concerned with authentication code.
  4. Set UserContext Angular service on server side once during login, easily shared throughout SPA using Angular DI. See presentation as mentioned above.
  5. Integrates with external providers as easily as you would with any normal MVC app.

Cons:

  1. Since the browser does not send the hash # part of the URL to the server, return URL on login will always be the root of your SPA. E.g. suppose your SPA root is /app, and you try to access /app#/client when you aren't authenticated, you will be redirected to the login page, but the return URL will be /app and not /app#/client as the server has no way to know the hash part of the URL as the browser never sends this.
  2. Not really supported design if you plan to make your your Web Api available outside your SPA. Imagine a console app trying to connect to your API?

So in short, the MVC view that we use to bootstrap our SPA is protected with [Authorize] as well as our Web Api methods. Inside the MVC view we also initialize our UserContext Angular service using Razor to inject whatever user properties we want to expose. Once the SPA is loaded via the single Razor view, everything else is handled via Angular.

Beyers
  • 8,968
  • 43
  • 56
  • How did you handle anonymous access on your SPA then? How did you actually implement login functionality if **everything** is part of your SPA including login? I'm currently implementing Signup/Login as a separate page from the whole SPA. So when one accesses SPA they would be redirected to login/signup and then back to SPA. I think that takes the best of both worlds really. – Robert Koritnik Jan 30 '14 at 08:30
  • @RobertKoritnik I think you misunderstood, or maybe I was unclear. Our login/signup is outside the SPA, just standard MVC controller/view. That's what I meant with "we use server-side MVC for authentication". So it sounds like the same thing you are doing. And yes I agree, best of both worlds. – Beyers Jan 30 '14 at 08:42
  • I thought the other way around as you've been explaining the problem in Cons#1... Ok. So I'm apparently doing the same way as you. But the more problematic thing was the redirection to Google/FB. Are using that? Because that's the main problem I'm trying to solve and asking in this question... – Robert Koritnik Jan 30 '14 at 08:45
  • Since the login/signup pages are just standard MVC pages, it should work the same as any other MVC app using 3rd party providers. What specific problems are you experiencing with these providers? – Beyers Jan 30 '14 at 08:50
  • @Beyers : I don't know I am right or wrong here, but it is worth to clear the doubt. Is it good to mix the razor view and angular templates? Since you use MVC for authentication, subsequent views are rendered as .cshtml or html pages? – anoop Apr 01 '14 at 23:45
  • 1
    @anoop I only use razor view for the index page where the SPA is loaded. All subsequent views are standard html files where my angular templates are defined in. The html files itself does not contain sensitive data, all the data is received via protected Web Api calls, so I'm not concerned about authorizing the html views. Hope that helps. – Beyers Apr 02 '14 at 09:04
  • @Beyers, did you ever run into a problem where external OAuth login was adding a # to the end of your url, causing problems for the AngularJS app when the user is redirected to it? – Chaddeus Apr 14 '14 at 00:17
7

We have used what Beyers described before and it works well for most apps, and I use it frequently.

In our current application we are working on the premise that separation of concern should apply to route management.

Normal lifecycle:

  1. User goes to www.server.com
  2. Server sends down index.html
  3. Client makes request for minified assets (.js, .css., etc.)
  4. Angular loads -- a directive removes the loading class from the body (revealing the login section)
    1. The Angular LoginCtrl makes an autologin attempt. (Login and Autologin in an Angular service).
    2. The server returns a HTTP 401
  5. The login screen remains visible.
  6. User successfully logs in ( server gives the browser a authToken cookie; angular does not know or care)
  7. Angular sets some isAuthenticated variables in the BodyCtrl and LoginCtrl
  8. The login section receives a class of .hidden and the content recieves a class of .visible (insert ng-hide/show animations for fun)
  9. User starts filling out a form, but takes an obligitory, 30 minute phone call from relative.
  10. Server has expired his session 10 minutes ago
  11. User finishes and submits form but the server return unauthorized (401)
  12. http-auth-interceptor intercepts the 401 from the server, caches the submit call and publishes a "login-required' event.
  13. The BodyCtrl listens and sets isAuthenticated = false and then the ng-class and ng-show/hide do there work on the login and content sections.
  14. User re-signs in and 'login-confirmed' event is published
  15. http-auth-interceptor posts cached call.
  16. User is happy
  17. (the content section can also display some public views as our rest api has some routes that are made public -- displaying the public views is handled by a simple function similar to isAuthenticated)

Angular Ctrl structure:

index.html

<body>
    <!-- I am a fullscreen login element, z-index=2000-->
    <div data-ng-controller="LoginCtrl" data-ng-hide="isAuthenticated()"</div>
    <div data-ng-controller="ContentCtrl">
        <!-- fullscreen class has a z-index=2001 -->
        <section data-ng-view data-ng-class="{fullscreen: isViewPublic()}"></section>
        <!-- header and nav go here -->
    </div>
</body>

We could get a little more creative on how display the public views/routes but you get the idea. We only have a few public routes and they are mainly for registration, password resets, etc.

Disclaimer: I have yet to integrate with and oauth/external authentication services. Hopefully this setup will still hold water.

Any critique of this process is welcome.

Cory Silva
  • 2,761
  • 17
  • 23
  • +1 for the link to http-auth-interceptor. Your approach looks decent. – Beyers Jan 30 '14 at 21:05
  • In 4.1. is that an Angular login controller or Asp.net MVC server side login controller? Could you also explain the *auto login* a bit more? – Robert Koritnik Jan 31 '14 at 09:58
  • Sorry about the ambiguity: It is an Angular Controller – Cory Silva Feb 01 '14 at 00:23
  • Our Autologin (angular service) just hits an endpoint and the server determines whether the session is still valid and returns a chunk of "app data" that will then be used to do some client side business logic – Cory Silva Feb 01 '14 at 00:30
4

By no means am I familiar with Microsoft backends, but still I'll give it a try ;-) :

Good resources on how the authentication/authorisation should be done in Angular-based SPA:

  • https://github.com/fnakstad/angular-client-side-auth
    live demo: http://angular-client-side-auth.herokuapp.com/login

    • As you requested there are 2 methods of authenticating:
      • own profile
      • external providers.
        It redirects to the provider website though :-/
    • NodeJS on the backend
  • Good ng-conf talk on how authorisation is done in Google Doubleclick Manager application: http://www.youtube.com/watch?v=62RvRQuMVyg&t=2m29s
    It's not quite what you want (authentication), but the solution begins to kick in on the authentication phase. Furthermore it may be useful later and the approach Ido is presenting seems really sound.
    Slides: https://docs.google.com/file/d/0B4F6Csor-S1cNThqekp4NUZCSmc/edit

  • Last but not least: Mastering Web Application Development with AngularJS.
    A brilliant Angular book by Paweł Kozłowski and Pete Bacon Darwin.
    It has a whole chapter or two dedicated to auth- stuff. It shows some complex solutions, such as retrial and session-expired interceptors. But even if you will not use approaches from the book directly, those chapters are still a must-reads since they may give you an inspiration for devising your own auth- solutions.

    Remark - http-auth-interceptor: As it is mentioned in the book, the securityInterceptor solution was originally invented by Witold Szczerba. See the blog post.
    http-auth-interceptor code, mentioned by @CorySilva, is actually sample code to concepts explained in the post.

    btw: Those 2 chapters are great, but I hope that the Community comes up with some easier solutions in the future. Every time I read this interceptor promise api-based code I get a severe headache :)

    btw2: If somebody doesn't consider oneself an Angular expert, the entire book is definetly a must-read and great complement after reading the Guide

As for building the login page with ASP - I suggest using ASP only as a backend and middleware and drawing whole the app with Angular.
You can start with your approach and switch to the pure-Angular SPA if it will begin to require more and more crazy hacks to make technologies play together nicely.
But I might be wrong and this particular case won't require applying any hacks.

Community
  • 1
  • 1
vucalur
  • 6,007
  • 3
  • 29
  • 46