Heads up… this post is old!
For an updated version of this post, see Angular Authentication with OpenID Connect and Okta in 20 Minutes on the Okta developer blog.
Today I’m happy to announce the first (beta) release of Stormpath’s Angular 2 support! The npm module is called angular-stormpath
and you can easily installing it using npm install --save angular-stormpath
. If you’d like to try Angular 2 with Stormpath without writing any code, you can checkout the project from GitHub and run its demo. You will need to have your Stormpath API key setup for this to work.
1 2 3 4 |
git clone https://github.com/stormpath/stormpath-sdk-angular.git cd stormpath-sdk-angular npm install npm start |
If you’d like to learn how to integrate our Angular 2 components into your own application, continue reading!
What Is Stormpath?
Stormpath is an API service that allows developers to create, edit, and securely store user accounts and user account data, and connect them with one or multiple applications. We make user account management a lot easier, more secure, and infinitely scalable. To get started register for a free account.
Create an Angular 2 Application with Express
To see how you might use this in a simple Angular 2 application, create a new application with Angular CLI. First, you’ll need to install Angular CLI.
1 |
npm install -g angular-cli |
After this command completes, you can create a new application.
1 |
ng new angular2-express-stormpath-example |
The reason I included “express” in the project name is because Stormpath currently requires one of our backend integrations to communicate with Stormpath’s API. For this example, you’ll use express-stormpath.
From the command line, cd
into angular2-express-stormpath-example
and run ng serve
. In another terminal window, run ng e2e
to run Protractor tests. All tests should pass and you should see results like the following.
Integrate Stormpath’s Angular 2 Support
Add angular-stormpath
to the project:
1 |
npm install angular-stormpath --save |
In src/app/app.component.html
, add HTML that shows a welcome message to the user when they’re logged in. When they’re not logged in, the <sp-authport></sp-authport>
component will render forms to register, login, and retrieve forgotten passwords.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<h1>{{title}}</h1> <div *ngIf="(user$ | async)" class="row text-center"> <h2 class=""> Welcome, ({{ ( user$ | async ).fullName }}). </h2> <hr/> <h4>What would you like to do?</h4> <ul class="nav nav-pills nav-stacked text-centered"> <li role="presentation" (click)="logout()"><a href="#">Logout</a></li> </ul> </div> <sp-authport></sp-authport> |
In src/app/app.component.ts
, add the following variables, constructor, and methods to the body of AppComponent
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
import { Stormpath, Account } from 'angular-stormpath'; import { Observable } from 'rxjs'; ... export class AppComponent { title = 'app works!'; private user$: Observable<Account | boolean>; private loggedIn$: Observable<boolean>; private login: boolean; private register: boolean; constructor(public stormpath: Stormpath) { } ngOnInit() { this.login = true; this.register = false; this.user$ = this.stormpath.user$; this.loggedIn$ = this.user$.map(user => !!user); } showLogin() { this.login = !(this.register = false); } showRegister() { this.register = !(this.login = false); } logout() { this.stormpath.logout(); } } |
If you run npm start
and view http://localhost:4200 in your browser, you’ll see “Loading…”, but nothing renders. A quick check of the console will show you errors about sp-authport not being a known element.
This happens because Stormpath’s Angular 2 components haven’t been imported into the application’s module. Open src/app/app.module.ts
and import StormpathModule
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import { AppComponent } from './app.component'; import { StormpathModule } from 'angular-stormpath'; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, FormsModule, HttpModule, StormpathModule ], providers: [], bootstrap: [AppComponent] }) |
Now the app should launch correctly, but you’ll see a 404 in your console for the /me endpoint.
Install Stormpath’s Express Support
To fix this, install express-stormpath
:
1 |
npm install express-stormpath --save-dev |
Create a server
directory and a server.js
file in it. In this file, create an Express application and protect it with Stormpath.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
'use strict'; var express = require('express'); var path = require('path'); var stormpath = require('express-stormpath'); /** * Create the Express application. */ var app = express(); /** * The 'trust proxy' setting is required if you will be deploying your * application to Heroku, or any other environment where you will be behind an * HTTPS proxy. */ app.set('trust proxy', true); /* We need to setup a static file server that can serve the assets for the angular application. We don't need to authenticate those requests, so we setup this server before we initialize Stormpath. */ app.use('/', express.static(path.join(__dirname, '..'), {redirect: false})); app.use(function (req, res, next) { console.log(new Date, req.method, req.url); next(); }); /** * Now we initialize Stormpath, any middleware that is registered after this * point will be protected by Stormpath. */ console.log('Initializing Stormpath'); app.use(stormpath.init(app, { web: { // produces: ['text/html'], spa: { enabled: true, view: path.join(__dirname, '..', 'index.html') }, me: { // enabled: false, expand: { customData: true, groups: true } } } })); /** * Now that our static file server and Stormpath are configured, we let Express * know that any other route that hasn't been defined should load the Angular * application. It then becomes the responsibility of the Angular application * to define all view routes, and redirect to the home page if the URL is not * defined. */ app.route('/*') .get(function (req, res) { res.sendFile(path.join(__dirname, '..', 'index.html')); }); /** * Start the web server. */ app.on('stormpath.ready', function () { console.log('Stormpath Ready'); }); var port = process.env.PORT || 3000; app.listen(port, function () { console.log('Application running at http://localhost:' + port); }); |
This will now service the Stormpath endpoints (e.g. /login, /logout, /me) on port 3000 when it’s started. However, since Angular CLI runs on port 4200, you have to proxy these requests.
Proxy Requests to Express
Create a proxy.conf.json
file in the root directory of the project to contain proxy definitions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
{ "/forgot": { "target": "http://localhost:3000", "secure": false }, "/login": { "target": "http://localhost:3000", "secure": false }, "/logout": { "target": "http://localhost:3000", "secure": false }, "/me": { "target": "http://localhost:3000", "secure": false }, "/register": { "target": "http://localhost:3000", "secure": false } } |
Next, change package.json
to modify npm start
to start express and run ng serve
with proxy support.
1 2 3 4 5 6 7 |
"scripts": { "start": "concurrently --raw \"ng serve --proxy-config proxy.conf.json\" \"node server/server.js stormpath-api\"", "lint": "tslint \"src/**/*.ts\"", "test": "ng test", "pree2e": "webdriver-manager update", "e2e": "protractor" }, |
You’ll need to install concurrently
to make this command work.
1 |
npm install concurrently --save-dev |
Run npm start
and you should be able to navigate between Login, Register, and Forgot Password in your browser. The forms won’t be pretty though. You can make them look good by adding Bootstrap to src/index.html
.
1 |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> |
If you haven’t registered for Stormpath, you’ll see an error like the following when you run npm start
. The Quickstart guide for Stormpath’s Node.js integration explains how to register and create an API key pair.
Fix Tests
If you try to run npm test
or ng test
, tests will fail with the same error you saw before:
1 2 |
'sp-authport' is not a known element: 1. If 'sp-authport' is an Angular component, then verify that it is part of this module. |
The first step to fixing this is to import StormpathModule
into src/app/app.component.spec.ts
.
1 2 3 4 5 6 7 8 |
beforeEach(() => { TestBed.configureTestingModule({ declarations: [ AppComponent ], imports: [StormpathModule] }); }); |
This will get you a bit further, but there will be an error about the /me endpoint not being found.
1 2 |
Chrome 54.0.2840 (Mac OS X 10.12.1) ERROR Uncaught Error: /me endpoint not found, please check server configuration. |
To workaround this, you can override the Angular’s Http
dependency and mock out its backend.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import { StormpathModule, Stormpath } from 'angular-stormpath'; import { BaseRequestOptions, Http, ConnectionBackend } from '@angular/http'; import { MockBackend } from '@angular/http/testing'; ... beforeEach(() => { TestBed.configureTestingModule({ declarations: [AppComponent], imports: [StormpathModule], providers: [ { provide: Http, useFactory: (backend: ConnectionBackend, defaultOptions: BaseRequestOptions) => { return new Http(backend, defaultOptions); }, deps: [MockBackend, BaseRequestOptions] }, {provide: Stormpath, useClass: Stormpath}, {provide: MockBackend, useClass: MockBackend}, {provide: BaseRequestOptions, useClass: BaseRequestOptions} ] }); }); |
After making these changes, you should see the sweet smell of success.
1 |
Chrome 54.0.2840 (Mac OS X 10.12.1): Executed 3 of 3 SUCCESS (0.536 secs / 0.532 secs) |
Protractor tests should still work as well. You can prove this by running npm start
in one terminal and npm run e2e
in another.
Kudos
Thanks to Stormpath’s Robert Damphousse for providing a preview of Angular 2 support and writing most of the code in this release. I’d also like to thank Matt Lewis for his generator-angular2-module. Matt’s library made it easy to create this module and he was a great help in getting tests to work.
Angular 2 + Express Source Code
A completed version of the application created in this blog post is available on GitHub.
I hope you’ve enjoyed this quick tour of our Angular 2 support. If you have any questions about features or our roadmap going forward, please hit me up on Twitter, leave a comment below, or open an issue on GitHub.