Angular 7: What's New and Noteworthy + OIDC Goodness
Angular 7 was released earlier this quarter and I’m pumped about a few of its features. If you’ve been following Angular since Angular 2, you know that upgrading can sometimes be a pain. There was no Angular 3, but upgrading to Angular 4 wasn’t too bad, aside from a bunch of changes in Angular’s testing infrastructure. Angular 4 to Angular 5 was painless, and 5 to 6 only required changes to classes that used RxJS.
Before I dive into showing you how to build an Angular app with authn/authz, let’s take a look at what’s new and noteworthy in this release.
Upgrade to Angular 7
If you created your app with Angular CLI, chances are you can easily upgrade to the latest release using ng update
.
ng update @angular/cli @angular/core
You can also use the Angular Update Guide for complete step-by-step instructions.
What’s New in Angular 7
There are a few notable features in Angular 7, summarized below:
-
CLI prompts: this feature has been added to Schematics so you can prompt the user to make choices when running
ng
commands. -
Performance enhancements: the Angular team found many people were using
reflect-metadata
as a dependency (rather than a dev-only dependency). If you update using the aforementioned methods, this dependency will automatically be moved. Angular 7 also adds bundle budgets so you’ll get warnings when your bundles exceed a particular size. -
Angular Material: Material Design had significant updates in 2018 and Angular Material v7 reflects those updates.
-
Virtual Scrolling: this feature allows you to load/unload parts of a list based on visibility.
-
Drag and Drop: this feature has been added to the CDK of Angular Material.
Bundle budgets is the feature that excites me the most. I see a lot of Angular apps with large bundle sizes. You want your baseline cost to be minimal, so this feature should help. The following defaults are specified in angular.json
when you create a new project with Angular CLI.
"budgets": [{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}]
You can use Chrome’s data saver extension for maximum awareness of the data your app uses. |
For more details on what’s new in Angular 7, see the Angular blog, coverage on InfoQ, or the Angular project’s changelog.
Now that you know how awesome Angular 7 is, let’s take a look at how to create secure applications with it!
Create a Secure Angular 7 Application
An easy way to create Angular 7 apps is using the Angular CLI. To install it, run the following command:
npm i -g @angular/cli
The example below uses Angular CLI 7.1.0. To verify you’re using the same version, you can run ng --version
.
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 7.1.0
Node: 11.1.0
OS: darwin x64
Angular:
...
Package Version
------------------------------------------------------
@angular-devkit/architect 0.11.0
@angular-devkit/core 7.1.0
@angular-devkit/schematics 7.1.0
@schematics/angular 7.1.0
@schematics/update 0.11.0
rxjs 6.3.3
typescript 3.1.6
To create a new app, run ng new ng-secure
. When prompted for routing, type "Y". The stylesheet format is not relevant to this example, so choose whatever you like. I used CSS.
After Angular CLI finishes creating your app, cd into its directory, run ng new
, and navigate to http://localhost:4200
to see what it looks like.
Add Identity and Authentication to Your Angular 7 App with OIDC
If you’re developing apps for a large enterprise, you probably want to code them to use the same set of users. If you’re creating new user stores for each of your apps, stop it! There’s an easier way. You can use OpenID Connect (OIDC) to add authentication to your apps and allow them all to use the same user store.
OIDC requires an identity provider (or IdP). There are many well-known IdPs like Google, Twitter, and Facebook, but those services don’t allow you to manage your users like you would in Active Directory. Okta allows this, and you can use Okta’s API for OIDC.
Register for a forever-free developer account, and when you’re done, come on back so you can learn more about how to secure your Angular app!
Now that you have a developer account, I’ll show you several techniques for adding OIDC authentication to you Angular 7 app. But first, you’ll need to create a new OIDC app in Okta.
Create an OIDC App in Okta
Log in to your Okta Developer account and navigate to Applications > Add Application. Click Web and click Next. Give the app a name you’ll remember, and specify http://localhost:4200
as a Login redirect URI. Click Done. Edit your app after creating it and specify http://localhost:4200
as a Logout redirect URI too. The result should look something like the screenshot below.
Use angular-oauth2-oidc
The angular-oauth2-oidc library provides support for OAuth 2.0 and OIDC. It was originally created by Manfred Steyer and includes many community contributions.
Install angular-oauth2-oidc using the following command:
npm i angular-oauth2-oidc@5.0.2
Open src/app/app.module.ts
and import OAuthModule
as well as HttpClientModule
.
import { HttpClientModule } from '@angular/common/http';
import { OAuthModule } from 'angular-oauth2-oidc';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
OAuthModule.forRoot()
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Modify src/app/app.component.ts
to import OAuthService
and configure it to use your Okta application settings. Add login()
and logout()
methods, as well as a getter for the user’s name.
import { Component } from '@angular/core';
import { OAuthService, JwksValidationHandler, AuthConfig } from 'angular-oauth2-oidc';
export const authConfig: AuthConfig = {
issuer: 'https://{yourOktaDomain}/oauth2/default',
redirectUri: window.location.origin,
clientId: '{yourClientId}'
};
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'ng-secure';
constructor(private oauthService: OAuthService) {
this.oauthService.configure(authConfig);
this.oauthService.tokenValidationHandler = new JwksValidationHandler();
this.oauthService.loadDiscoveryDocumentAndTryLogin();
}
login() {
this.oauthService.initImplicitFlow();
}
logout() {
this.oauthService.logOut();
}
get givenName() {
const claims = this.oauthService.getIdentityClaims();
if (!claims) {
return null;
}
return claims['name'];
}
}
Modify src/app/app.component.html
to add Login and Logout buttons.
<h1>Welcome to {{ title }}!</h1>
<div *ngIf="givenName">
<h2>Hi, {{givenName}}!</h2>
<button (click)="logout()">Logout</button>
</div>
<div *ngIf="!givenName">
<button (click)="login()">Login</button>
</div>
<router-outlet></router-outlet>
Restart your app and you should see a login button.
Click the login button, sign in to your Okta account, and you should see your name with a logout button.
Pretty slick, eh?
Okta’s Angular SDK
You can also use Okta’s Angular SDK to implement the same functionality. You can start by installing it.
npm i @okta/okta-angular@1.0.7
Change app.module.ts
to configure your Okta settings and import the OktaAuthModule
.
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { OktaAuthModule } from '@okta/okta-angular';
const config = {
issuer: 'https://{yourOktaDomain}/oauth2/default',
redirectUri: window.location.origin + '/implicit/callback',
clientId: '{yourClientId}'
};
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
OktaAuthModule.initAuth(config)
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
You might notice the redirect URI is a bit different than the previous one. For this to work, you’ll need to modify your Okta app and add http://localhost:4200/implicit/callback
as a Login redirect URI.
Modify src/app/app-routing.ts
to have a route for this path.
import { OktaCallbackComponent } from '@okta/okta-angular';
const routes: Routes = [
{
path: 'implicit/callback',
component: OktaCallbackComponent
}
];
Change app.component.ts
to use the OktaAuthService
to determine if the user is authenticated.
import { Component, OnInit } from '@angular/core';
import { OktaAuthService, UserClaims } from '@okta/okta-angular';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'ng-secure';
isAuthenticated: boolean;
email: string;
constructor(public oktaAuth: OktaAuthService) {
}
async ngOnInit() {
this.isAuthenticated = await this.oktaAuth.isAuthenticated();
this.user = await this.oktaAuth.getUser();
// Subscribe to authentication state changes
this.oktaAuth.$authenticationState.subscribe( async(isAuthenticated: boolean) => {
this.isAuthenticated = isAuthenticated;
this.user = await this.oktaAuth.getUser();
});
}
}
Then update app.component.html
to use isAuthenticated
and display the user’s name.
<h1>Welcome to {{ title }}!</h1>
<div *ngIf="isAuthenticated">
<h2>Hi, {{user?.name}}!</h2>
<button (click)="oktaAuth.logout()">Logout</button>
</div>
<div *ngIf="!isAuthenticated">
<button (click)="oktaAuth.loginRedirect()">Login</button>
</div>
<router-outlet></router-outlet>
Restart your app and you should be able to log in to your app using Okta’s Angular SDK.
Use Authorization Code Flow
There is a new draft specification for OAuth called OAuth 2.0 for Browser-Based Apps. This was created by Okta’s own Aaron Parecki and contains an interesting clause.
The OAuth 2.0 Implicit grant authorization flow (defined in Section 4.2 of OAuth 2.0 [RFC6749]) works by receiving an access token in the HTTP redirect (front-channel) immediately without the code exchange step. The Implicit Flow cannot be protected by PKCE [RFC7636] (which is required according to Section 6), so clients and authorization servers MUST NOT use the Implicit Flow for browser-based apps.
Both angular-oauth2-oidc and Okta’s Angular SDK use implicit flow, the accepted practice prior to the recent discussion in Aaron’s draft specification. So how do you follow Aaron’s recommendation and use authorization code flow with PKCE in your Angular app? There are a couple options:
-
There is a fork of angular-oauth2-oidc called angular-oauth2-oidc-codeflow.
-
AppAuth for JS supports authorization code flow, complete with PKCE support.
I tried using angular-oauth2-oidc-codeflow with Okta. I used the code from my angular-oauth2-oidc example above and found I only needed to change a few things (after installing it with npm i angular-oauth2-oidc-codeflow
):
-
Imports should be from
'angular-oauth2-oidc-codeflow'
-
The
login()
method inAppComponent
should be changed to use auth code flow.login() { this.oauthService.initAuthorizationCodeFlow(); }
After making these changes, I tried to log in to my original SPA app. The error I received was unsupported_response_type
. I tried creating a new Native app with PKCE, but it failed because angular-oauth2-oidc-codeflow does not send a code challenge.
In my Build a Desktop App with Electron and Authentication, I successfully used AppAuth and PKCE. The reason this works is because it’s a desktop app and doesn’t send an origin
header. Okta’s token endpoint doesn’t allow CORS (cross-origin resource sharing), so it won’t work in a browser client.
We hope to fix this soon. As the industry evolves, we’ll do our best to keep our libraries current with best practices. In the meantime, we recommend you use a CSP (content security policy) to make sure 3rd party scripts don’t have access to your Angular app.
See 10 Excellent Ways to Secure Your Spring Boot Application to see how to add a CSP with Spring Boot. |
You might also find Micah Silverman’s PKCE Command Line project interesting.
Limit Access Based on Group for Your Angular 7 App
If you’d like to show/hide components of your app based on a user’s group, you’ll need to add a "groups" claim to your ID token. Log in to your Okta account, navigate to API > Authorization Servers, click the Authorization Servers tab and edit the default one. Click the Claims tab and Add Claim. Name it "groups", and include it in the ID Token. Set the value type to "Groups" and set the filter to be a Regex of .*
.
Now you can create an Angular directive to show/hide elements based on the user’s groups. There is currently an open issue that shows how you might go about doing this.
Control Access to Routes with an AuthGuard
Angular’s router documentation includes an example of how to create an AuthGuard
to protect routes so they’re only available to authenticated users.
Okta’s Angular SDK ships with an OktaAuthGuard
that you can use to protect your routes. It verifies there is a valid access token before allowing the user to navigate to the route. Below is an example of how to configure it in app-routing.module.ts
.
import { OktaAuthGuard } from '@okta/okta-angular';
const routes: Routes = [
{ path: 'secure', component: MySecureComponent, canActivate: [OktaAuthGuard] }
]
You can implement a similar auth guard for angular-oauth2-oidc as shown in Angular Authentication with OpenID Connect and Okta in 20 Minutes.
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { OAuthService } from 'angular-oauth2-oidc';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private oauthService: OAuthService, private router: Router) {}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if (this.oauthService.hasValidIdToken()) {
return true;
}
this.router.navigate(['/']);
return false;
}
}
Angular 7 CLI Tutorial and Angular 7 CRUD with Spring Boot
Phew, that’s a lot of information about authentication with Angular 7! For more straightforward Angular content, I invite you to take a look at a couple tutorials I recently upgraded to Angular 7.
I updated a few of my tutorials to use Angular 7 recently.
-
Angular 7 and Angular CLI Tutorial: a tutorial that shows you to create an app from scratch, as well as test it. Includes sections on how to add Angular Material, Bootstrap, and authentication with Okta.
-
Build a Basic CRUD App with Angular 7.0 and Spring Boot 2.1: a tutorial that features a Spring Boot backend and an Angular UI.
In fact, I enjoyed playing with Angular 7 so much, I turned the basic CRUD app tutorial into a screencast!
JHipster and Angular 7
I’m a committer on a project called JHipster. JHipster allows you to generate a Spring Boot app with an Angular UI quickly and easily. The JHipster team upgraded to Angular 7 in its 5.6.0 release. You can create a JHipster app with Angular using a single JDL file. JDL stands for JHipster Domain Language.
To see JHipster in action, install it using npm i generator-jhipster
and create an app.jh
file with the following JDL.
application { config { baseName blog, applicationType monolith, packageName com.jhipster.demo.blog, prodDatabaseType mysql, cacheProvider hazelcast, buildTool maven, clientFramework angular, useSass true, testFrameworks [protractor] } }
JHipster uses JWT authentication by default, but you can switch it to use OIDC for authentication pretty easily (hint: just add authenticationType oauth2 to app.jh ).
|
Create a blog
directory and run jhipster import-jdl app.jh
inside of it. In a minute or two, you’ll have a fully functional (and well-tested) Spring Boot + Angular + Bootstrap app! If you want to add entities to CRUD, see this sample JDL.
The sample JDL mentioned uses React for its clientFramework . Make sure to change it to angular to use Angular 7.
|
If you’ve never heard of JHipster before, you should download the free JHipster Mini-Book from InfoQ! It’s a book I wrote to help you get started with hip technologies today: Angular, Bootstrap and Spring Boot. The 5.0 version was recently released.
Learn More About Angular 7, JHipster, and OAuth 2.0
I hope you’ve enjoyed learning about Angular 7 and how to add authn/authz to an Angular app. I’ve written a lot about Angular on this blog. See the following posts to learn more about this modern web framework.
Okta Developer Blog Comment Policy
We welcome relevant and respectful comments. Off-topic comments may be removed.