skyid-angular
v2.0.0
Published
Easy SkyId JS setup for Angular applications
Downloads
118
Maintainers
Readme
Skyid Angular
Easy Skyid-js setup for Angular applications.
About
This library helps you to use skyid-js in Angular applications providing the following features:
- A Skyid Service which wraps the
skyid-js
methods to be used in Angular, giving extra functionalities to the original functions and adding new methods to make it easier to be consumed by Angular applications. - Generic AuthGuard implementation, so you can customize your own AuthGuard logic inheriting the authentication logic and the roles load.
- A HttpClient interceptor that adds the authorization header to all HttpClient requests. It is also possible to disable this interceptor or exclude routes from having the authorization header.
- This documentation also assists you to configure the skyid in your Angular applications and with the client setup in the admin console of your skyid installation.
Installation
Run the following command to install both Skyid Angular and the official Skyid client library:
npm install skyid-angular skyid-js
Note that skyid-js
is a peer dependency of Skyid Angular. This change allows greater flexibility of choosing the right version of the Skyid client version for your project.
Versions
| Angular | skyid-js | skyid-angular | Support | | :-----: |:--------:|:-------------:|:-------------------:| | 14.x | 1.x.x | 1.x.x | - | | :-----: |:--------:|:-------------:|:-------------------:| | 16.x | 1.x.x | 2.x.x | - |
Only the latest version of Angular in the table above is actively supported. This is due to the fact that compilation of Angular libraries might be incompatible between major versions.
Choosing the right skyid-js version
The Skyid client documentation recommends to use the same version of your Skyid server installation.
Setup
In order to make sure Skyid is initialized when your application is bootstrapped you will have to add an APP_INITIALIZER
provider to your AppModule
. This provider will call the initializeSkyid
factory function shown below which will set up the Skyid service so that it can be used in your application.
Use the code provided below as an example and implement it's functionality in your application. In this process ensure that the configuration you are providing matches that of your client as configured in Skyid. environment.ts
export const environment = {
// ...
"SKYID_BASE_PATH": "https://id.skyjoy.vn",
"MY_REALM": "my-realm",
"MY_APP_CLIENT_ID": "my-app-client-id",
"SKYID_REALM_PATH": "https://id.skyjoy.vn/realms/{{my-realm}}",
"APP_END_POINT": "http://localhost"
}
import { APP_INITIALIZER, NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { SkyidAngularModule, SkyidService } from 'skyid-angular';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
function initializeSkyid(skyid: SkyidService) {
return () =>
skyid.init({
config: {
url: `${SKYID_BASE_PATH}`,
realm: `${MY_REALM}`,
clientId: `${MY_APP_CLIENT_ID}`
},
initOptions: {
onLoad: 'check-sso',
silentCheckSsoRedirectUri:
window.location.origin + '/assets/silent-check-sso.html'
}
});
}
@NgModule({
declarations: [AppComponent],
imports: [AppRoutingModule, BrowserModule, SkyidAngularModule],
providers: [
{
provide: APP_INITIALIZER,
useFactory: initializeSkyid,
multi: true,
deps: [SkyidService]
}
],
bootstrap: [AppComponent]
})
export class AppModule {}
In the example we have set up Skyid to use a silent check-sso
. With this feature enabled, your browser will not do a full redirect to the Skyid server and back to your application, instead this action will be performed in a hidden iframe, so your application resources only need to be loaded and parsed once by the browser when the app is initialized and not again after the redirect back from Skyid to your app.
To ensure that Skyid can communicate through the iframe you will have to serve a static HTML asset from your application at the location provided in silentCheckSsoRedirectUri
.
Create a file called silent-check-sso.html
in the assets
directory of your application and paste in the contents as seen below.
<html>
<body>
<script>
parent.postMessage(location.href, location.origin);
</script>
</body>
</html>
If you want to know more about these options and various other capabilities of the Skyid client is recommended to read the JavaScript Adapter documentation.
Example project
If you want to see a complete overview a pre-configured client together with a working Skyid server make sure to check out the example project in this repository.
AuthGuard
A generic AuthGuard, SkyidAuthGuard
is provided to help you protect authenticated routes in your application. This guard provides you with information to see if the user is logged in and a list of roles from that belong to the user. In your implementation you just need to implement the desired logic to protect your routes.
To write your own implementation extend the SkyidAuthGuard
class and implement the isAccessAllowed
method. For example the code provided below checks if the user is authenticated and if not the user is requested to sign in. It also checks if the user has the correct roles which could be provided by passing the roles
field into the data of the route.
import { Injectable } from '@angular/core';
import {
ActivatedRouteSnapshot,
Router,
RouterStateSnapshot
} from '@angular/router';
import { SkyidAuthGuard, SkyidService } from 'skyid-angular';
@Injectable({
providedIn: 'root'
})
export class AuthGuard extends SkyidAuthGuard {
constructor(
protected readonly router: Router,
protected readonly skyid: SkyidService
) {
super(router, skyid);
}
public async isAccessAllowed(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
) {
// Force the user to log in if currently unauthenticated.
if (!this.authenticated) {
await this.skyid.login({
redirectUri: window.location.origin + state.url
});
}
// Get the roles required from the route.
const requiredRoles = route.data.roles;
// Allow the user to proceed if no additional roles are required to access the route.
if (!Array.isArray(requiredRoles) || requiredRoles.length === 0) {
return true;
}
// Allow the user to proceed if all the required roles are present.
return requiredRoles.every((role) => this.roles.includes(role));
}
}
HttpClient Interceptor
By default, all HttpClient requests will add the Authorization header in the format of: Authorization: Bearer **_TOKEN_**
.
There is also the possibility to exclude requests that should not have the authorization header. This is accomplished by implementing the shouldAddToken
method in the skyid initialization. For example, the configuration below will not add the token to GET
requests that match the paths /assets
or /clients/public
:
await skyid.init({
config: {
url: `${SKYID_BASE_PATH}`,
realm: `${MY_REALM}`,
clientId: `${MY_APP_CLIENT_ID}`
},
initOptions: {
onLoad: 'check-sso',
silentCheckSsoRedirectUri:
window.location.origin + '/assets/silent-check-sso.html'
},
shouldAddToken: (request) => {
const { method, url } = request;
const isGetRequest = 'GET' === method.toUpperCase();
const acceptablePaths = ['/assets', '/clients/public'];
const isAcceptablePathMatch = acceptablePaths.some((path) =>
url.includes(path)
);
return !(isGetRequest && isAcceptablePathMatch);
}
});
In the case where your application frequently polls an authenticated endpoint, you will find that users will not be logged out automatically over time. If that functionality is not desirable, you can add an http header to the polling requests then configure the shouldUpdateToken
option in the skyid initialization.
In the example below, any http requests with the header token-update: false
will not trigger the user's skyid token to be updated.
await skyid.init({
config: {
url: `${SKYID_BASE_PATH}`,
realm: `${MY_REALM}`,
clientId: `${MY_APP_CLIENT_ID}`
},
initOptions: {
onLoad: 'check-sso',
silentCheckSsoRedirectUri:
window.location.origin + '/assets/silent-check-sso.html'
},
bearerExcludedUrls: ['/assets', '/clients/public'],
shouldUpdateToken(request) {
return !request.headers.get('token-update') === 'false';
}
});
Skyid-js Events
The callback events from skyid-js are available through a RxJS subject which is defined by skyidEvents$
.
For example you make skyid-angular auto refreshing your access token when expired:
skyidService.skyidEvents$.subscribe({
next(event) {
if (event.type == SkyidEventType.OnTokenExpired) {
skyidService.updateToken(20);
}
}
});