SPA + API: Angular 2 Implementation for the SPA
This document is part of the SPA + API Architecture Scenario and it explains how to implement the SPA in Angular 2. Please refer to the scenario for information on the implemented solution.
Your application will require certain configuration information. Before carrying on with the rest of the implementation, create an
AuthConfig interface which will contain various configuration values. Place this interface in a file called
The best way to manage and coordinate the tasks necessary for user authentication is to create a reusable service. With the service in place, you'll be able to call its methods throughout your application. An instance of the
WebAuth object from auth0.js can be created in the service.
The service includes several methods for handling authentication.
- login: calls
authorizefrom auth0.js which initiates Universal Login
- handleAuthentication: looks for an authentication result in the URL hash and processes it with the
parseHashmethod from auth0.js
- setSession: sets the user's Access Token, ID Token, and a time at which the Access Token will expire
- logout: removes the user's tokens from browser storage
- isAuthenticated: checks whether the expiry time for the Access Token has passed
Process the Authentication Result
When a user authenticates via Universal Login and is then redirected back to your application, their authentication information will be contained in a URL hash fragment. The
handleAuthentication method in the
AuthService is responsibile for processing the hash.
handleAuthentication in your app's root component so that the authentication hash fragment can be processed when the app first loads after the user is redirected back to it.
Add the Callback Component
Using Universal Login means that users are taken away from your application to a page hosted by Auth0. After they successfully authenticate, they are returned to your application where a client-side session is set for them.
You can choose to have users return to any URL in your application that you like; however, it is recommended that you create a dedicated callback route to serve as a central location that the user will be returned to upon successful authentication. Having a single callback route is beneficial for two main reasons:
- It prevents the need to whitelist multiple (and sometimes unknown) callback URLs
- It serves as a place to show a loading indicator while your application sets the user's client-side session
Create a component named
CallbackComponent and populate it with a loading indicator.
After authentication, users will be taken to the
/callback route for a brief time where they will be shown a loading indicator. During this time, their client-side session will be set, after which they will be redirected to the
3. Get the User Profile
Extract info from the token
This section shows how to retrieve the user info using the Access Token and the /userinfo endpoint. Alternatively, you can just decode the ID Token using a library (make sure you validate it first). The output will be the same. If you need additional user information consider using the our Management API.
To obtain the user's profile, update the existing
AuthService class. Add a
getProfile function which will extract the user's Access Token from local storage, and then pass that call the
userInfo function to retrieve the user's information.
You can now simply call this function from any service where you want to retrieve and display information about the user.
For example you may choose to create a new component to display the user's profile information:
The template for this component looks as follows:
4. Display UI Elements Conditionally Based on Scope
During the authorization process we already stored the actual scopes which a user was granted in the local storage. If the
scope returned in the
authResult is not empty, it means that a user was issued a different set of scopes than what was initially requested, and we should therefore use
authResult.scope to determine the scopes granted to the user.
scope returned in
authResult is issued is empty, it means the user was granted all the scopes that were requested, and we can therefore use the requested scopes to determine the scopes granted to the user.
Here is the code we wrote earlier for the
setSession function that does that check:
Next we need to add a function to the
AuthService class which we can call to determine if a user was granted a specific scope:
You can call this method to determine whether we should display a specific UI element, or not. As an example we only want to display the Approve Timesheets link if the user has the
approve:timesheets scope. Note in the code below that we added a call to the
userHasScopes function to determine whether that link should be displayed or not.
Protect a route
We should also protect a route to not allow a route to be navigated to if a user has not been granted the correct scopes. For this we can add a new
ScopeGuardService service class:
And then use that when we configure the routes to determine whether a route can be activated. Notice the use of the new
ScopeGuardService in the definition for the
approval route below:
5. Call the API
The angular2-jwt module can be used to automatically attach JSON Web Tokens to requests made to your API. It does this by providing an
AuthHttp class which is a wrapper over Angular's
Create a factory function with some configuration values for
angular2-jwt and add it to the
providers array in your application's
@NgModule. The factory function should have a
tokenGetter function which fetches the
access_token from local storage.
angular2-jwt is configured, you can use the
AuthHttp class to make secure calls to your API from anywhere in the application. To do so, inject
AuthHttp in any component or service where it is needed and use it just as you would use Angular's regular
6. Renew the Access Token
Renewing the user's Access Token requires to update the Angular SPA. Add a method to the
AuthService which calls the
checkSession method from auth0.js. If the renewal is successful, use the existing
setSession method to set the new tokens in local storage.
AuthService class, add a method called
scheduleRenewal to set up a time at which authentication should be silently renewed. In the sample below this is set up to happen 30 seconds before the actual token expires. Also add a method called
unscheduleRenewal which will unsubscribe from the Observable.
Finally you need to initiate the schedule renewal. This can be done by calling
scheduleRenewal inside your
AppComponent which will happen when the page is loaded. This will occur after every authentication flow, either when the user explicitly logs in, or when the silent authentication happens.