node-nlp
Version:
Library for NLU (Natural Language Understanding) done in Node.js
133 lines (112 loc) • 4.17 kB
text/typescript
/**
* @module botbuilder
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import * as request from 'request';
// tslint:disable-next-line:no-var-requires no-require-imports
const getPem: any = require('rsa-pem-from-mod-exp');
// tslint:disable-next-line:no-var-requires no-require-imports
const base64url: any = require('base64url');
export class OpenIdMetadata {
private url: string;
private lastUpdated: number = 0;
private keys: IKey[];
constructor(url: string) {
this.url = url;
}
public getKey(keyId: string): Promise<IOpenIdMetadataKey | null> {
return new Promise((resolve: any, reject: any): void => {
// If keys are more than 5 days old, refresh them
const now: number = new Date().getTime();
if (this.lastUpdated < (now - 1000 * 60 * 60 * 24 * 5)) {
this.refreshCache((err: any): void => {
if (err) {
//logger.error('Error retrieving OpenId metadata at ' + this.url + ', error: ' + err.toString());
// fall through and return cached key on error
reject(err);
}
// Search the cache even if we failed to refresh
const key: IOpenIdMetadataKey = this.findKey(keyId);
resolve(key);
});
} else {
// Otherwise read from cache
const key: IOpenIdMetadataKey = this.findKey(keyId);
resolve(key);
}
});
}
private refreshCache(cb: (err: Error) => void): void {
const options: request.Options = {
method: 'GET',
url: this.url,
json: true
};
request(options, (err: any, response: any, body: any) => {
if (!err && (response.statusCode && response.statusCode >= 400 || !body)) {
err = new Error(`Failed to load openID config: ${ response.statusCode }`);
}
if (err) {
cb(err);
} else {
const openIdConfig: IOpenIdConfig = <IOpenIdConfig>body;
const get_key_options: request.Options = {
method: 'GET',
url: openIdConfig.jwks_uri,
json: true
};
request(get_key_options, (get_key_error: Error, get_key_response: any, get_key_body: any) => {
if (!get_key_error && (get_key_response.statusCode && get_key_response.statusCode >= 400 || !get_key_body)) {
get_key_error = new Error(`Failed to load Keys: ${ get_key_response.statusCode }`);
}
if (!get_key_error) {
this.lastUpdated = new Date().getTime();
this.keys = <IKey[]>get_key_body.keys;
}
cb(get_key_error);
});
}
});
}
private findKey(keyId: string): IOpenIdMetadataKey | null {
if (!this.keys) {
return null;
}
for (const key of this.keys) {
if (key.kid === keyId) {
if (!key.n || !key.e) {
// Return null for non-RSA keys
return null;
}
const modulus: any = base64url.toBase64(key.n);
const exponent: string = key.e;
return { key: getPem(modulus, exponent), endorsements: key.endorsements } as IOpenIdMetadataKey;
}
}
return null;
}
}
interface IOpenIdConfig {
issuer: string;
authorization_endpoint: string;
jwks_uri: string;
id_token_signing_alg_values_supported: string[];
token_endpoint_auth_methods_supported: string[];
}
interface IKey {
kty: string;
use: string;
kid: string;
x5t: string;
n: string;
e: string;
x5c: string[];
endorsements?: string[];
}
export interface IOpenIdMetadataKey {
key: string;
endorsements?: string[];
}