Skip to content

Commit

Permalink
feat: add new token manager for ICP4D (#26)
Browse files Browse the repository at this point in the history
feat: add new token manager for ICP4D
  • Loading branch information
germanattanasio authored Jun 5, 2019
2 parents 6d24f0a + dc3165d commit 2097a64
Show file tree
Hide file tree
Showing 19 changed files with 1,284 additions and 473 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ node_modules/
**/*v*.js
!test/**/*.js
lib/*.js
auth/*.js
index.js
scripts/typedoc/
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ doc/
.env
.eslintcache
lib/*.js
auth/*.js
iam-token-manager/*.js
index.js
.nyc_output
Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,28 @@ import { BaseService } from 'ibm-cloud-sdk-core';
class YourSDK extends BaseService { ... }
```

## Authentication Types
There are several flavors of authentication supported in this package. To specify the intended authentication pattern to use, the user can pass in the parameter `authentication_type`. This parameter is optional, but it may become required in a future major release. The options for this parameter are `basic`, `iam`, and `icp4d`.

### basic
This indicates Basic Auth is to be used. Users will pass in a `username` and `password` and the SDK will generate a Basic Auth header to send with requests to the service.

### iam
This indicates that IAM token authentication is to be used. Users can pass in an `iam_apikey` or an `iam_access_token`. If an API key is used, the SDK will manage the token for the user. In either case, the SDK will generate a Bearer Auth header to send with requests to the service.

### icp4d
This indicates that the service is an instance of ICP4D, which has its own version of token authentication. Users can pass in a `username` and `password`, or an `icp4d_access_token`. If a username and password is given, the SDK will manage the token for the user.
A `url` is **required** for this type. In order to use an SDK-managed token with ICP4D authentication, this option **must** be passed in.

## Available Modules
### BaseService
This Class is the base class that all generated service-specific classes inherit from. It implements credentials handling and other shared behavior.

### IamTokenManagerV1
This Class contains logic for managing an IAM token over its lifetime. Tokens can be requested or set manually. When requested, the token manager will either return the current token, request a new token or refresh the current token if it is expired. If a token is manually set, it must be managed by the user.
This Class contains logic for managing an IAM token over its lifetime. Tokens can be requested or set manually. When requested, the token manager will either return the current token or request a new token if one is not saved or the the current token is expired. If a token is manually set, it must be managed by the user.

### Icp4dTokenManagerV1
This Class is similar in function to IamTokenManagerV1. The only difference is that the `url` parameter is required, it takes a `username` and `password` instead of an API key, and manages tokens for instances of ICP4D. To use this token manager in an SDK, the parameter `authentication_type` must be set to `icp4d` in the constructor.

### isFileParam
This function takes an Object and returns `true` if the object is a Stream, a Buffer, has a `value` property, or has a `data` property that is a file param (checked recursively).
Expand Down
158 changes: 158 additions & 0 deletions auth/iam-token-manager-v1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/**
* Copyright 2019 IBM Corp. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import extend = require('extend');
import { JwtTokenManagerV1 } from './jwt-token-manager-v1';
import { computeBasicAuthHeader } from './utils';

/**
* Check for only one of two elements being defined.
* Returns true if a is defined and b is undefined,
* or vice versa. Returns false if both are defined
* or both are undefined.
*
* @param {any} a - The first object
* @param {any} b - The second object
* @returns {boolean}
*/
function onlyOne(a: any, b: any): boolean {
return Boolean((a && !b) || (b && !a));
}

const CLIENT_ID_SECRET_WARNING = 'Warning: Client ID and Secret must BOTH be given, or the defaults will be used.';

export type Options = {
url?: string;
iamUrl?: string;
iamApikey?: string;
accessToken?: string;
iamAccessToken?: string;
iamClientId?: string;
iamClientSecret?: string;
}

// this interface is a representation of the response
// object from the IAM service, hence the snake_case
// parameter names
export interface IamTokenData {
access_token: string;
refresh_token: string;
token_type: string;
expires_in: number;
expiration: number;
}

export class IamTokenManagerV1 extends JwtTokenManagerV1 {
private iamApikey: string;
private iamClientId: string;
private iamClientSecret: string;

/**
* IAM Token Manager Service
*
* Retreives and stores IAM access tokens.
*
* @param {Object} options
* @param {String} options.iamApikey
* @param {String} options.iamAccessToken
* @param {String} options.iamUrl - url of the iam api to retrieve tokens from
* @constructor
*/
constructor(options: Options) {
super(options);

this.url = this.url || options.iamUrl || 'https://iam.cloud.ibm.com/identity/token';

if (options.iamApikey) {
this.iamApikey = options.iamApikey;
}
if (options.iamAccessToken) {
this.userAccessToken = options.iamAccessToken;
}
if (options.iamClientId) {
this.iamClientId = options.iamClientId;
}
if (options.iamClientSecret) {
this.iamClientSecret = options.iamClientSecret;
}
if (onlyOne(options.iamClientId, options.iamClientSecret)) {
// tslint:disable-next-line
console.log(CLIENT_ID_SECRET_WARNING);
}
}

/**
* Set the IAM 'client_id' and 'client_secret' values.
* These values are used to compute the Authorization header used
* when retrieving the IAM access token.
* If these values are not set, then a default Authorization header
* will be used when interacting with the IAM token server.
*
* @param {string} iamClientId - The client id
* @param {string} iamClientSecret - The client secret
* @returns {void}
*/
public setIamAuthorizationInfo(iamClientId: string, iamClientSecret: string): void {
this.iamClientId = iamClientId;
this.iamClientSecret = iamClientSecret;
if (onlyOne(iamClientId, iamClientSecret)) {
// tslint:disable-next-line
console.log(CLIENT_ID_SECRET_WARNING);
}
}

/**
* Callback for handling response.
*
* @callback requestTokenCallback
* @param {Error} An error if there is one, null otherwise
* @param {Object} The response if request is successful, null otherwise
*/
/**
* Request an IAM token using an API key.
*
* @param {requestTokenCallback} callback - The callback that handles the response.
* @returns {void}
*/
protected requestToken(callback: Function): void {
// Use bx:bx as default auth header creds.
let clientId = 'bx';
let clientSecret = 'bx';

// If both the clientId and secret were specified by the user, then use them.
if (this.iamClientId && this.iamClientSecret) {
clientId = this.iamClientId;
clientSecret = this.iamClientSecret;
}

const parameters = {
options: {
url: this.url,
method: 'POST',
headers: {
'Content-type': 'application/x-www-form-urlencoded',
Authorization: computeBasicAuthHeader(clientId, clientSecret),
},
form: {
grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
apikey: this.iamApikey,
response_type: 'cloud_iam'
},
}
};
this.requestWrapperInstance.sendRequest(parameters, callback);
}
}
109 changes: 109 additions & 0 deletions auth/icp4d-token-manager-v1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* Copyright 2019 IBM Corp. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import extend = require('extend');
import { JwtTokenManagerV1 } from './jwt-token-manager-v1';
import { computeBasicAuthHeader } from './utils';

export type Options = {
url: string;
accessToken?: string;
username?: string;
password?: string;
disableSslVerification?: boolean;
}

// this interface is a representation of the response
// object from the ICP4D authentication service
export interface IcpTokenData {
username: string;
role: string;
permissions: string[];
sub: string;
iss: string;
aud: string;
uid: string;
_messageCode_: string;
message: string;
accessToken: string;
}

export class Icp4dTokenManagerV1 extends JwtTokenManagerV1 {
private username: string;
private password: string;

/**
* ICP Token Manager Service
*
* Retreives and stores ICP access tokens.
*
* @param {Object} options
* @param {String} options.username
* @param {String} options.password
* @param {String} options.accessToken - user-managed access token
* @param {String} options.url - URL for the ICP4D cluster
* @param {Boolean} options.disableSslVerification - disable SSL verification for token request
* @constructor
*/
constructor(options: Options) {
super(options);

this.tokenName = 'accessToken';

if (this.url) {
this.url = this.url + '/v1/preauth/validateAuth';
} else if (!this.userAccessToken) {
// url is not needed if the user specifies their own access token
throw new Error('`url` is a required parameter for Icp4dTokenManagerV1');
}

if (options.username) {
this.username = options.username;
}
if (options.password) {
this.password = options.password;
}
// username and password are required too, unless there's access token
this.rejectUnauthorized = !options.disableSslVerification;
}

/**
* Callback for handling response.
*
* @callback requestTokenCallback
* @param {Error} An error if there is one, null otherwise
* @param {Object} The response if request is successful, null otherwise
*/
/**
* Request an ICP token using a basic auth header.
*
* @param {requestTokenCallback} callback - The callback that handles the response.
* @returns {void}
*/
protected requestToken(callback: Function): void {
const parameters = {
options: {
url: this.url,
method: 'GET',
headers: {
Authorization: computeBasicAuthHeader(this.username, this.password),
},
rejectUnauthorized: this.rejectUnauthorized,
}
};
this.requestWrapperInstance.sendRequest(parameters, callback);
}
}
20 changes: 20 additions & 0 deletions auth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright 2019 IBM Corp. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export { IamTokenManagerV1 } from './iam-token-manager-v1';
export { Icp4dTokenManagerV1 } from './icp4d-token-manager-v1';
export { JwtTokenManagerV1 } from './jwt-token-manager-v1';
export * from './utils';
Loading

0 comments on commit 2097a64

Please sign in to comment.