Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

keycloak-auth-utils

Package Overview
Dependencies
Maintainers
1
Versions
39
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

keycloak-auth-utils - npm Package Compare versions

Comparing version 0.0.5 to 0.0.6

.grunt/grunt-gh-pages/gh-pages/src/.nojekyll

79

config.js

@@ -1,2 +0,2 @@

/*
/*!
* Copyright 2014 Red Hat, Inc.

@@ -22,2 +22,14 @@ *

/**
* Construct a configuration object.
*
* A configuration object may be constructed with either
* a path to a `keycloak.json` file (which defaults to
* `$PWD/keycloak.json` if not present, or with a configuration
* object akin to what parsing `keycloak.json` provides.
*
* @param {String|Object} config Configuration path or details.
*
* @constructor
*/
function Config(config) {

@@ -35,2 +47,7 @@ if ( ! config ) {

/**
* Load configuration from a path.
*
* @param {String} configPath Path to a `keycloak.json` configuration.
*/
Config.prototype.loadConfiguration = function(configPath) {

@@ -42,13 +59,71 @@ var json = fs.readFileSync( configPath );

/**
* Configure this `Config` object.
*
* This will set the internal configuration details. The details
* may come from a `keycloak.json` formatted object (with names such
* as `auth-server-url`) or from an existing `Config` object (using
* names such as `authServerUrl`).
*
* @param {Object} config The configuration to instill.
*/
Config.prototype.configure = function(config) {
this.authServerUrl = config['auth-server-url'] || config.authServerUrl;
/**
* Realm ID
* @type {String}
*/
this.realm = config['realm'] || config.realm;
/**
* Client/Application ID
* @type {String}
*/
this.clientId = config['resource'] || config.clientId;
/**
* Client/Application secret
* @type {String}
*/
this.secret = (config['credentials'] || {}).secret || config.secret;
/**
* If this is a public application or confidential.
* @type {String}
*/
this.public = config['public-client'] || config.public || false;
/**
* Authentication server URL
* @type {String}
*/
this.authServerUrl = config['auth-server-url'] || config.authServerUrl;
/**
* Root realm URL.
* @type {String}
*/
this.realmUrl = this.authServerUrl + '/realms/' + this.realm;
/**
* Root realm admin URL.
* @type {String} */
this.realmAdminUrl = this.authServerUrl + '/admin/realms/' + this.realm;
var plainKey = config['realm-public-key'];
/**
* Formatted public-key.
* @type {String}
*/
this.publicKey = "-----BEGIN PUBLIC KEY-----\n";
for ( i = 0 ; i < plainKey.length ; i = i + 64 ) {
this.publicKey += plainKey.substring( i, i + 64 );
this.publicKey += "\n";
}
this.publicKey += "-----END PUBLIC KEY-----\n";
};
module.exports = Config;

@@ -1,2 +0,2 @@

/*
/*!
* Copyright 2014 Red Hat, Inc.

@@ -21,17 +21,40 @@ *

var URL = require('url');
var http = require('http');
var URL = require('url');
var http = require('http');
var crypto = require('crypto');
var Form = require('./form');
var Grant = require('./grant');
var Token = require('./token');
/**
* Construct a grant manager.
*
* @param {Config} config Config object.
*
* @constructor
*/
function GrantManager(config) {
this.realmUrl = config.realmUrl;
this.clientId = config.clientId;
this.secret = config.secret;
this.realmUrl = config.realmUrl;
this.clientId = config.clientId;
this.secret = config.secret;
this.publicKey = config.publicKey;
this.notBefore = 0;
}
/**
* Use the direct grant API to obtain a grant from Keycloak.
*
* The direct grant API must be enabled for the configured realm
* for this method to work. This function ostensibly provides a
* non-interactive, programatic way to login to a Keycloak realm.
*
* This method can either accept a callback as the last parameter
* or return a promise.
*
* @param {String} username The username.
* @param {String} password The cleartext password.
* @param {Function} callback Optional callback, if not using promises.
*/
GrantManager.prototype.obtainDirectly = function(username, password, callback) {
var deferred = Q.defer();

@@ -50,3 +73,3 @@

var params = new Form( {
var params = new Form({
username: username,

@@ -72,6 +95,5 @@ password: password,

try {
var grant = JSON.parse( json );
deferred.resolve( new Grant( grant ) );
return deferred.resolve( self.createGrant( json ) );
} catch (err) {
deferred.reject( err );
return deferred.reject( err );
}

@@ -88,8 +110,83 @@ });

/**
* Obtain a grant from a previous interactive login which results in a code.
*
* This is typically used by servers which receive the code through a
* redirect_uri when sending a user to Keycloak for an interactive login.
*
* An optional session ID and host may be provided if there is desire for
* Keycloak to be aware of this information. They may be used by Keycloak
* when session invalidation is triggered from the Keycloak console itself
* during its postbacks to `/k_logout` on the server.
*
* This method returns or promise or may optionally take a callback function.
*
* @param {String} code The code from a successful login redirected from Keycloak.
* @param {String} sessionId Optional opaque session-id.
* @param {String} sessionHost Optional session host for targetted Keycloak console post-backs.
* @param {Function} callback Optional callback, if not using promises.
*/
GrantManager.prototype.obtainFromCode = function(code, sessionId, sessionHost, callback) {
var deferred = Q.defer();
var self = this;
var params = 'code=' + code + '&application_session_state=' + sessionId + '&application_session_host=' + sessionHost;
var options = URL.parse( this.realmUrl + '/tokens/access/codes' );
options.method = 'POST';
options.agent = false;
options.headers = {
'Content-Length': params.length,
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic ' + new Buffer( this.clientId + ':' + this.secret ).toString('base64' ),
};
var request = http.request( options, function(response) {
var json = '';
response.on('data', function(d) {
json += d.toString();
});
response.on( 'end', function() {
try {
return deferred.resolve( self.createGrant( json ) );
} catch (err) {
return deferred.reject( err );
}
});
} );
request.write( params );
request.end();
return deferred.promise.nodeify( callback );
};
/**
* Ensure that a grant is *fresh*, refreshing if required & possible.
*
* If the access_token is not expired, the grant is left untouched.
*
* If the access_token is expired, and a refresh_token is available,
* the grant is refreshed, in place (no new object is created),
* and returned.
*
* If the access_token is expired and no refresh_token is available,
* an error is provided.
*
* The method may either return a promise or take an optional callback.
*
* @param {Grant} grant The grant object to ensure freshness of.
* @param {Function} callback Optional callback if promises are not used.
*/
GrantManager.prototype.ensureFreshness = function(grant, callback) {
if ( ! grant.expired() ) {
if ( ! grant.isExpired() ) {
return Q(grant).nodeify( callback );
}
if ( ! grant.refresh_token ) {
return Q.reject( new Error( "Unable to refresh without a refresh token" )).nodeify( callback );
}
var self = this;

@@ -108,5 +205,5 @@ var deferred = Q.defer();

var params = new Form( {
var params = new Form({
grant_type: 'refresh_token',
refresh_token: grant.refresh_token,
refresh_token: grant.refresh_token.token,
});

@@ -120,5 +217,8 @@

response.on( 'end', function() {
var data = JSON.parse( json );
grant.update( data );
deferred.resolve(grant);
try {
grant.update( self.createGrant( json ) );
return deferred.resolve(grant);
} catch (err) {
return deferred.reject( err );
}
});

@@ -134,2 +234,200 @@

/**
* Perform live validation of an `access_token` against the Keycloak server.
*
* @param {Token|String} token The token to validate.
* @param {Function} callback Callback function if not using promises.
*
* @return {boolean} `false` if the token is invalid, or the same token if valid.
*/
GrantManager.prototype.validateAccessToken = function(token, callback) {
var deferred = Q.defer();
var self = this;
var url = this.realmUrl + '/tokens/validate';
var options = URL.parse( url );
options.method = 'GET';
var t;
if ( typeof token == 'string' ) {
t = token;
} else {
t = token.token;
}
var params = new Form({
access_token: t,
});
options.path = options.path + '?' + params.encode();
var req = http.request( options, function(response) {
var json = '';
response.on('data', function(d) {
json += d.toString();
});
response.on( 'end', function() {
var data = JSON.parse( json );
if ( data.error ) {
return deferred.resolve( false );
}
return deferred.resolve( token );
});
});
req.end();
return deferred.promise.nodeify( callback );
};
/**
* Create a `Grant` object from a string of JSON data.
*
* This method creates the `Grant` object, including
* the `access_token`, `refresh_token` and `id_token`
* if available, and validates each for expiration and
* against the known public-key of the server.
*
* @param {String} rawData The raw JSON string received from the Keycloak server or from a client.
* @return {Grant} A validated Grant.
*/
GrantManager.prototype.createGrant = function(rawData) {
var grantData = JSON.parse( rawData );
var access_token;
var refresh_token;
var id_token;
if ( grantData.access_token ) {
access_token = new Token( grantData.access_token, this.clientId );
}
if ( grantData.refresh_token ) {
refresh_token = new Token( grantData.refresh_token );
}
if ( grantData.id_token ) {
id_token = new Token( grantData.id_token );
}
var grant = new Grant( {
access_token: access_token,
refresh_token: refresh_token,
id_token: id_token,
expires_in: grantData.expires_in,
token_type: grantData.token_type,
});
grant.__raw = rawData;
return this.validateGrant( grant );
};
/**
* Validate the grant and all tokens contained therein.
*
* This method filters a grant (in place), by nulling out
* any invalid tokens. After this method returns, the
* passed in grant will only contain valid tokens.
*
* @param {Grant} The grant to validate.
*/
GrantManager.prototype.validateGrant = function(grant) {
grant.access_token = this.validateToken( grant.access_token );
grant.refresh_token = this.validateToken( grant.refresh_token );
grant.id_token = this.validateToken( grant.id_token );
return grant;
};
/**
* Validate a token.
*
* This method accepts a token, and either returns the
* same token object, if valid, else, it returns `undefined`
* if any of the following errors are seen:
*
* - The token was undefined in the first place.
* - The token is expired.
* - The token is not expired, but issued before the current *not before* timestamp.
* - The token signature does not verify against the known realm public-key.
*
* @return {Token} The same token passed in, or `undefined`
*/
GrantManager.prototype.validateToken = function(token) {
if ( ! token ) {
return;
}
if ( token.isExpired() ) {
return;
}
if ( token.content.issuedAt < this.notBefore ) {
return;
}
var verify = crypto.createVerify('RSA-SHA256');
verify.update( token.signed );
if ( ! verify.verify( this.publicKey, token.signature, 'base64' ) ) {
return;
}
return token;
};
GrantManager.prototype.getAccount = function(token, callback) {
var deferred = Q.defer();
var self = this;
var url = this.realmUrl + '/account';
var options = URL.parse( url );
options.method = 'GET';
var t;
if ( typeof token == 'string' ) {
t = token;
} else {
t = token.token;
}
options.headers = {
'Authorization': 'Bearer ' + t,
'Accept': 'application/json',
};
var req = http.request( options, function(response) {
console.log( "RESPONSE", response.statusCode );
if ( response.statusCode < 200 || response.statusCode >= 300 ) {
return deferred.reject( "Error fetching account" );
}
var json = '';
response.on('data', function(d) {
json += d.toString();
});
response.on( 'end', function() {
var data = JSON.parse( json );
if ( data.error ) {
return deferred.reject( data );
}
console.log( "ACCOUNT", data );
return deferred.resolve( data );
});
});
req.end();
return deferred.promise.nodeify( callback );
};
module.exports = GrantManager;

63

grant.js

@@ -1,2 +0,2 @@

/*
/*!
* Copyright 2014 Red Hat, Inc.

@@ -17,9 +17,18 @@ *

var Form = require('./form');
var Q = require('q');
var http = require('http');
var URL = require('url');
/**
* Construct a new grant.
*
* The passed in argument may be another `Grant`, or any object with
* at least `access_token`, and optionally `refresh_token` and `id_token`,
* `token_type`, and `expires_in`. Each token should be an instance of
* `Token` if present.
*
* If the passed in object contains a field named `__raw` that is also stashed
* away as the verbatim raw `String` data of the grant.
*
* @param {Object} grant The `Grant` to copy, or a simple `Object` with similar fields.
*
* @constructor
*/
function Grant(grant) {

@@ -29,6 +38,8 @@ this.update( grant );

Grant.prototype.dump = function(token) {
console.log( JSON.parse( new Buffer( token.split('.')[1], 'base64' ) ) );
};
/**
* Update this grant in-place given data in another grant.
*
* This is used to avoid making client perform extra-bookkeeping
* to maintain the up-to-date/refreshed grant-set.
*/
Grant.prototype.update = function(grant) {

@@ -38,13 +49,39 @@ // intentional naming with under_scores instead of

// and to allow new Grant(new Grant(kc)) copy-ctor
this.access_token = grant.access_token;
this.refresh_token = grant.refresh_token;
this.id_token = grant.id_token;
this.token_type = grant.token_type;
this.expires_in = grant.expires_in;
this.__raw = grant.__raw;
};
Grant.prototype.expired = function() {
return true;
/**
* Returns the raw String of the grant, if available.
*
* If the raw string is unavailable (due to programatic construction)
* then `undefined` is returned.
*/
Grant.prototype.toString = function() {
return this.__raw;
};
/**
* Determine if this grant is expired/out-of-date.
*
* Determination is made based upon the expiration status of the `access_token`.
*
* An expired grant *may* be possible to refresh, if a valid
* `refresh_token` is available.
*
* @return {boolean} `true` if expired, otherwise `false`.
*/
Grant.prototype.isExpired = function() {
if ( ! this.access_token ) {
return true;
}
return this.access_token.isExpired();
};
module.exports = Grant;

@@ -7,13 +7,23 @@

pkg: grunt.file.readJSON('package.json'),
docco: {
debug: {
src: ['index.js', 'token-refresher.js', 'grant.js'],
doxx: {
all: {
src: '.',
target: 'doc',
options: {
output: 'doc/',
layout: 'classic'
ignore: 'Gruntfile.js,form.js,spec,node_modules,.git',
}
}
},
touch: {
src: [ 'doc/.nojekyll' ]
},
jshint: {
all: ['Gruntfile.js', '*.js', 'test/**/*.js']
},
'gh-pages': {
options: {
base: 'doc',
dotfiles: true,
},
src: ['**']
}

@@ -23,8 +33,10 @@ });

grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-docco');
grunt.loadNpmTasks('grunt-doxx');
grunt.loadNpmTasks('grunt-gh-pages');
grunt.loadNpmTasks('grunt-touch');
// Default task(s).
grunt.registerTask('default', ['jshint', 'docco']);
grunt.registerTask('default', ['jshint', 'doxx', 'touch']);
};

@@ -1,2 +0,2 @@

/*
/*!
* Copyright 2014 Red Hat, Inc.

@@ -18,3 +18,4 @@ *

/** Export all useful things.
*/
module.exports = {

@@ -24,4 +25,3 @@ Config: require('./config'),

Grant: require('./grant' ),
Form: require('./form'),
};
{
"name": "keycloak-auth-utils",
"version": "0.0.5",
"version": "0.0.6",
"description": "General Keycloak Utilities",

@@ -13,3 +13,2 @@ "main": "index.js",

"devDependencies": {
"jasmine": "^2.1.1",
"grunt": "^0.4.5",

@@ -19,3 +18,7 @@ "grunt-cli": "^0.1.13",

"grunt-contrib-uglify": "^0.6.0",
"grunt-docco": "^0.3.3"
"grunt-docco": "^0.3.3",
"grunt-doxx": "^0.1.2",
"grunt-gh-pages": "^0.9.1",
"grunt-touch": "^0.1.0",
"jasmine": "^2.1.1"
},

@@ -27,5 +30,5 @@ "dependencies": {

"type": "git",
"url": "http://github.com/bobmcwhirter/keycloak-auth-utils.git"
"url": "http://github.com/keycloak/keycloak-nodejs.git"
},
"bugs": "http://github.com/bobmcwhirter/keycloak-auth-utils/issues"
"bugs": "http://github.com/keycloak/keycloak-nodejs/issues"
}

@@ -8,4 +8,5 @@

* Can obtain a grant through the direct API
* Can renew any token using a `refresh_token`
* Can obtain a grant through the direct API using name/password.
* Can renew any token using a `refresh_token`.
* Validates grants/tokens.

@@ -16,1 +17,10 @@ ## `Grant`

## `Token`
Embodies JSON Web Token bits and checking for roles.
# Resources
* [GitHub](http://github.com/bobmcwhirter/keycloak-auth-utils)
* [Documentation](http://bobmcwhirter.github.io/keycloak-auth-utils)

@@ -38,3 +38,3 @@

expect( grant.access_token ).not.toBe( undefined );
expect( grant.access_token ).not.toBe( originalAccessToken );
expect( grant.access_token.token ).not.toBe( originalAccessToken.token );
})

@@ -44,2 +44,59 @@ .done( done );

it( 'should be able to validate a valid token', function(done) {
var originalAccessToken;
manager.obtainDirectly( 'lucy', 'lucy' )
.then( function(grant) {
originalAccessToken = grant.access_token;
return manager.validateAccessToken( grant.access_token )
})
.then( function(token) {
expect( token ).not.toBe( undefined );
expect( token ).toBe( originalAccessToken );
})
.done( done );
})
it( 'should be able to validate an invalid token', function(done) {
var originalAccessToken;
manager.obtainDirectly( 'lucy', 'lucy' )
.delay(3000)
.then( function(grant) {
originalAccessToken = grant.access_token;
return manager.validateAccessToken( grant.access_token )
})
.then( function(result) {
expect( result ).toBe( false );
})
.done( done );
})
it( 'should be able to validate a valid token string', function(done) {
var originalAccessToken;
manager.obtainDirectly( 'lucy', 'lucy' )
.then( function(grant) {
originalAccessToken = grant.access_token.token;
return manager.validateAccessToken( grant.access_token.token )
})
.then( function(token) {
expect( token ).not.toBe( undefined );
expect( token ).toBe( originalAccessToken );
})
.done( done );
})
it( 'should be able to validate an invalid token string', function(done) {
var originalAccessToken;
manager.obtainDirectly( 'lucy', 'lucy' )
.delay(3000)
.then( function(grant) {
originalAccessToken = grant.access_token.token;
return manager.validateAccessToken( grant.access_token.token )
})
.then( function(result) {
expect( result ).toBe( false );
})
.done( done );
})
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc