Authentication using OAuth2 + OpenId Connect in Angular Single Page Application
Learn how to use Okta as an authorization server for your angular application
Authentication and authorization are critical component of any application. In simple terms, authentication is a mechanism to verify if the user is the person who he/she is claiming to be. On the other hand, authorization is used to check for the accesses the user have. Authorization tells the application what actions the user is allowed to perform. In this article, we will focus on authentication only. Authorization will be covered in next articles of the series.
If you want your users to login to your application using Google, Facebook, GitHub or any other identity providers, then you need to use OAuth2 for authorization.
OAuth2 and OpenId Connect
OAuth2 is an industry-standard protocol for authorization. It is not meant for authentication. But, we need authentication as well. That's where OpenId Connect comes into play. It is a simple identity layer on top of OAuth2 protocol.
OAuth2 Components
3 components are involved while making single page application which uses OAuth2.
Authorization Server takes care of authenticating and authorizing users using various methods - Username/Password, Google, Facebook, GitHub (you name it). We have some options for it. First is to build your own which will be very time and resources consuming, error-prone and not so secure task. Second is to use any existing authorization server and maintain that by yourself. Keycloak is a good example of it. Third option is to completely outsource this task to other identity providers. Okta and Auth0 are good examples of these providers. We will be using Okta for this series.
Client Server is responsible to display User Interface and data in the web browser. For now, we will use Angular framework to create it.
API/Resource Server exposes APIs to be consumed by Client Server and is responsible for storing all the data related to the application. It also restricts the user from accessing the resources which she doesn't have permission to. We will use Spring Boot to create API Server in next parts of the series.
Setup Okta
Let's create one application and user in Okta identity platform for this application.
Create Application
- Create a free developer account at
https://developer.okta.com/
. - Go to Applications > Applications.
- Click on Create App Integration.
- For Sign-in method, select OIDC - OpenId Connect.
- For Application type, select Single-Page Application.
- Click on Next.
- For App Integration name, enter name best suitable to your application.
- For Grant Type, choose Authorization Code.
- For Sign-in redirect URIs, add localhost:4200.
- For Sign-out redirect URIs, add localhost:4200.
- For Controlled Access, select Allow everyone in your organization.
- Click on Save.
After that, we can see application name, Client ID, Okta domain and other settings for the newly created application. Client ID
and Okta domain
will be used later in the article.
Create User
- Go to Directory > People.
- Click on Add Person.
- Fill all the fields as given in the image below and click on Save.
Build Angular application
Now, let's dive into code and build an angular application which will use Okta as an identity provider to login the users. But hold on, let's make sure you have the right tools with you.
- Node.js is a JavaScript runtime built on Chrome's V8 JavaScript Engine. It is needed to run the JavaScript code outside of the browser. If you don't have it in your machine, install it from here.
- Angular CLI is used to create angular apps. If you don't have it, follow the instructions here to install it.
Now, my friend, you are ready to code.
Open terminal and run the following command to create a new angular app named AngularOAuth2Demo
:
$: ng new AngularOAuth2Demo
This command might prompt you to select some options. Choose what is best for your application. After that, change your current directory to AngularOAuth2Demo
app with the following command:
$: cd AngularOAuth2Demo
To manage all the requests for OAuth2 flow and to manage state of the logged in user in our angular app, we will use angular-oauth2-oidc package. Install it into your app using the following command:
$: npm i angular-oauth2-oidc --save
Crete a file src/app/config/authCodeFlowConfig.ts
to manage configurations related to OAuth2 flow. You will need to create config
directory inside src/app
directory and then create the file. Replace the code of authCodeFlowConfig.ts
with the following code:
import { AuthConfig } from "angular-oauth2-oidc";
export const authCodeFlowConfig: AuthConfig = {
issuer: "<Your Issuer URI>",
redirectUri: window.location.origin,
clientId: "<Your Client Id>",
responseType: "code",
scope: "openid profile email",
showDebugInformation: true
};
Let's break down this file and try to understand it. Issuer URI is a unique URI to identify the application for authentication and authorization purpose. You can find it on your application page in identity provider. If you created app in Okta, you will see Okta Domain for you app and the Issuer URI will be https://<Your Okta Domain>/oauth2/default
.
The second part is redirectUri
, which is used to redirect the user after authentication. This URI must be one of those Sign-in redirect URIs
which you configured while making the app in Okta provider. We are using window.location.origin
which will be http://localhost:4200
, when the angular app will start, since Angular apps run on port 4200 by default. But, if you deploy your app to any server, say https://yourcoolwebsite.dev
, then be sure to add this URI (https://yourcoolwebsite.dev
) to Sign-in redirect URIs for your app in Okta or any other identity provider.
The clientId
is unique to your application and you will find it on the application page in your identity provider. We are using responseType
as "code"
, so that identity provider will not send the access token
and id token
in query parameters (which is displayed in browser search bar). The next attribute scope
is used to get access token with the given scopes from identity provider. The last attribute showDebugInformation
is used to print some debugging information to the console.
Replace the code of app.module.ts
with the following code:
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { OAuthModule } from 'angular-oauth2-oidc';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule,
OAuthModule.forRoot()
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Here, we imported HttpClientModule
module from '@angular/common/httppackage and
OAuthModulefrom
angular-oauth2-oidc` package to handle auth flow and user state.
Now, replace the code of app.component.ts
with the following code:
import { Component } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { authCodeFlowConfig } from './config/authCodeFlowConfig';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'AngularOAuth2Demo';
constructor(private oauthService: OAuthService) {
this.oauthService.configure(authCodeFlowConfig);
this.oauthService.loadDiscoveryDocumentAndTryLogin();
}
login() {
this.oauthService.initCodeFlow();
}
logout() {
this.oauthService.logOut();
}
get name () {
const claims = this.oauthService.getIdentityClaims();
if (!claims) return null;
const nameClaim = claims as {name: string};
return nameClaim.name;
}
}
Here, in the constructor
we are configuring the oauthService
to use authCodeFlowConfig.ts
file for configuration. Then, we are trying to login the user if the session already exists.
In the login
method, we are initiating the code flow for login. The initCodeFlow
method of oauthService
handles all the requests to and from the identity provider to get the access token. In the logout
method, we are simply logging the user out.
The name
setter method is used to extract name
property from the claims, which we receive from the identity provider.
Now, replace the code of app.component.html
with the following code:
<div>
Welcome to Angular OAuth2 Demo
<div *ngIf="name">
Welcome, {{name}}<br />
<button (click)="logout()">Logout</button>
</div>
<div *ngIf="!name">
You are not logged in.
<button (click)="login()">Login</button>
</div>
</div>
Here, if the name
is not empty, we are displaying a welcome message and a button which will call logout
method when clicked. If the name
is null or empty, another button will be displayed, which will call login
method when clicked. And we have already created login
and logout
methods and name
setter method in app.component.ts
.
Now, its time to see the application in action. Run the following command in the terminal to start the application:
$: ng serve
In a private window of the browser, open http://localhost:4200
. You will see a page with Login button. Click on login button. You will be redirected to Okta login page to enter username and password. Enter the username and password of the user which you created in Okta platform. After log in you will be redirected back to the angular application. Now, you will be able to see welcome message along with Logout
button. Clicking on the logout button will logout the user and login button will appear again.
Summary
Click here to view the source code on GitHub. In this article, we used Okta as an identity provider to user OAuth2 in a Single Page Application. We still need to make resource server in order to complete the application. We will do that in next article. Also, we will discuss about some of the drawback of client side app and how to resolve them.
Your suggestions are welcome. Until then, Bye and keep learning...