DEV Community

Deekshith Raj Basa 🔥
Deekshith Raj Basa 🔥

Posted on

Implicit flow authentication using angular-oauth2-oidc (Angular)

Installing the pacakage

Install the angular-oauth2-oidc package using npm.

npm i angular-oauth2-oidc --save 
Enter fullscreen mode Exit fullscreen mode

Setting up the NgModule (app.module)

When package installation has been done then import the OAuthModule in the app.modulefile.

import { OAuthModule } from 'angular-oauth2-oidc';
[...]
@NgModule({
  imports: [  
   [...]    HttpModule,
    OAuthModule.forRoot()  
],  
........export classAppModule{}

Enter fullscreen mode Exit fullscreen mode

Implicit Flow configuration & Login page

This is the OAuth2/OIDC flow best suitable for SPA. It sends the user to the IdentityProvider's login page (Identity Server). After logging in, the SPA gets tokens. This alsoallows for single sign on as well as single sign off.
To configure the library just have to set some properties (AuthConfig) on startup as requiredby OAuthService i.e. on the constructor of the AppComponent which is called before therouting kicks in.

import { AuthConfig } from'angular-oauth2-oidc';
export const authConfig: AuthConfig = {
// Url of the Identity Provider  
issuer: 'https://demo.identityserver.com/identity',
// Login Url of the Identity Provider  
loginurl: 'https://demo.identityserver.com/identity/connect/authorize',
// Login Url of the Identity Provider  
logouturl: 'https://demo.identityserver.com/identity/connect/endsession',
// URL of the SPA to redirect the user to after login  
redirectUri: window.location.origin + '/dashboard.html',
// The SPA's id. The SPA is registerd with this id at the auth-server  
clientId: 'billing_demo',
// set the scope for the permissions the client should request
// The first three are defined by OIDC. Also provide user sepecific
 scope: 'openid profile email billing_demo_api',
}
Enter fullscreen mode Exit fullscreen mode

When the AuthConfig properties has been set then configure the OAuthService for loadingidentity server's discovery document & if access token is invalid then trigger implicit flow for presenting users with the identity server's login page .

import { OAuthService } from 'angular-oauth2-oidc';
import { JwksValidationHandler } from 'angular-oauth2-oidc';
import { authConfig } from './auth.config';
import { Component } from '@angular/core';
@Component({
  selector: 'billing-app',
  templateUrl: './app.component.html'
})
exportclassAppComponent{
  constructor(private oauthService: OAuthService) {
    this.ConfigureImplicitFlowAuthentication();
  } 
               private ConfigureImplicitFlowAuthentication() {
    this.oauthService.configure(authConfig);
    this.oauthService.tokenValidationHandler = 
new JwksValidationHandler();
    this.oauthService.loadDiscoveryDocument().then(doc) => {
      this.oauthService.tryLogin().catch(err => {
        console.error(err);
      }).then(() => {
        if (!this.oauthService.hasValidAccessToken()) {
          this.oauthService.initImplicitFlow()
        }
      });
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

The discovery endpoint can be used to retrieve metadata about your IdentityServer - it returns information like the issuer name, key material, supported scopes etc.The discovery endpoint is available via /.well-known/openid-configuration relative to the base address

https://demo.identityserver.com/.well-known/openid-configuration
Enter fullscreen mode Exit fullscreen mode

Automatically refreshing a token when/ before it expires (silent refresh)

To automatically refresh a token when/ some time before it expires, just call the following method after configuring the OAuthService in the AppComponent.

this.oauthService.setupAutomaticSilentRefresh();
Enter fullscreen mode Exit fullscreen mode

By default, this event is fired after 75% of the token's life time is over. You can adjust this factor by setting the property timeoutFactor to a value between 0 and 1. For instance, 0.5means, that the event is fired after half of the life time is over and 0.33 triggers the event after a third

Passing Bearer access token when calling Web API

1. Passing directly by using the class HttpHeaders of new HttpClient

var headers = new HttpHeaders({"Authorization": "Bearer " + this.oauthService.getAccessToken()});
Enter fullscreen mode Exit fullscreen mode

2. By setting allowedUrls to an array with prefixes for the respective urls. Use lower case forthe prefixes.

OAuthModule.forRoot({
  resourceServer: {
    allowedUrls: ['https://billing_demo_api.com/api'],
    sendAccessToken: true
  }
}

Enter fullscreen mode Exit fullscreen mode

Logout configuration

In order to log out from the application, just need to call the logout() method of theOAuthService. It will end the session & redirects the user to the post logout redirect url associated to the client.

this.oauthService.logout();
Enter fullscreen mode Exit fullscreen mode

Access Token & Identity Claims

To get the access token & identity claims, just need to call the getAccessToken() andgetIdentityClaims() methods of the OAuthService

this.oauthService.getIdentityClaims();
this.oauthService.getAccessToken()
Enter fullscreen mode Exit fullscreen mode

Implementing AuthGuard

If you want to restrict some pages from unauthorized access, create AuthGuard class which further implements CanActivate that return either true if the user can access a route or false if they can’t based on the validity of the access token

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { OAuthService } from 'angular-oauth2-oidc';

@Injectable() export class AuthGuard implements CanActivate {
  constructor(private oauthService: OAuthService, private router: Router) {

  }
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    if (this.oauthService.hasValidAccessToken()) {
      return true;
    } this.router.navigate(['']);
  }
}
Enter fullscreen mode Exit fullscreen mode

Then add the AuthGuard to the routing configuation as

const routes: Routes = [
  { path: 'home', component: HomeComponent },
  {
    path: 'search', component: SearchComponent,
    canActivate: [AuthGuard]
  },
  {
    path: 'bill/:billId', component: BillingComponent,
    canActivate: [AuthGuard], children: [
      { path: '', redirectTo: 'ledger' }
    ]
  }];

Enter fullscreen mode Exit fullscreen mode

Top comments (7)

Collapse
 
tolga_koseoglu_f15d427aec profile image
Tolga Koseoglu

Thank you. I am having issues authentication my app against my api. My client app config looks just like yours. I assume my resource api has not been configured correctly. Would you share please how you have your resource api configured? Thank you

Collapse
 
abdulghani200 profile image
Abdul Ghani

Informative post. Thanks for sharing bro! ❤️✌🏻

Collapse
 
deekshithrajbasa profile image
Deekshith Raj Basa 🔥

Thanks bro 😊✌️

Collapse
 
gokhanelek profile image
Gökhan Elek

Thank you

Collapse
 
gagandeepp profile image
Gagan Deep

Hi Deekshith , I am trying to oidc with angular but my app is reloading again and again after trylogin method there by increasing uri size and stopping my angular app,please help!

Collapse
 
deekshithrajbasa profile image
Deekshith Raj Basa 🔥 • Edited

Hey Gagan Deep, did you tried the entire process that I have explained above?

Collapse
 
stirumala76 profile image
Sridhar Tirumala

Do you have codebase for it