Okta Auth JS and Angular
On This Page
This guide will walk you through integrating authentication and authorization into an Angular application using the Okta Auth SDK.
The Auth SDK is a lower level SDK than the Okta Angular SDK, which builds upon the Auth SDK to implement many of the features shown in this guide. Our complete Angular Sample Apps are built using the Angular SDK. For a simple integration, we generally recommend using the Angular SDK. However, in certain cases it may be preferable to use the Auth SDK directly, as shown here.
Prerequisites
If you do not already have a Developer Edition Account, you can create one at https://developer.okta.com/signup/.
Add an OpenID Connect Client
- Sign in to the Okta Developer Dashboard, and select Create New App
- Choose Single Page App (SPA) as the platform, then populate your new OpenID Connect application with appropriate values for your app. For example:
Setting | Value |
---|---|
Application Name | OpenId Connect App (must be unique) |
Login redirect URIs | http://localhost:4200/callback |
Logout redirect URIs | http://localhost:4200 |
Allowed grant types | Authorization Code |
Note: CORS is automatically enabled for the granted login redirect URIs.
Create an Angular App
To create an Angular app, open a terminal and install the Angular CLI:
npm install -g @angular/cli
Now, create a new application using the Angular CLI:
ng new okta-app
When asked Would you like to add Angular routing?
, press "y"
For this example we are using CSS
as the style engine. If you would like to use SCSS
or another style engine, you may need to make a few adjustments to the code snippets shown in this guide.
After all prompts have been answered, the Angular CLI will create a new project in a folder named okta-app
and installs all required dependencies.
cd okta-app
Install the Okta Auth SDK using npm
:
npm install @okta/okta-auth-js --save
Create an Authentication Service
Users can sign in to your Angular application a number of different ways. The easiest, and most secure way is to use the default login page. This page renders the Okta Sign-In Widget, equipped to handle User Lifecycle operations, MFA, and more.
First, create src/app/app.service.ts
as an authorization utility file and use it to bootstrap the required fields to login:
Important: We're using Okta's organization authorization server to make setup easy, but it's less flexible than a custom authorization server. Many SPAs send access tokens to access APIs. If you're building an API that will need to accept access tokens, create an authorization server.
import { Observable, Observer } from 'rxjs';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { OktaAuth } from '@okta/okta-auth-js';
@Injectable()
export class OktaAuthService {
// IMPORTANT!
// Replace {clientId} with your actual Client ID
// Replace {yourOktaDomain} with your actual Okta domain
// If using a custom authorization server, ISSUER should be 'https://{yourOktaDomain}/oauth2/${authServerId}'
CLIENT_ID = '{clientId}';
ISSUER = 'https://{yourOktaDomain}'
LOGIN_REDIRECT_URI = 'http://localhost:4200/callback';
LOGOUT_REDIRECT_URI = 'http://localhost:4200/';
oktaAuth = new OktaAuth({
clientId: this.CLIENT_ID,
issuer: this.ISSUER,
redirectUri: this.LOGIN_REDIRECT_URI,
pkce: true
});
$isAuthenticated: Observable<boolean>;
private observer: Observer<boolean>;
constructor(private router: Router) {
this.$isAuthenticated = new Observable((observer: Observer<boolean>) => {
this.observer = observer;
this.isAuthenticated().then(val => {
observer.next(val);
});
});
}
async isAuthenticated() {
// Checks if there is a current accessToken in the TokenManger.
return !!(await this.oktaAuth.tokenManager.get('accessToken'));
}
login(originalUrl) {
// Save current URL before redirect
sessionStorage.setItem('okta-app-url', originalUrl || this.router.url);
// Launches the login redirect.
this.oktaAuth.token.getWithRedirect({
scopes: ['openid', 'email', 'profile']
});
}
async handleAuthentication() {
const tokenContainer = await this.oktaAuth.token.parseFromUrl();
this.oktaAuth.tokenManager.add('idToken', tokenContainer.tokens.idToken);
this.oktaAuth.tokenManager.add('accessToken', tokenContainer.tokens.accessToken);
if (await this.isAuthenticated()) {
this.observer.next(true);
}
// Retrieve the saved URL and navigate back
const url = sessionStorage.getItem('okta-app-url');
this.router.navigateByUrl(url);
}
async logout() {
await this.oktaAuth.signOut({
postLogoutRedirectUri: this.LOGOUT_REDIRECT_URI
});
}
}
Create an Authorization Guard
Now that you have a shared service to start, control, and end the authentication state, use it to protect the endpoints of an app.
Create src/app/app.guard.ts
that implements CanActivate
:
// app.guard.ts
import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { OktaAuthService } from './app.service';
@Injectable()
export class OktaAuthGuard implements CanActivate {
constructor(private okta: OktaAuthService, private router: Router) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const authenticated = await this.okta.isAuthenticated();
if (authenticated) { return true; }
// Redirect to login flow.
this.okta.login(state.url);
return false;
}
}
Whenever a user attempts to access a route that is protected by OktaAuthGuard
, it first checks to see if the user has been authenticated. If isAuthenticated()
returns false
, the login flow will be started.
Add Routes
Let's take a look at what routes are needed:
/
: A default page to handle basic control of the app./callback
: Handle the response back from Okta and store the returned tokens./protected
: A protected route by theOktaAuthGuard
.
/
First, update src/app/app.component.ts
to inject the OktaAuthService
into the component. This will make the service available within the component's template as the variable oktaAuth
. Subscribe to the Observable
exposed by the OktaAuthService
. This will keep the variable isAuthenticated
updated. We will use this variable within the template to control visibility of the login and logout buttons.
import { Component, OnInit } from '@angular/core';
import { OktaAuthService } from './app.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'okta-app';
isAuthenticated: boolean;
constructor(public oktaAuth: OktaAuthService) {}
ngOnInit() {
this.oktaAuth.$isAuthenticated.subscribe(val => this.isAuthenticated = val);
}
}
Next, update src/app/app.component.html
with some buttons to trigger login or logout. Also add a link to the /protected
route. There may be a large block of "placeholder" code in this file generated by the Angular CLI. You can safely remove this.
<!-- app.component.html -->
<button routerLink="/"> Home </button>
<button *ngIf="!isAuthenticated" (click)="oktaAuth.login('/')"> Login </button>
<button *ngIf="isAuthenticated" (click)="oktaAuth.logout()"> Logout </button>
<button routerLink="/protected"> Protected </button>
<router-outlet></router-outlet>
/callback
In order to handle the redirect back from Okta, we need to capture the token values from the URL. Use the /callback
route to handle the logic of storing these tokens and redirecting back to the main page.
Create a new component src/app/callback.component.ts
:
// callback.component.ts
import { Component, OnInit } from '@angular/core';
import { OktaAuthService } from './app.service';
@Component({ template: `` })
export class CallbackComponent implements OnInit {
constructor(private okta: OktaAuthService) {}
ngOnInit() {
// Handles the response from Okta and parses tokens
this.okta.handleAuthentication();
}
}
/protected
This route will be protected by the OktaAuthGuard
, only permitting authenticated users with a valid accessToken
.
// protected.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-secure',
templateUrl: './protected.component.html',
})
export class ProtectedComponent {}
Create a new template called protected.component.html
:
<!-- protected.component.html -->
<h2>PROTECTED!</h2>
Connect the Routes
Add each of our new routes to src/app/app-routing.module.ts
:
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { OktaAuthGuard } from './app.guard';
import { CallbackComponent } from './callback.component';
import { ProtectedComponent } from './protected.component';
const routes: Routes = [{
path: 'callback',
component: CallbackComponent
}, {
path: 'protected',
component: ProtectedComponent,
canActivate: [ OktaAuthGuard ]
}];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
Notice how the path /protected uses the canActivate
parameter to gate access to the route.
Update your @NgModule
in src/app/app.module.ts
:
- Import OktaAuthGuard, OktaAuthService, and the newly created components
- Add the components to the array of
declarations
- Add the guard and service to the array of
providers
// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
// Okta Guard and Service
import { OktaAuthGuard } from './app.guard';
import { OktaAuthService } from './app.service';
import { CallbackComponent } from './callback.component';
import { ProtectedComponent } from './protected.component';
@NgModule({
declarations: [
AppComponent,
CallbackComponent,
ProtectedComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [
OktaAuthGuard,
OktaAuthService,
],
bootstrap: [AppComponent]
})
export class AppModule { }
Build and start the app. In the terminal:
npm start
After the server is started, this message will appear in your terminal:
** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
Conclusion
You have now successfully authenticated with Okta! Now what? With a user's id_token
, you have basic claims for the user's identity. You can extend the set of claims by modifying the scopes
to retrieve custom information about the user. This includes locale
, address
, groups
, and more.
Want to learn how to use the user's access_token
? Check out our how to guide to learn about protecting routes on your server, validating the access_token
, and more!
Support
Have a question or see a bug? Post your question on the Okta Developer Forums.