@nuvo-prime/np-samlify
Version:
High-level API for Single Sign On (SAML 2.0)
147 lines (128 loc) • 4.2 kB
text/typescript
/**
* @file metadata-idp.ts
* @author tngan
* @desc Metadata of identity provider
*/
import Metadata, { MetadataInterface } from './metadata';
import { MetadataIdpOptions, MetadataIdpConstructor } from './types';
import { namespace } from './urn';
import libsaml from './libsaml';
import { castArrayOpt, isNonEmptyArray, isString } from './utility';
import xml from 'xml';
export interface IdpMetadataInterface extends MetadataInterface {
}
/*
* @desc interface function
*/
export default function(meta: MetadataIdpConstructor) {
return new IdpMetadata(meta);
}
export class IdpMetadata extends Metadata {
constructor(meta: MetadataIdpConstructor) {
const isFile = isString(meta) || meta instanceof Buffer;
if (!isFile) {
const {
entityID,
signingCert,
encryptCert,
wantAuthnRequestsSigned = false,
nameIDFormat = [],
singleSignOnService = [],
singleLogoutService = [],
} = meta as MetadataIdpOptions;
const IDPSSODescriptor: any[] = [{
_attr: {
WantAuthnRequestsSigned: String(wantAuthnRequestsSigned),
protocolSupportEnumeration: namespace.names.protocol,
},
}];
for(const cert of castArrayOpt(signingCert)) {
IDPSSODescriptor.push(libsaml.createKeySection('signing', cert));
}
for(const cert of castArrayOpt(encryptCert)) {
IDPSSODescriptor.push(libsaml.createKeySection('encryption', cert));
}
if (isNonEmptyArray(nameIDFormat)) {
nameIDFormat.forEach(f => IDPSSODescriptor.push({ NameIDFormat: f }));
}
if (isNonEmptyArray(singleSignOnService)) {
singleSignOnService.forEach((a, indexCount) => {
const attr: any = {
Binding: a.Binding,
Location: a.Location,
};
if (a.isDefault) {
attr.isDefault = true;
}
IDPSSODescriptor.push({ SingleSignOnService: [{ _attr: attr }] });
});
} else {
throw new Error('ERR_IDP_METADATA_MISSING_SINGLE_SIGN_ON_SERVICE');
}
if (isNonEmptyArray(singleLogoutService)) {
singleLogoutService.forEach((a, indexCount) => {
const attr: any = {};
if (a.isDefault) {
attr.isDefault = true;
}
attr.Binding = a.Binding;
attr.Location = a.Location;
IDPSSODescriptor.push({ SingleLogoutService: [{ _attr: attr }] });
});
} else {
console.warn('Construct identity provider - missing endpoint of SingleLogoutService');
}
// Create a new metadata by setting
meta = xml([{
EntityDescriptor: [{
_attr: {
'xmlns': namespace.names.metadata,
'xmlns:assertion': namespace.names.assertion,
'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
entityID,
},
}, { IDPSSODescriptor }],
}]);
}
super(meta as string | Buffer, [
{
key: 'wantAuthnRequestsSigned',
localPath: ['EntityDescriptor', 'IDPSSODescriptor'],
attributes: ['WantAuthnRequestsSigned'],
},
{
key: 'singleSignOnService',
localPath: ['EntityDescriptor', 'IDPSSODescriptor', 'SingleSignOnService'],
index: ['Binding'],
attributePath: [],
attributes: ['Location']
},
]);
}
/**
* @desc Get the preference whether it wants a signed request
* @return {boolean} WantAuthnRequestsSigned
*/
isWantAuthnRequestsSigned(): boolean {
const was = this.meta.wantAuthnRequestsSigned;
if (was === undefined) {
return false;
}
return String(was) === 'true';
}
/**
* @desc Get the entity endpoint for single sign on service
* @param {string} binding protocol binding (e.g. redirect, post)
* @return {string/object} location
*/
getSingleSignOnService(binding: string): string | object {
if (isString(binding)) {
const bindName = namespace.binding[binding];
const service = this.meta.singleSignOnService[bindName];
if (service) {
return service;
}
}
return this.meta.singleSignOnService;
}
}