@vendasta/oauth2

See TOC menu above.

Usage no npm install needed!

<script type="module">
  import vendastaOauth2 from 'https://cdn.skypack.dev/@vendasta/oauth2';
</script>

README

OAuth2 Service

Table of Contents

See TOC menu above.

Introduction

Hey folks! This is a vendasta-centric oauth2 angular service! It can help you out with the following things:

  • Adding authentication to your app -- It can authenticate the user with IAM using sso's oauth2 integration!
  • It doubles as a SessionService for your angular app, so all your sdks and api calls should just work
  • It provides a helpful canActivate route guard for you to use in your routes, which can detect unauthenticated users and will seamlessly log them in and redirect back to the desired page.

Sound good? Keep reading for installation instructions:

Installation

Install @vendasta/oauth2 in your Angular app.

Dependencies

First things first:

npm install --save @vendasta/oauth2 angular-oauth2-oidc

You'll also need to have the following list of peer dependencies, but you likely already have most of those:

npm install --save \
  typescript \
  rxjs \
  @angular/common \
  @angular/core \
  @angular/router \
  angular-oauth2-oidc \
  @vendasta/uikit \
  @vendasta/core \
  @vendasta/partner

Nice!

Module Setup

Now we need to provide the service in your app. This should be as easy as importing the OAuth2Module in your app.module.ts and providing OAuthStorage and OAuth2ServiceConfigToken dependencies.

We'll also want to specify that we're using the OAuth2Service for our sessions by providing it as the SessionService

import { OAuth2Service, OAuth2Module } from '@vendasta/oauth2';
import { OAuthModule, OAuthStorage } from 'angular-oauth2-oidc';

@NgModule({
  imports: [
    // ...
    OAuth2Module,
    OAuthModule.forRoot(),
  ],
  providers: [
    { provide: OAuthStorage, useFactory: (): OAuthStorage => window.localStorage },
    // Use the OAuth2Service service to handle sessions
    { provide: SessionService, useExisting: OAuth2Service },
  ],
})
export class AppModule {
}

Great!

Configuration

NOTE See the SSO frontend auth docs for more details.

Now we've got our service set up, but you'll need to configure it so it knows how to authorize your users for your app.

If you haven't done so already, you'll need to create a service provider configuration for your app on demo and prod.

Once you've done that, you can create an oauth2 configuration factory (or export a config value, if it's static) somewhere in your app. Here's what the one for WSP Admin Center looks like:

export function oauth2ConfigFactory(): OAuth2ServiceConfig {
  return {
    scopes: ['admin'],
    serviceProviderConfigs: {
      [Environment.LOCAL]: {
        serviceProviderId: 'wsp-admin-center',
        redirectUri: 'https://localhost:4200',
        silentRefreshRedirectUri: 'https://localhost:4200/silent-refresh.html',
        clientId: 'c5a86f66-5ed1-4c11-82e3-157088e94831',
      },
      [Environment.DEMO]: {
        serviceProviderId: 'wsp-admin-center',
        redirectUri: 'https://wsp-admin-center-demo.apigateway.co',
        silentRefreshRedirectUri: 'https://wsp-admin-center-demo.apigateway.co/silent-refresh.html',
        clientId: 'c5a86f66-5ed1-4c11-82e3-157088e94831',
      },
      [Environment.PROD]: {
        serviceProviderId: 'wsp-admin-center',
        redirectUri: 'https://wsp-admin-center-prod.apigateway.co',
        silentRefreshRedirectUri: 'https://wsp-admin-center-prod.apigateway.co/silent-refresh.html',
        clientId: 'f6486e6d-740c-48b1-b46e-38923aba92b5',
      },
    },
    customQueryParamsFunc: (next: ActivatedRouteSnapshot, _: RouterStateSnapshot) => ({
      partner_id: (next.params as PartnerRouteParams).partnerId,
    }),
  };
}

You can then provide this configuration in your app.module.ts, for example with useFactory:

import { OAuth2ServiceConfigToken } from '@vendasta/oauth2'

@NgModule({
  // ...
  providers: [
    { provide: OAuth2ServiceConfigToken, useFactory: oauth2ConfigFactory },
  ],
}

Using the AuthGuard

The OAuth2Service implements the CanActivate interface so it can be used directly as an route guard in your routing module.

This example requires the user to be logged in on every page except the logout page. Remember that ALL pages with the OAuth2Service route guard will redirect through an authentication flow if the app doesn't have a session for the user.

IMPORTANT SECURITY WARNING: do not rely on this route guard for performing authorization. Malicious actors can easily modify frontend code to bypass this kind of check.

You MUST perform backend authorization in each RPC that your app calls, and only use the route guard as a convenient way to ensure that users are directed to login if they don't have a valid session and are not redirected to login if their session is valid.

NOTE you must use OAuth2Service as a route guard for the route that handles your oauth2 redirectUri. This ensures that the code exchange flow works correctly, redirecting the user back to their originally-requested URL once they have a valid session.

const appRoutes = [
  // ...
]

@NgModule({
  imports: [
    RouterModule.forRoot([
      {path: 'logout', component: LogoutComponent},
      {path: '', canActivate: [OAuth2Service], component: IndexComponent, children: appRoutes},
    ])],
  exports: [RouterModule]
})
export class AppRoutingModule {
}

Configuring silent refresh

This oauth service uses a technique called "silent refresh" to allow refreshing tokens without the use of a Refresh Token (which are very difficult to use securely).

It does this by opening a hidden window with an iframe which silently goes through the oauth flow to get an up-to-date token.

See the SSO frontend auth docs for details.