# Angular

## Integrating SSO with <img src="https://3706867246-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F0tGo33otNRgIhFn6lpgU%2Fuploads%2FBmtqrLljO6DgXHOOiiJB%2Fangular-icon.svg?alt=media&#x26;token=a679de04-b93e-4329-905e-c0a31c76ac92" alt="" data-size="line">Angular

The following example illustrates how SSO can be integrated within your applications' code. The example shows how would an application using `Angular` enable the SSO feature via **OpenID-Connect.**

### Angular Guide for SSO OpenID-Connect

This Angular guide requires the following dependency via npm:

* `angular-oauth2-oidc`

**Run in terminal:**

```
npm install angular-oauth2-oidc --save
```

Add an `environment.ts` file to your project directory. This will contain the required credentials to connect to IndustryApps Keycloak OpenID-Connect.

{% code title="environment.ts" overflow="wrap" lineNumbers="true" %}

```javascript
export const environment = {
  production: true,
  envName: 'local',
  keycloak: {
    // Url of the Identity Provider
    issuer: 'https://auth.uat.industryapps.net/auth/realms/IndustryApps',

    // URL of the SPA to redirect the user to after login (Development Env)
    redirectUri: 'https://democustomer.uat.industryapps.net/<APPCODE>/',

    // The SPA's id. 
    // The SPA is registerd with this id at the auth-serverß
    clientId: 'APPID',

    responseType: 'code',
    // set the scope for the permissions the client should request
    // The first three are defined by OIDC.
    scope: 'openid profile email',
    // Remove the requirement of using Https to simplify the demo
    // THIS SHOULD NOT BE USED IN PRODUCTION
    // USE A CERTIFICATE FOR YOUR IDP
    // IN PRODUCTION
    requireHttps: false,
    // at_hash is not present in JWT token
    showDebugInformation: true,
    disableAtHashCheck: true
  }
};

```

{% endcode %}

Include an authconfig sub-directory in your project directory. Create an authconfig.module.ts file which initialises an oauth2-oidc module as illustrated below:

{% code title="authconfig.module.ts" overflow="wrap" lineNumbers="true" %}

```javascript
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { OAuthModule, AuthConfig } from 'angular-oauth2-oidc';

import { AuthConfigService } from './auth-config.service';
import { authConfig, OAuthModuleConfig } from './auth.config';

export function init_app(authConfigService: AuthConfigService) {
    return () => authConfigService.initAuth();
}

@NgModule({
  imports: [ HttpClientModule, OAuthModule.forRoot() ],
  providers: [
    AuthConfigService,
    { provide: AuthConfig, useValue: authConfig },
    OAuthModuleConfig,
    { 
      provide: APP_INITIALIZER, 
      useFactory: init_app, 
      deps: [ AuthConfigService ], 
      multi: true
    }
  ]
})
export class AuthConfigModule { }

```

{% endcode %}

Add an `auth.config.ts` file in the `authconfig` sub-directory. `auth.config.ts` would utilise the environment variables configured in the `environment.ts` file made previously.

{% code title="auth.config.ts" overflow="wrap" lineNumbers="true" %}

```javascript
import { AuthConfig } from 'angular-oauth2-oidc';
import { environment } from '../../environments/environment';

export const authConfig: AuthConfig = {

    // Url of the Identity Provider
    issuer: environment.keycloak.issuer,

    // URL of the SPA to redirect the user to after login
    redirectUri: environment.keycloak.redirectUri,

    // The SPA's id. 
    // The SPA is registerd with this id at the auth-serverß
    clientId: environment.keycloak.clientId,

    responseType: environment.keycloak.responseType,
    // set the scope for the permissions the client should request
    // The first three are defined by OIDC.
    scope: environment.keycloak.scope,
    // Remove the requirement of using Https to simplify the demo
    // THIS SHOULD NOT BE USED IN PRODUCTION
    // USE A CERTIFICATE FOR YOUR IDP
    // IN PRODUCTION
    requireHttps: environment.keycloak.requireHttps,
    // at_hash is not present in JWT token
    showDebugInformation: environment.keycloak.showDebugInformation,
    disableAtHashCheck: environment.keycloak.disableAtHashCheck
};


export class OAuthModuleConfig {
    resourceServer: OAuthResourceServerConfig = {sendAccessToken: false};
}

export class OAuthResourceServerConfig {
    /**
     * Urls for which calls should be intercepted.
     * If there is an ResourceServerErrorHandler registered, it is used for them.
     * If sendAccessToken is set to true, the access_token is send to them too.
     */
    allowedUrls?: Array<string>;
    sendAccessToken = true;
    customUrlValidation?: (url: string) => boolean;
}
```

