Skip to content

Commit

Permalink
updates to node.js clientruntime (#1070)
Browse files Browse the repository at this point in the history
* inital commit

* minor update

* some more updates

* UserTokenCredentials use the token cache to retrieve tokens

* updates to node.js clientruntime

* update readme
  • Loading branch information
amarzavery committed May 22, 2016
1 parent 3cb1bec commit f9a1606
Showing 10 changed files with 260 additions and 92 deletions.
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ namespace Microsoft.Rest.Generator.Azure.NodeJS
{
public class AzureNodeJSCodeGenerator : NodeJSCodeGenerator
{
private const string ClientRuntimePackage = "ms-rest-azure version 1.14.0";
private const string ClientRuntimePackage = "ms-rest-azure version 1.14.2";

// List of models with paging extensions.
private IList<PageTemplateModel> pageModels;
2 changes: 1 addition & 1 deletion AutoRest/Generators/NodeJS/NodeJS/NodeJSCodeGenerator.cs
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ namespace Microsoft.Rest.Generator.NodeJS
{
public class NodeJSCodeGenerator : CodeGenerator
{
private const string ClientRuntimePackage = "ms-rest version 1.14.0";
private const string ClientRuntimePackage = "ms-rest version 1.14.2";

public NodeJsCodeNamer Namer { get; private set; }

12 changes: 10 additions & 2 deletions ClientRuntimes/NodeJS/ms-rest-azure/README.md
Original file line number Diff line number Diff line change
@@ -45,10 +45,18 @@ Otherwise it is better to use the above mechanism (interactive login).
});
```

### ServicePrincipal authentication
#### Login with service principal name and secret
```javascript
var credentials = new msRestAzure.ApplicationTokenCredentials('your-client-id', 'your-domain', 'your-secret');
var someAzureServiceClient = require('azure-arm-someService');
msRestAzure.loginWithServicePrincipalSecret(clientId, secret, domain, function(err, credentials) {
var client = new someAzureServiceClient(credentials, 'your-subscriptionId');
client.someOperationGroup.method(param1, param2, function(err, result) {
if (err) console.log(err);
console.log(result);
});
});
```

### Non-Interactive Authentication
If you need to create an automation account for non interactive or scripting scenarios then please take a look at the documentation over [here](https://github.com/Azure/azure-sdk-for-node/blob/master/Documentation/Authentication.md).

Original file line number Diff line number Diff line change
@@ -60,6 +60,39 @@ function ApplicationTokenCredentials(clientId, domain, secret, options) {
this.context = new adal.AuthenticationContext(authorityUrl, this.environment.validateAuthority, this.tokenCache);
}

function _retrieveTokenFromCache (callback) {
//For service principal userId and clientId are the same thing. Since the token has _clientId property we shall
//retrieve token using it.
this.context.acquireToken(this.environment.activeDirectoryResourceId, null, this.clientId, function (err, result) {
if (err) return callback(err);
return callback(null, result);
});
}

/**
* Tries to get the token from cache initially. If that is unsuccessfull then it tries to get the token from ADAL.
* @param {function} callback The callback in the form (err, result)
* @return {function} callback
* {Error} [err] The error if any
* {object} [tokenResponse] The tokenResponse (tokenType and accessToken are the two important properties).
*/
ApplicationTokenCredentials.prototype.getToken = function (callback) {
var self = this;
_retrieveTokenFromCache.call(this, function (err, result) {
if (err) {
//Some error occured in retrieving the token from cache. May be the cache was empty or the access token expired. Let's try again.
self.context.acquireTokenWithClientCredentials(self.environment.activeDirectoryResourceId, self.clientId, self.secret, function (err, tokenResponse) {
if (err) {
return callback(new Error('Failed to acquire token for application with the provided secret. \n' + err));
}
return callback(null, tokenResponse);
});
} else {
return callback(null, result);
}
});
};

/**
* Signs a request with the Authentication header.
*
@@ -68,15 +101,11 @@ function ApplicationTokenCredentials(clientId, domain, secret, options) {
* @return {undefined}
*/
ApplicationTokenCredentials.prototype.signRequest = function (webResource, callback) {
var self = this;
self.context.acquireTokenWithClientCredentials(self.environment.activeDirectoryResourceId, self.clientId, self.secret, function (err, result) {
if (err) {
return callback(new Error('Failed to acquire token for application. \n' + err));
}

this.getToken(function (err, result) {
if (err) return callback(err);
webResource.headers[Constants.HeaderConstants.AUTHORIZATION] =
util.format('%s %s', self.authorizationScheme, result.accessToken);
callback(null);
util.format('%s %s', result.tokenType, result.accessToken);
return callback(null);
});
};

Original file line number Diff line number Diff line change
@@ -67,11 +67,35 @@ function UserTokenCredentials(clientId, domain, username, password, options) {
this.context = new adal.AuthenticationContext(authorityUrl, this.environment.validateAuthority, this.tokenCache);
}

UserTokenCredentials.prototype.retrieveTokenFromCache = function (callback) {
var self = this;
self.context.acquireToken(self.environment.activeDirectoryResourceId, self.username, self.clientId, function (err, result) {
function _retrieveTokenFromCache(callback) {
this.context.acquireToken(this.environment.activeDirectoryResourceId, this.username, this.clientId, function (err, result) {
if (err) return callback(err);
return callback(null, result.tokenType, result.accessToken);
return callback(null, result);
});
}

/**
* Tries to get the token from cache initially. If that is unsuccessfull then it tries to get the token from ADAL.
* @param {function} callback The callback in the form (err, result)
* @return {function} callback
* {Error} [err] The error if any
* {object} [tokenResponse] The tokenResponse (tokenType and accessToken are the two important properties).
*/
UserTokenCredentials.prototype.getToken = function (callback) {
var self = this;
_retrieveTokenFromCache.call(this, function (err, result) {
if (err) {
//Some error occured in retrieving the token from cache. May be the cache was empty. Let's try again.
self.context.acquireTokenWithUsernamePassword(self.environment.activeDirectoryResourceId, self.username,
self.password, self.clientId, function (err, tokenResponse) {
if (err) {
return callback(new Error('Failed to acquire token for the user. \n' + err));
}
return callback(null, tokenResponse);
});
} else {
return callback(null, result);
}
});
};

@@ -83,9 +107,10 @@ UserTokenCredentials.prototype.retrieveTokenFromCache = function (callback) {
* @return {undefined}
*/
UserTokenCredentials.prototype.signRequest = function (webResource, callback) {
return this.retrieveTokenFromCache(function(err, scheme, token) {
this.getToken(function (err, result) {
if (err) return callback(err);
webResource.headers[Constants.HeaderConstants.AUTHORIZATION] = util.format('%s %s', scheme, token);
webResource.headers[Constants.HeaderConstants.AUTHORIZATION] =
util.format('%s %s', result.tokenType, result.accessToken);
return callback(null);
});
};
102 changes: 98 additions & 4 deletions ClientRuntimes/NodeJS/ms-rest-azure/lib/index.d.ts
Original file line number Diff line number Diff line change
@@ -150,13 +150,40 @@ export interface AzureTokenCredentialsOptions {
*/
authorizationScheme?: string;

// TODO: What type should this really have? How is it used?
/**
* The token cache. Default value is null.
* The token cache. Default value is MemoryCache from adal.
*/
tokenCache?: any;
}

export interface LoginWithUsernamePasswordOptions extends AzureTokenCredentialsOptions {
/**
* The domain or tenant id containing this application. Default value is 'common'.
*/
domain?: string;

/**
* The active directory application client id.
* See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
* for an example.
*/
clientId?: string
}

export interface DeviceTokenCredentialsOptions extends LoginWithUsernamePasswordOptions {
/**
* The user name for account in the form: '[email protected]'. Default value is '[email protected]'.
*/
username?: string;
}

export interface InteractiveLoginOptions extends DeviceTokenCredentialsOptions {
/**
* The language code specifying how the message should be localized to. Default value 'en-us'.
*/
language?: string;
}

export class ApplicationTokenCredentials extends msRest.ServiceClientCredentials {
/**
* Creates a new ApplicationTokenCredentials object.
@@ -179,12 +206,79 @@ export class UserTokenCredentials extends msRest.ServiceClientCredentials {
* @param {string} domain The domain or tenant id containing this application.
* @param {string} username The user name for the Organization Id account.
* @param {string} password The password for the Organization Id account.
* @param {string} clientRedirectUri The Uri where the user will be redirected after authenticating with AD.
* @param {AzureTokenCredentialsOptions} options Object representing optional parameters.
*/
constructor(clientId: string, domain: string, username: string, password: string, clientRedirectUri: string, options?: AzureTokenCredentialsOptions);
constructor(clientId: string, domain: string, username: string, password: string, options?: AzureTokenCredentialsOptions);
}

export class DeviceTokenCredentials extends msRest.ServiceClientCredentials {
/**
* Creates a new DeviceTokenCredentials object.
* @param {DeviceTokenCredentialsOptions} options Object representing optional parameters.
*/
constructor(options?: DeviceTokenCredentialsOptions);
}

// TODO: WHAT SHOULD WE EXPOSE HERE?
export class BaseResource {
}

/**
* Provides a url and code that needs to be copy and pasted in a browser and authenticated over there. If successful, the user will get a
* DeviceTokenCredentials object
*
* @param {InteractiveLoginOptions} [options] The parameter options.
*
* @param {function} callback
*
* @returns {function} callback(err, credentials)
*
* {Error} [err] - The Error object if an error occurred, null otherwise.
*
* {DeviceTokenCredentials} [credentials] - The DeviceTokenCredentials object
*/
export function interactiveLogin (options?: InteractiveLoginOptions, callback);

/**
* Provides a UserTokenCredentials object. This method is applicable only for organizational ids that are not 2FA enabled.
* Otherwise please use interactive login.
*
* @param {string} username The user name for the Organization Id account.
*
* @param {string} password The password for the Organization Id account.
*
* @param {LoginWithUsernamePasswordOptions} [options] The parameter options.
*
* @param {function} callback
*
* @returns {function} callback(err, credentials)
*
* {Error} [err] - The Error object if an error occurred, null otherwise.
*
* {UserTokenCredentials} [credentials] - The UserTokenCredentials object
*/
export function loginWithUsernamePassword (username: string, password: string, options?: LoginWithUsernamePasswordOptions, callback);


/**
* Provides an ApplicationTokenCredentials object.
*
* @param {string} clientId The active directory application client id also known as the SPN (ServicePrincipal Name).
* See {@link https://azure.microsoft.com/en-us/documentation/articles/active-directory-devquickstarts-dotnet/ Active Directory Quickstart for .Net}
* for an example.
*
* @param {string} secret The application secret for the service principal.
*
* @param {string} domain The domain or tenant id containing this application.
*
* @param {AzureTokenCredentialsOptions} [options] The parameter options.
*
* @param {function} callback
*
* @returns {function} callback(err, credentials)
*
* {Error} [err] - The Error object if an error occurred, null otherwise.
*
* {ApplicationTokenCredentials} [credentials] - The ApplicationTokenCredentials object
*/
export function loginWithServicePrincipalSecret (clientId: string, secret: string, domain: string, options?: AzureTokenCredentialsOptions, callback);
Loading

0 comments on commit f9a1606

Please sign in to comment.