{% endcode %}

Add an `auth-config.service.ts` file to the `authconfig` sub-directory which sets up OAuthService. `auth-config.service.ts` initialises a login page if authorisation is required otherwise initialises the app instance.

{% code title="auth-config.service.ts" overflow="wrap" lineNumbers="true" %}

```javascript
import { Injectable } from '@angular/core';
import { AuthConfig, NullValidationHandler, OAuthService } from 'angular-oauth2-oidc';
import { filter } from 'rxjs/operators';

@Injectable()
export class AuthConfigService {
    
    private _decodedAccessToken: any;
    private _decodedIDToken: any;
    get decodedAccessToken() { return this._decodedAccessToken; }
    get decodedIDToken() { return this._decodedIDToken; }

    constructor(
      private readonly oauthService: OAuthService,
      private readonly authConfig: AuthConfig,
    ) {}

    async initAuth(): Promise<any> {
      return new Promise<void>((resolveFn, rejectFn) => {
        // setup oauthService
        this.oauthService.configure(this.authConfig);
        this.oauthService.setStorage(localStorage);
        this.oauthService.tokenValidationHandler = new NullValidationHandler();
  
        // subscribe to token events
        this.oauthService.events
          .pipe(filter((e: any) => {
            return e.type === 'token_received';
          }))
          .subscribe(() => this.handleNewToken());
          
        // continue initializing app or redirect to login-page
        
        this.oauthService.loadDiscoveryDocumentAndLogin().then(isLoggedIn => {
          if (isLoggedIn) {
            this.oauthService.setupAutomaticSilentRefresh();
            resolveFn();
          } else {
            this.oauthService.initImplicitFlow();
            rejectFn();
          }
        });
        
      });
    }
  
    private handleNewToken() {
      this._decodedAccessToken = this.oauthService.getAccessToken();
      this._decodedIDToken = this.oauthService.getIdToken();
    }

}
```

{% endcode %}

Add `auth-config.service.spec.ts` to the `authconfig` sub-directory which is the last requirement to be added in the authconfig sub-directory.

{% code title="auth-config.service.spec.ts" overflow="wrap" lineNumbers="true" %}

```javascript
import { TestBed } from '@angular/core/testing';

import { AuthConfigService } from './auth-config.service';

describe('AuthConfigService', () => {
  let service: AuthConfigService;

  beforeEach(() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(AuthConfigService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });
});

```

{% endcode %}

So far the addition of the `authconfig` sub-directory includes:

{% code title="authconfig" %}

```bash
.
├── auth-config.service.spec.ts
├── auth-config.service.ts
├── auth.config.ts
└── authconfig.module.ts
```

{% endcode %}

Be sure to import the environment into the `main.ts` file in your app directory. e.g.&#x20;

{% code overflow="wrap" lineNumbers="true" %}

```javascript
import { environment } from './environments/environment';
```

{% endcode %}

A full `main.ts` file example is illustrated below, given you've designated both an `environment.prod.ts` version and `environment.ts` file in an environments sub-directory.

{% code title="main.ts" overflow="wrap" lineNumbers="true" %}

```javascript
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

```

{% endcode %}

A full demo sample client-side Angular app is available below provided as a reference.

{% embed url="<https://github.com/IndustryApps/SSODemo-Angular>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.industryapps.net/authentication/single-sign-on/angular.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
