[Pkg-javascript-commits] [node-kerberos] 01/02: Imported Upstream version 0.0.14

Christopher Hoskin christopher.hoskin at gmail.com
Mon Sep 14 21:33:23 UTC 2015


This is an automated email from the git hooks/post-receive script.

grinorcole-guest pushed a commit to branch master
in repository node-kerberos.

commit 02dbc84f5caa699a0f8b6f0d39759aa358c6e542
Author: Christopher Hoskin <christopher.hoskin at gmail.com>
Date:   Mon Sep 14 22:07:53 2015 +0100

    Imported Upstream version 0.0.14
---
 .gitignore                                       |  17 +
 .npmignore                                       |  18 +
 .travis.yml                                      |  20 +
 LICENSE                                          | 201 ++++++
 README.md                                        |   4 +
 binding.gyp                                      |  46 ++
 index.js                                         |   6 +
 lib/auth_processes/mongodb.js                    | 281 ++++++++
 lib/base64.c                                     | 134 ++++
 lib/base64.h                                     |  22 +
 lib/kerberos.cc                                  | 864 +++++++++++++++++++++++
 lib/kerberos.h                                   |  50 ++
 lib/kerberos.js                                  | 128 ++++
 lib/kerberos_context.cc                          | 116 +++
 lib/kerberos_context.h                           |  63 ++
 lib/kerberosgss.c                                | 688 ++++++++++++++++++
 lib/kerberosgss.h                                |  69 ++
 lib/sspi.js                                      |  15 +
 lib/win32/base64.c                               | 121 ++++
 lib/win32/base64.h                               |  18 +
 lib/win32/kerberos.cc                            |  51 ++
 lib/win32/kerberos.h                             |  60 ++
 lib/win32/kerberos_sspi.c                        | 244 +++++++
 lib/win32/kerberos_sspi.h                        | 106 +++
 lib/win32/worker.cc                              |   7 +
 lib/win32/worker.h                               |  38 +
 lib/win32/wrappers/security_buffer.cc            | 101 +++
 lib/win32/wrappers/security_buffer.h             |  48 ++
 lib/win32/wrappers/security_buffer.js            |  12 +
 lib/win32/wrappers/security_buffer_descriptor.cc | 182 +++++
 lib/win32/wrappers/security_buffer_descriptor.h  |  46 ++
 lib/win32/wrappers/security_buffer_descriptor.js |   3 +
 lib/win32/wrappers/security_context.cc           | 856 ++++++++++++++++++++++
 lib/win32/wrappers/security_context.h            |  74 ++
 lib/win32/wrappers/security_context.js           |   3 +
 lib/win32/wrappers/security_credentials.cc       | 348 +++++++++
 lib/win32/wrappers/security_credentials.h        |  68 ++
 lib/win32/wrappers/security_credentials.js       |  22 +
 lib/worker.cc                                    |   7 +
 lib/worker.h                                     |  38 +
 package.json                                     |  29 +
 test/kerberos_tests.js                           |  34 +
 test/kerberos_win32_test.js                      |  15 +
 test/win32/security_buffer_descriptor_tests.js   |  41 ++
 test/win32/security_buffer_tests.js              |  22 +
 test/win32/security_credentials_tests.js         |  55 ++
 46 files changed, 5391 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..c70380d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+.DS_Store
+
+pids
+logs
+results
+node_modules
+build
+
+npm-debug.log
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..724ad09
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,18 @@
+.npmignore
+.gitignore
+.buildinfo
+.DS_Store
+
+HISTORY
+Readme.md
+TODO
+
+docs/
+docs/sphinx-docs
+data/
+dev/
+examples/
+
+
+build/
+node_modules/
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..b0fb9f4
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,20 @@
+language: node_js
+node_js:
+  - "0.8"
+  - "0.10"
+  - "0.12"
+  - "iojs-v1.8.4"
+  - "iojs-v2.5.0"
+  - "iojs-v3.3.0"
+  - "4"
+addons:
+  apt:
+    sources:
+      - ubuntu-toolchain-r-test
+    packages:
+      - g++-4.8
+before_install:
+  - '[ "${TRAVIS_NODE_VERSION}" != "0.8" ] || npm install -g npm at 1.4.28'
+  - if [[ $TRAVIS_OS_NAME == "linux" ]]; then export CXX=g++-4.8; fi
+  - $CXX --version
+  - npm explore npm -g -- npm install node-gyp at latest
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7428b0d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,4 @@
+kerberos
+========
+
+Kerberos library for node.js
\ No newline at end of file
diff --git a/binding.gyp b/binding.gyp
new file mode 100644
index 0000000..6655299
--- /dev/null
+++ b/binding.gyp
@@ -0,0 +1,46 @@
+{
+  'targets': [
+    {
+      'target_name': 'kerberos',      
+      'cflags!': [ '-fno-exceptions' ],
+      'cflags_cc!': [ '-fno-exceptions' ],
+      'include_dirs': [ '<!(node -e "require(\'nan\')")', '/usr/include/mit-krb5' ],
+      'conditions': [
+        ['OS=="mac"', {
+          'sources': [ 'lib/kerberos.cc', 'lib/worker.cc', 'lib/kerberosgss.c', 'lib/base64.c', 'lib/kerberos_context.cc' ],
+          'defines': [
+            '__MACOSX_CORE__'
+          ],
+          'xcode_settings': {
+            'GCC_ENABLE_CPP_EXCEPTIONS': 'YES'
+          },
+          "link_settings": {
+            "libraries": [
+              "-lkrb5"
+            ]
+          }
+        }],
+        ['OS=="linux"', {
+          'sources': [ 'lib/kerberos.cc', 'lib/worker.cc', 'lib/kerberosgss.c', 'lib/base64.c', 'lib/kerberos_context.cc' ],
+          'libraries': ['-lkrb5', '-lgssapi_krb5']
+        }],
+        ['OS=="win"',  {
+          'sources': [ 
+            'lib/win32/kerberos.cc', 
+            'lib/win32/base64.c', 
+            'lib/win32/worker.cc',
+            'lib/win32/kerberos_sspi.c',
+            'lib/win32/wrappers/security_buffer.cc',
+            'lib/win32/wrappers/security_buffer_descriptor.cc',
+            'lib/win32/wrappers/security_context.cc',
+            'lib/win32/wrappers/security_credentials.cc'
+          ],          
+          "link_settings": {
+            "libraries": [
+            ]
+          }
+        }]
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..b8c8532
--- /dev/null
+++ b/index.js
@@ -0,0 +1,6 @@
+// Get the Kerberos library
+module.exports = require('./lib/kerberos');
+// Set up the auth processes
+module.exports['processes'] = {
+  MongoAuthProcess: require('./lib/auth_processes/mongodb').MongoAuthProcess
+}
\ No newline at end of file
diff --git a/lib/auth_processes/mongodb.js b/lib/auth_processes/mongodb.js
new file mode 100644
index 0000000..f1e9231
--- /dev/null
+++ b/lib/auth_processes/mongodb.js
@@ -0,0 +1,281 @@
+var format = require('util').format;
+
+var MongoAuthProcess = function(host, port, service_name) {  
+  // Check what system we are on
+  if(process.platform == 'win32') {
+    this._processor = new Win32MongoProcessor(host, port, service_name);
+  } else {
+    this._processor = new UnixMongoProcessor(host, port, service_name);
+  }
+}
+
+MongoAuthProcess.prototype.init = function(username, password, callback) {
+  this._processor.init(username, password, callback);
+}
+
+MongoAuthProcess.prototype.transition = function(payload, callback) {
+  this._processor.transition(payload, callback);
+}
+
+/*******************************************************************
+ *
+ * Win32 SSIP Processor for MongoDB
+ *
+ *******************************************************************/
+var Win32MongoProcessor = function(host, port, service_name) {
+  this.host = host;
+  this.port = port  
+  // SSIP classes
+  this.ssip = require("../kerberos").SSIP;
+  // Set up first transition
+  this._transition = Win32MongoProcessor.first_transition(this);
+  // Set up service name
+  service_name = service_name || "mongodb";
+  // Set up target
+  this.target = format("%s/%s", service_name, host);
+  // Number of retries
+  this.retries = 10;
+}
+
+Win32MongoProcessor.prototype.init = function(username, password, callback) {
+  var self = this;
+  // Save the values used later
+  this.username = username;
+  this.password = password;
+  // Aquire credentials
+  this.ssip.SecurityCredentials.aquire_kerberos(username, password, function(err, security_credentials) {
+    if(err) return callback(err);
+    // Save credentials
+    self.security_credentials = security_credentials;
+    // Callback with success
+    callback(null);
+  });
+}
+
+Win32MongoProcessor.prototype.transition = function(payload, callback) {
+  if(this._transition == null) return callback(new Error("Transition finished"));
+  this._transition(payload, callback);
+}
+
+Win32MongoProcessor.first_transition = function(self) {
+  return function(payload, callback) {    
+    self.ssip.SecurityContext.initialize(
+      self.security_credentials, 
+      self.target, 
+      payload, function(err, security_context) {   
+        if(err) return callback(err);
+        
+        // If no context try again until we have no more retries
+        if(!security_context.hasContext) {
+          if(self.retries == 0) return callback(new Error("Failed to initialize security context"));
+          // Update the number of retries
+          self.retries = self.retries - 1;
+          // Set next transition
+          return self.transition(payload, callback);
+        }
+
+        // Set next transition
+        self._transition = Win32MongoProcessor.second_transition(self);
+        self.security_context = security_context;
+        // Return the payload
+        callback(null, security_context.payload);
+    });
+  }
+}
+
+Win32MongoProcessor.second_transition = function(self) {
+  return function(payload, callback) {    
+    // Perform a step
+    self.security_context.initialize(self.target, payload, function(err, security_context) {
+      if(err) return callback(err);
+
+      // If no context try again until we have no more retries
+      if(!security_context.hasContext) {
+        if(self.retries == 0) return callback(new Error("Failed to initialize security context"));
+        // Update the number of retries
+        self.retries = self.retries - 1;
+        // Set next transition
+        self._transition = Win32MongoProcessor.first_transition(self);
+        // Retry
+        return self.transition(payload, callback);
+      }
+
+      // Set next transition
+      self._transition = Win32MongoProcessor.third_transition(self);
+      // Return the payload
+      callback(null, security_context.payload);
+    });
+  }  
+}
+
+Win32MongoProcessor.third_transition = function(self) {
+  return function(payload, callback) {   
+    var messageLength = 0;
+    // Get the raw bytes
+    var encryptedBytes = new Buffer(payload, 'base64');
+    var encryptedMessage = new Buffer(messageLength);
+    // Copy first byte
+    encryptedBytes.copy(encryptedMessage, 0, 0, messageLength);
+    // Set up trailer
+    var securityTrailerLength = encryptedBytes.length - messageLength;
+    var securityTrailer = new Buffer(securityTrailerLength);
+    // Copy the bytes
+    encryptedBytes.copy(securityTrailer, 0, messageLength, securityTrailerLength);
+
+    // Types used
+    var SecurityBuffer = self.ssip.SecurityBuffer;
+    var SecurityBufferDescriptor = self.ssip.SecurityBufferDescriptor;
+
+    // Set up security buffers
+    var buffers = [
+        new SecurityBuffer(SecurityBuffer.DATA, encryptedBytes)
+      , new SecurityBuffer(SecurityBuffer.STREAM, securityTrailer)
+    ];
+
+    // Set up the descriptor
+    var descriptor = new SecurityBufferDescriptor(buffers);
+
+    // Decrypt the data
+    self.security_context.decryptMessage(descriptor, function(err, security_context) {
+      if(err) return callback(err);
+
+      var length = 4;
+      if(self.username != null) {
+        length += self.username.length;          
+      }
+
+      var bytesReceivedFromServer = new Buffer(length);
+      bytesReceivedFromServer[0] = 0x01;  // NO_PROTECTION
+      bytesReceivedFromServer[1] = 0x00;  // NO_PROTECTION
+      bytesReceivedFromServer[2] = 0x00;  // NO_PROTECTION
+      bytesReceivedFromServer[3] = 0x00;  // NO_PROTECTION        
+
+      if(self.username != null) {
+        var authorization_id_bytes = new Buffer(self.username, 'utf8');
+        authorization_id_bytes.copy(bytesReceivedFromServer, 4, 0);
+      }
+
+      self.security_context.queryContextAttributes(0x00, function(err, sizes) {
+        if(err) return callback(err);
+
+        var buffers = [
+            new SecurityBuffer(SecurityBuffer.TOKEN, new Buffer(sizes.securityTrailer))
+          , new SecurityBuffer(SecurityBuffer.DATA, bytesReceivedFromServer)
+          , new SecurityBuffer(SecurityBuffer.PADDING, new Buffer(sizes.blockSize))
+        ]
+
+        var descriptor = new SecurityBufferDescriptor(buffers);
+
+        self.security_context.encryptMessage(descriptor, 0x80000001, function(err, security_context) {
+          if(err) return callback(err);
+          callback(null, security_context.payload);
+        });
+      });
+    });
+  }  
+}
+
+/*******************************************************************
+ *
+ * UNIX MIT Kerberos processor
+ *
+ *******************************************************************/
+var UnixMongoProcessor = function(host, port, service_name) {
+  this.host = host;
+  this.port = port  
+  // SSIP classes
+  this.Kerberos = require("../kerberos").Kerberos;
+  this.kerberos = new this.Kerberos();
+  service_name = service_name || "mongodb";
+  // Set up first transition
+  this._transition = UnixMongoProcessor.first_transition(this);
+  // Set up target
+  this.target = format("%s@%s", service_name, host);
+  // Number of retries
+  this.retries = 10;
+}
+
+UnixMongoProcessor.prototype.init = function(username, password, callback) {
+  var self = this;
+  this.username = username;
+  this.password = password;
+  // Call client initiate
+  this.kerberos.authGSSClientInit(
+      self.target
+    , this.Kerberos.GSS_C_MUTUAL_FLAG, function(err, context) {
+      self.context = context;
+      // Return the context
+      callback(null, context);
+  });
+}
+
+UnixMongoProcessor.prototype.transition = function(payload, callback) {
+  if(this._transition == null) return callback(new Error("Transition finished"));
+  this._transition(payload, callback);
+}
+
+UnixMongoProcessor.first_transition = function(self) {
+  return function(payload, callback) {    
+    self.kerberos.authGSSClientStep(self.context, '', function(err, result) {
+      if(err) return callback(err);
+      // Set up the next step
+      self._transition = UnixMongoProcessor.second_transition(self);
+      // Return the payload
+      callback(null, self.context.response);
+    })
+  }
+}
+
+UnixMongoProcessor.second_transition = function(self) {
+  return function(payload, callback) {    
+    self.kerberos.authGSSClientStep(self.context, payload, function(err, result) {
+      if(err && self.retries == 0) return callback(err);
+      // Attempt to re-establish a context
+      if(err) {
+        // Adjust the number of retries
+        self.retries = self.retries - 1;
+        // Call same step again
+        return self.transition(payload, callback);
+      }
+      
+      // Set up the next step
+      self._transition = UnixMongoProcessor.third_transition(self);
+      // Return the payload
+      callback(null, self.context.response || '');
+    });
+  }
+}
+
+UnixMongoProcessor.third_transition = function(self) {
+  return function(payload, callback) {    
+    // GSS Client Unwrap
+    self.kerberos.authGSSClientUnwrap(self.context, payload, function(err, result) {
+      if(err) return callback(err, false);
+      
+      // Wrap the response
+      self.kerberos.authGSSClientWrap(self.context, self.context.response, self.username, function(err, result) {
+        if(err) return callback(err, false);
+        // Set up the next step
+        self._transition = UnixMongoProcessor.fourth_transition(self);
+        // Return the payload
+        callback(null, self.context.response);
+      });
+    });
+  }
+}
+
+UnixMongoProcessor.fourth_transition = function(self) {
+  return function(payload, callback) {    
+    // Clean up context
+    self.kerberos.authGSSClientClean(self.context, function(err, result) {
+      if(err) return callback(err, false);
+      // Set the transition to null
+      self._transition = null;
+      // Callback with valid authentication
+      callback(null, true);
+    });
+  }
+}
+
+// Set the process
+exports.MongoAuthProcess = MongoAuthProcess;
\ No newline at end of file
diff --git a/lib/base64.c b/lib/base64.c
new file mode 100644
index 0000000..aca0a61
--- /dev/null
+++ b/lib/base64.c
@@ -0,0 +1,134 @@
+/**
+ * Copyright (c) 2006-2008 Apple Inc. 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.
+ **/
+
+#include "base64.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+void die2(const char *message) {
+  if(errno) {
+    perror(message);
+  } else {
+    printf("ERROR: %s\n", message);
+  }
+
+  exit(1);
+}
+
+// base64 tables
+static char basis_64[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static signed char index_64[128] =
+{
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+    52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
+    -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
+    15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+    -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+    41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+};
+#define CHAR64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
+
+// base64_encode    :    base64 encode
+//
+// value            :    data to encode
+// vlen             :    length of data
+// (result)         :    new char[] - c-str of result
+char *base64_encode(const unsigned char *value, int vlen)
+{
+    char *result = (char *)malloc((vlen * 4) / 3 + 5);
+    if(result == NULL) die2("Memory allocation failed");
+    char *out = result;
+    while (vlen >= 3)
+    {
+        *out++ = basis_64[value[0] >> 2];
+        *out++ = basis_64[((value[0] << 4) & 0x30) | (value[1] >> 4)];
+        *out++ = basis_64[((value[1] << 2) & 0x3C) | (value[2] >> 6)];
+        *out++ = basis_64[value[2] & 0x3F];
+        value += 3;
+        vlen -= 3;
+    }
+    if (vlen > 0)
+    {
+        *out++ = basis_64[value[0] >> 2];
+        unsigned char oval = (value[0] << 4) & 0x30;
+        if (vlen > 1) oval |= value[1] >> 4;
+        *out++ = basis_64[oval];
+        *out++ = (vlen < 2) ? '=' : basis_64[(value[1] << 2) & 0x3C];
+        *out++ = '=';
+    }
+    *out = '\0';
+
+    return result;
+}
+
+// base64_decode    :    base64 decode
+//
+// value            :    c-str to decode
+// rlen             :    length of decoded result
+// (result)         :    new unsigned char[] - decoded result
+unsigned char *base64_decode(const char *value, int *rlen)
+{
+    *rlen = 0;
+    int c1, c2, c3, c4;
+
+    int vlen = strlen(value);
+    unsigned char *result =(unsigned char *)malloc((vlen * 3) / 4 + 1);
+    if(result == NULL) die2("Memory allocation failed");    
+    unsigned char *out = result;
+
+    while (1)
+    {
+        if (value[0]==0)
+            return result;
+        c1 = value[0];
+        if (CHAR64(c1) == -1)
+            goto base64_decode_error;;
+        c2 = value[1];
+        if (CHAR64(c2) == -1)
+            goto base64_decode_error;;
+        c3 = value[2];
+        if ((c3 != '=') && (CHAR64(c3) == -1))
+            goto base64_decode_error;;
+        c4 = value[3];
+        if ((c4 != '=') && (CHAR64(c4) == -1))
+            goto base64_decode_error;;
+
+        value += 4;
+        *out++ = (CHAR64(c1) << 2) | (CHAR64(c2) >> 4);
+        *rlen += 1;
+        if (c3 != '=')
+        {
+            *out++ = ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2);
+            *rlen += 1;
+            if (c4 != '=')
+            {
+                *out++ = ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4);
+                *rlen += 1;
+            }
+        }
+    }
+
+base64_decode_error:
+    *result = 0;
+    *rlen = 0;
+    return result;
+}
diff --git a/lib/base64.h b/lib/base64.h
new file mode 100644
index 0000000..9152e6a
--- /dev/null
+++ b/lib/base64.h
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2006-2008 Apple Inc. 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.
+ **/
+#ifndef BASE64_H
+#define BASE64_H
+
+char *base64_encode(const unsigned char *value, int vlen);
+unsigned char *base64_decode(const char *value, int *rlen);
+
+#endif
\ No newline at end of file
diff --git a/lib/kerberos.cc b/lib/kerberos.cc
new file mode 100644
index 0000000..ffe865f
--- /dev/null
+++ b/lib/kerberos.cc
@@ -0,0 +1,864 @@
+#include "kerberos.h"
+#include <stdlib.h>
+#include <errno.h>
+#include "worker.h"
+#include "kerberos_context.h"
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0]))
+#endif
+
+void die(const char *message) {
+  if(errno) {
+    perror(message);
+  } else {
+    printf("ERROR: %s\n", message);
+  }
+
+  exit(1);
+}
+
+// Call structs
+typedef struct AuthGSSClientCall {
+  uint32_t  flags;
+  char *uri;
+} AuthGSSClientCall;
+
+typedef struct AuthGSSClientStepCall {
+  KerberosContext *context;
+  char *challenge;
+} AuthGSSClientStepCall;
+
+typedef struct AuthGSSClientUnwrapCall {
+  KerberosContext *context;
+  char *challenge;
+} AuthGSSClientUnwrapCall;
+
+typedef struct AuthGSSClientWrapCall {
+  KerberosContext *context;
+  char *challenge;
+  char *user_name;
+} AuthGSSClientWrapCall;
+
+typedef struct AuthGSSClientCleanCall {
+  KerberosContext *context;
+} AuthGSSClientCleanCall;
+
+typedef struct AuthGSSServerInitCall {
+  char *service;
+} AuthGSSServerInitCall;
+
+typedef struct AuthGSSServerCleanCall {
+  KerberosContext *context;
+} AuthGSSServerCleanCall;
+
+typedef struct AuthGSSServerStepCall {
+  KerberosContext *context;
+  char *auth_data;
+} AuthGSSServerStepCall;
+
+Kerberos::Kerberos() : Nan::ObjectWrap() {
+}
+
+Nan::Persistent<FunctionTemplate> Kerberos::constructor_template;
+
+void Kerberos::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
+  // Grab the scope of the call from Node
+  Nan::HandleScope scope;
+
+  // Define a new function template
+  Local<FunctionTemplate> t = Nan::New<FunctionTemplate>(New);
+  t->InstanceTemplate()->SetInternalFieldCount(1);
+  t->SetClassName(Nan::New<String>("Kerberos").ToLocalChecked());
+
+  // Set up method for the Kerberos instance
+  Nan::SetPrototypeMethod(t, "authGSSClientInit", AuthGSSClientInit);
+  Nan::SetPrototypeMethod(t, "authGSSClientStep", AuthGSSClientStep);
+  Nan::SetPrototypeMethod(t, "authGSSClientUnwrap", AuthGSSClientUnwrap);
+  Nan::SetPrototypeMethod(t, "authGSSClientWrap", AuthGSSClientWrap);
+  Nan::SetPrototypeMethod(t, "authGSSClientClean", AuthGSSClientClean);
+  Nan::SetPrototypeMethod(t, "authGSSServerInit", AuthGSSServerInit);
+  Nan::SetPrototypeMethod(t, "authGSSServerClean", AuthGSSServerClean);
+  Nan::SetPrototypeMethod(t, "authGSSServerStep", AuthGSSServerStep);
+
+  constructor_template.Reset(t);
+
+  // Set the symbol
+  target->ForceSet(Nan::New<String>("Kerberos").ToLocalChecked(), t->GetFunction());
+}
+
+NAN_METHOD(Kerberos::New) {
+  // Create a Kerberos instance
+  Kerberos *kerberos = new Kerberos();
+  // Return the kerberos object
+  kerberos->Wrap(info.This());
+  info.GetReturnValue().Set(info.This());
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// authGSSClientInit
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+static void _authGSSClientInit(Worker *worker) {
+  gss_client_state *state;
+  gss_client_response *response;
+
+  // Allocate state
+  state = (gss_client_state *)malloc(sizeof(gss_client_state));
+  if(state == NULL) die("Memory allocation failed");
+
+  // Unpack the parameter data struct
+  AuthGSSClientCall *call = (AuthGSSClientCall *)worker->parameters;
+  // Start the kerberos client
+  response = authenticate_gss_client_init(call->uri, call->flags, state);
+
+  // Release the parameter struct memory
+  free(call->uri);
+  free(call);
+
+  // If we have an error mark worker as having had an error
+  if(response->return_code == AUTH_GSS_ERROR) {
+    worker->error = TRUE;
+    worker->error_code = response->return_code;
+    worker->error_message = response->message;
+    free(state);
+  } else {
+    worker->return_value = state;
+  }
+
+  // Free structure
+  free(response);
+}
+
+static Local<Value> _map_authGSSClientInit(Worker *worker) {
+  KerberosContext *context = KerberosContext::New();
+  context->state = (gss_client_state *)worker->return_value;
+  return context->handle();
+}
+
+// Initialize method
+NAN_METHOD(Kerberos::AuthGSSClientInit) {
+  // Ensure valid call
+    if(info.Length() != 3) return Nan::ThrowError("Requires a service string uri, integer flags and a callback function");
+  if(info.Length() == 3 && (!info[0]->IsString() || !info[1]->IsInt32() || !info[2]->IsFunction()))
+      return Nan::ThrowError("Requires a service string uri, integer flags and a callback function");
+
+  Local<String> service = info[0]->ToString();
+  // Convert uri string to c-string
+  char *service_str = (char *)calloc(service->Utf8Length() + 1, sizeof(char));
+  if(service_str == NULL) die("Memory allocation failed");
+
+  // Write v8 string to c-string
+  service->WriteUtf8(service_str);
+
+  // Allocate a structure
+  AuthGSSClientCall *call = (AuthGSSClientCall *)calloc(1, sizeof(AuthGSSClientCall));
+  if(call == NULL) die("Memory allocation failed");
+  call->flags =info[1]->ToInt32()->Uint32Value();
+  call->uri = service_str;
+
+  // Unpack the callback
+  Local<Function> callbackHandle = Local<Function>::Cast(info[2]);
+  Nan::Callback *callback = new Nan::Callback(callbackHandle);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = callback;
+  worker->parameters = call;
+  worker->execute = _authGSSClientInit;
+  worker->mapper = _map_authGSSClientInit;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// authGSSClientStep
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+static void _authGSSClientStep(Worker *worker) {
+  gss_client_state *state;
+  gss_client_response *response;
+  char *challenge;
+
+  // Unpack the parameter data struct
+  AuthGSSClientStepCall *call = (AuthGSSClientStepCall *)worker->parameters;
+  // Get the state
+  state = call->context->state;
+  challenge = call->challenge;
+
+  // Check what kind of challenge we have
+  if(call->challenge == NULL) {
+    challenge = (char *)"";
+  }
+
+  // Perform authentication step
+  response = authenticate_gss_client_step(state, challenge);
+
+  // If we have an error mark worker as having had an error
+  if(response->return_code == AUTH_GSS_ERROR) {
+    worker->error = TRUE;
+    worker->error_code = response->return_code;
+    worker->error_message = response->message;
+  } else {
+    worker->return_code = response->return_code;
+  }
+
+  // Free up structure
+  if(call->challenge != NULL) free(call->challenge);
+  free(call);
+  free(response);
+}
+
+static Local<Value> _map_authGSSClientStep(Worker *worker) {
+  Nan::HandleScope scope;
+  // Return the return code
+  return Nan::New<Int32>(worker->return_code);
+}
+
+// Initialize method
+NAN_METHOD(Kerberos::AuthGSSClientStep) {
+  // Ensure valid call
+  if(info.Length() != 2 && info.Length() != 3) return Nan::ThrowError("Requires a GSS context, optional challenge string and callback function");
+  if(info.Length() == 2 && (!KerberosContext::HasInstance(info[0]) || !info[1]->IsFunction())) return Nan::ThrowError("Requires a GSS context, optional challenge string and callback function");
+  if(info.Length() == 3 && (!KerberosContext::HasInstance(info[0]) || !info[1]->IsString() || !info[2]->IsFunction())) return Nan::ThrowError("Requires a GSS context, optional challenge string and callback function");
+
+  // Challenge string
+  char *challenge_str = NULL;
+  // Let's unpack the parameters
+  Local<Object> object = info[0]->ToObject();
+  KerberosContext *kerberos_context = KerberosContext::Unwrap<KerberosContext>(object);
+
+  if (!kerberos_context->IsClientInstance()) {
+      return Nan::ThrowError("GSS context is not a client instance");
+  }
+
+  int callbackArg = 1;
+
+  // If we have a challenge string
+  if(info.Length() == 3) {
+    // Unpack the challenge string
+    Local<String> challenge = info[1]->ToString();
+    // Convert uri string to c-string
+    challenge_str = (char *)calloc(challenge->Utf8Length() + 1, sizeof(char));
+    if(challenge_str == NULL) die("Memory allocation failed");
+    // Write v8 string to c-string
+    challenge->WriteUtf8(challenge_str);
+
+    callbackArg = 2;
+  }
+
+  // Allocate a structure
+  AuthGSSClientStepCall *call = (AuthGSSClientStepCall *)calloc(1, sizeof(AuthGSSClientStepCall));
+  if(call == NULL) die("Memory allocation failed");
+  call->context = kerberos_context;
+  call->challenge = challenge_str;
+
+  // Unpack the callback
+  Local<Function> callbackHandle = Local<Function>::Cast(info[callbackArg]);
+  Nan::Callback *callback = new Nan::Callback(callbackHandle);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = callback;
+  worker->parameters = call;
+  worker->execute = _authGSSClientStep;
+  worker->mapper = _map_authGSSClientStep;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
+
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// authGSSClientUnwrap
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+static void _authGSSClientUnwrap(Worker *worker) {
+  gss_client_response *response;
+  char *challenge;
+
+  // Unpack the parameter data struct
+  AuthGSSClientUnwrapCall *call = (AuthGSSClientUnwrapCall *)worker->parameters;
+  challenge = call->challenge;
+
+  // Check what kind of challenge we have
+  if(call->challenge == NULL) {
+    challenge = (char *)"";
+  }
+
+  // Perform authentication step
+  response = authenticate_gss_client_unwrap(call->context->state, challenge);
+
+  // If we have an error mark worker as having had an error
+  if(response->return_code == AUTH_GSS_ERROR) {
+    worker->error = TRUE;
+    worker->error_code = response->return_code;
+    worker->error_message = response->message;
+  } else {
+    worker->return_code = response->return_code;
+  }
+
+  // Free up structure
+  if(call->challenge != NULL) free(call->challenge);
+  free(call);
+  free(response);
+}
+
+static Local<Value> _map_authGSSClientUnwrap(Worker *worker) {
+  Nan::HandleScope scope;
+  // Return the return code
+  return Nan::New<Int32>(worker->return_code);
+}
+
+// Initialize method
+NAN_METHOD(Kerberos::AuthGSSClientUnwrap) {
+  // Ensure valid call
+    if(info.Length() != 2 && info.Length() != 3) return Nan::ThrowError("Requires a GSS context, optional challenge string and callback function");
+    if(info.Length() == 2 && (!KerberosContext::HasInstance(info[0]) || !info[1]->IsFunction())) return Nan::ThrowError("Requires a GSS context, optional challenge string and callback function");
+    if(info.Length() == 3 && (!KerberosContext::HasInstance(info[0]) || !info[1]->IsString() || !info[2]->IsFunction())) return Nan::ThrowError("Requires a GSS context, optional challenge string and callback function");
+
+  // Challenge string
+  char *challenge_str = NULL;
+  // Let's unpack the parameters
+  Local<Object> object = info[0]->ToObject();
+  KerberosContext *kerberos_context = KerberosContext::Unwrap<KerberosContext>(object);
+
+  if (!kerberos_context->IsClientInstance()) {
+      return Nan::ThrowError("GSS context is not a client instance");
+  }
+
+  // If we have a challenge string
+  if(info.Length() == 3) {
+    // Unpack the challenge string
+    Local<String> challenge = info[1]->ToString();
+    // Convert uri string to c-string
+    challenge_str = (char *)calloc(challenge->Utf8Length() + 1, sizeof(char));
+    if(challenge_str == NULL) die("Memory allocation failed");
+    // Write v8 string to c-string
+    challenge->WriteUtf8(challenge_str);
+  }
+
+  // Allocate a structure
+  AuthGSSClientUnwrapCall *call = (AuthGSSClientUnwrapCall *)calloc(1, sizeof(AuthGSSClientUnwrapCall));
+  if(call == NULL) die("Memory allocation failed");
+  call->context = kerberos_context;
+  call->challenge = challenge_str;
+
+  // Unpack the callback
+  Local<Function> callbackHandle = info.Length() == 3 ? Local<Function>::Cast(info[2]) : Local<Function>::Cast(info[1]);
+  Nan::Callback *callback = new Nan::Callback(callbackHandle);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = callback;
+  worker->parameters = call;
+  worker->execute = _authGSSClientUnwrap;
+  worker->mapper = _map_authGSSClientUnwrap;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
+
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// authGSSClientWrap
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+static void _authGSSClientWrap(Worker *worker) {
+  gss_client_response *response;
+  char *user_name = NULL;
+
+  // Unpack the parameter data struct
+  AuthGSSClientWrapCall *call = (AuthGSSClientWrapCall *)worker->parameters;
+  user_name = call->user_name;
+
+  // Check what kind of challenge we have
+  if(call->user_name == NULL) {
+    user_name = (char *)"";
+  }
+
+  // Perform authentication step
+  response = authenticate_gss_client_wrap(call->context->state, call->challenge, user_name);
+
+  // If we have an error mark worker as having had an error
+  if(response->return_code == AUTH_GSS_ERROR) {
+    worker->error = TRUE;
+    worker->error_code = response->return_code;
+    worker->error_message = response->message;
+  } else {
+    worker->return_code = response->return_code;
+  }
+
+  // Free up structure
+  if(call->challenge != NULL) free(call->challenge);
+  if(call->user_name != NULL) free(call->user_name);
+  free(call);
+  free(response);
+}
+
+static Local<Value> _map_authGSSClientWrap(Worker *worker) {
+  Nan::HandleScope scope;
+  // Return the return code
+  return Nan::New<Int32>(worker->return_code);
+}
+
+// Initialize method
+NAN_METHOD(Kerberos::AuthGSSClientWrap) {
+  // Ensure valid call
+    if(info.Length() != 3 && info.Length() != 4) return Nan::ThrowError("Requires a GSS context, the result from the authGSSClientResponse after authGSSClientUnwrap, optional user name and callback function");
+    if(info.Length() == 3 && (!KerberosContext::HasInstance(info[0]) || !info[1]->IsString() || !info[2]->IsFunction())) return Nan::ThrowError("Requires a GSS context, the result from the authGSSClientResponse after authGSSClientUnwrap, optional user name and callback function");
+    if(info.Length() == 4 && (!KerberosContext::HasInstance(info[0]) || !info[1]->IsString() || !info[2]->IsString() || !info[3]->IsFunction())) return Nan::ThrowError("Requires a GSS context, the result from the authGSSClientResponse after authGSSClientUnwrap, optional user name and callback function");
+
+  // Challenge string
+  char *challenge_str = NULL;
+  char *user_name_str = NULL;
+
+  // Let's unpack the kerberos context
+  Local<Object> object = info[0]->ToObject();
+  KerberosContext *kerberos_context = KerberosContext::Unwrap<KerberosContext>(object);
+
+  if (!kerberos_context->IsClientInstance()) {
+      return Nan::ThrowError("GSS context is not a client instance");
+  }
+
+  // Unpack the challenge string
+  Local<String> challenge = info[1]->ToString();
+  // Convert uri string to c-string
+  challenge_str = (char *)calloc(challenge->Utf8Length() + 1, sizeof(char));
+  if(challenge_str == NULL) die("Memory allocation failed");
+  // Write v8 string to c-string
+  challenge->WriteUtf8(challenge_str);
+
+  // If we have a user string
+  if(info.Length() == 4) {
+    // Unpack user name
+    Local<String> user_name = info[2]->ToString();
+    // Convert uri string to c-string
+    user_name_str = (char *)calloc(user_name->Utf8Length() + 1, sizeof(char));
+    if(user_name_str == NULL) die("Memory allocation failed");
+    // Write v8 string to c-string
+    user_name->WriteUtf8(user_name_str);
+  }
+
+  // Allocate a structure
+  AuthGSSClientWrapCall *call = (AuthGSSClientWrapCall *)calloc(1, sizeof(AuthGSSClientWrapCall));
+  if(call == NULL) die("Memory allocation failed");
+  call->context = kerberos_context;
+  call->challenge = challenge_str;
+  call->user_name = user_name_str;
+
+  // Unpack the callback
+  Local<Function> callbackHandle = info.Length() == 4 ? Local<Function>::Cast(info[3]) : Local<Function>::Cast(info[2]);
+  Nan::Callback *callback = new Nan::Callback(callbackHandle);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = callback;
+  worker->parameters = call;
+  worker->execute = _authGSSClientWrap;
+  worker->mapper = _map_authGSSClientWrap;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
+
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// authGSSClientClean
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+static void _authGSSClientClean(Worker *worker) {
+  gss_client_response *response;
+
+  // Unpack the parameter data struct
+  AuthGSSClientCleanCall *call = (AuthGSSClientCleanCall *)worker->parameters;
+
+  // Perform authentication step
+  response = authenticate_gss_client_clean(call->context->state);
+
+  // If we have an error mark worker as having had an error
+  if(response->return_code == AUTH_GSS_ERROR) {
+    worker->error = TRUE;
+    worker->error_code = response->return_code;
+    worker->error_message = response->message;
+  } else {
+    worker->return_code = response->return_code;
+  }
+
+  // Free up structure
+  free(call);
+  free(response);
+}
+
+static Local<Value> _map_authGSSClientClean(Worker *worker) {
+  Nan::HandleScope scope;
+  // Return the return code
+  return Nan::New<Int32>(worker->return_code);
+}
+
+// Initialize method
+NAN_METHOD(Kerberos::AuthGSSClientClean) {
+  // Ensure valid call
+  if(info.Length() != 2) return Nan::ThrowError("Requires a GSS context and callback function");
+  if(!KerberosContext::HasInstance(info[0]) || !info[1]->IsFunction()) return Nan::ThrowError("Requires a GSS context and callback function");
+
+  // Let's unpack the kerberos context
+  Local<Object> object = info[0]->ToObject();
+  KerberosContext *kerberos_context = KerberosContext::Unwrap<KerberosContext>(object);
+
+  if (!kerberos_context->IsClientInstance()) {
+      return Nan::ThrowError("GSS context is not a client instance");
+  }
+
+  // Allocate a structure
+  AuthGSSClientCleanCall *call = (AuthGSSClientCleanCall *)calloc(1, sizeof(AuthGSSClientCleanCall));
+  if(call == NULL) die("Memory allocation failed");
+  call->context = kerberos_context;
+
+  // Unpack the callback
+  Local<Function> callbackHandle = Local<Function>::Cast(info[1]);
+  Nan::Callback *callback = new Nan::Callback(callbackHandle);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = callback;
+  worker->parameters = call;
+  worker->execute = _authGSSClientClean;
+  worker->mapper = _map_authGSSClientClean;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
+
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// authGSSServerInit
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+static void _authGSSServerInit(Worker *worker) {
+  gss_server_state *state;
+  gss_client_response *response;
+
+  // Allocate state
+  state = (gss_server_state *)malloc(sizeof(gss_server_state));
+  if(state == NULL) die("Memory allocation failed");
+
+  // Unpack the parameter data struct
+  AuthGSSServerInitCall *call = (AuthGSSServerInitCall *)worker->parameters;
+  // Start the kerberos service
+  response = authenticate_gss_server_init(call->service, state);
+
+  // Release the parameter struct memory
+  free(call->service);
+  free(call);
+
+  // If we have an error mark worker as having had an error
+  if(response->return_code == AUTH_GSS_ERROR) {
+    worker->error = TRUE;
+    worker->error_code = response->return_code;
+    worker->error_message = response->message;
+    free(state);
+  } else {
+    worker->return_value = state;
+  }
+
+  // Free structure
+  free(response);
+}
+
+static Local<Value> _map_authGSSServerInit(Worker *worker) {
+  KerberosContext *context = KerberosContext::New();
+  context->server_state = (gss_server_state *)worker->return_value;
+  return context->handle();
+}
+
+// Server Initialize method
+NAN_METHOD(Kerberos::AuthGSSServerInit) {
+  // Ensure valid call
+  if(info.Length() != 2) return Nan::ThrowError("Requires a service string service and a callback function");
+  if(!info[0]->IsString() || !info[1]->IsFunction()) return Nan::ThrowError("Requires a service string service and a callback function");
+
+  Local<String> service = info[0]->ToString();
+  // Convert service string to c-string
+  char *service_str = (char *)calloc(service->Utf8Length() + 1, sizeof(char));
+  if(service_str == NULL) die("Memory allocation failed");
+
+  // Write v8 string to c-string
+  service->WriteUtf8(service_str);
+
+  // Allocate a structure
+  AuthGSSServerInitCall *call = (AuthGSSServerInitCall *)calloc(1, sizeof(AuthGSSServerInitCall));
+  if(call == NULL) die("Memory allocation failed");
+  call->service = service_str;
+
+  // Unpack the callback
+  Local<Function> callbackHandle = Local<Function>::Cast(info[1]);
+  Nan::Callback *callback = new Nan::Callback(callbackHandle);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = callback;
+  worker->parameters = call;
+  worker->execute = _authGSSServerInit;
+  worker->mapper = _map_authGSSServerInit;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// authGSSServerClean
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+static void _authGSSServerClean(Worker *worker) {
+  gss_client_response *response;
+
+  // Unpack the parameter data struct
+  AuthGSSServerCleanCall *call = (AuthGSSServerCleanCall *)worker->parameters;
+
+  // Perform authentication step
+  response = authenticate_gss_server_clean(call->context->server_state);
+
+  // If we have an error mark worker as having had an error
+  if(response->return_code == AUTH_GSS_ERROR) {
+    worker->error = TRUE;
+    worker->error_code = response->return_code;
+    worker->error_message = response->message;
+  } else {
+    worker->return_code = response->return_code;
+  }
+
+  // Free up structure
+  free(call);
+  free(response);
+}
+
+static Local<Value> _map_authGSSServerClean(Worker *worker) {
+  Nan::HandleScope scope;
+  // Return the return code
+  return Nan::New<Int32>(worker->return_code);
+}
+
+// Initialize method
+NAN_METHOD(Kerberos::AuthGSSServerClean) {
+  // // Ensure valid call
+  if(info.Length() != 2) return Nan::ThrowError("Requires a GSS context and callback function");
+  if(!KerberosContext::HasInstance(info[0]) || !info[1]->IsFunction()) return Nan::ThrowError("Requires a GSS context and callback function");
+
+  // Let's unpack the kerberos context
+  Local<Object> object = info[0]->ToObject();
+  KerberosContext *kerberos_context = KerberosContext::Unwrap<KerberosContext>(object);
+
+  if (!kerberos_context->IsServerInstance()) {
+      return Nan::ThrowError("GSS context is not a server instance");
+  }
+
+  // Allocate a structure
+  AuthGSSServerCleanCall *call = (AuthGSSServerCleanCall *)calloc(1, sizeof(AuthGSSServerCleanCall));
+  if(call == NULL) die("Memory allocation failed");
+  call->context = kerberos_context;
+
+  // Unpack the callback
+  Local<Function> callbackHandle = Local<Function>::Cast(info[1]);
+  Nan::Callback *callback = new Nan::Callback(callbackHandle);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = callback;
+  worker->parameters = call;
+  worker->execute = _authGSSServerClean;
+  worker->mapper = _map_authGSSServerClean;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
+
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// authGSSServerStep
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+static void _authGSSServerStep(Worker *worker) {
+  gss_server_state *state;
+  gss_client_response *response;
+  char *auth_data;
+
+  // Unpack the parameter data struct
+  AuthGSSServerStepCall *call = (AuthGSSServerStepCall *)worker->parameters;
+  // Get the state
+  state = call->context->server_state;
+  auth_data = call->auth_data;
+
+  // Check if we got auth_data or not
+  if(call->auth_data == NULL) {
+    auth_data = (char *)"";
+  }
+
+  // Perform authentication step
+  response = authenticate_gss_server_step(state, auth_data);
+
+  // If we have an error mark worker as having had an error
+  if(response->return_code == AUTH_GSS_ERROR) {
+    worker->error = TRUE;
+    worker->error_code = response->return_code;
+    worker->error_message = response->message;
+  } else {
+    worker->return_code = response->return_code;
+  }
+
+  // Free up structure
+  if(call->auth_data != NULL) free(call->auth_data);
+  free(call);
+  free(response);
+}
+
+static Local<Value> _map_authGSSServerStep(Worker *worker) {
+  Nan::HandleScope scope;
+  // Return the return code
+  return Nan::New<Int32>(worker->return_code);
+}
+
+// Initialize method
+NAN_METHOD(Kerberos::AuthGSSServerStep) {
+  // Ensure valid call
+  if(info.Length() != 3) return Nan::ThrowError("Requires a GSS context, auth-data string and callback function");
+  if(!KerberosContext::HasInstance(info[0]))  return Nan::ThrowError("1st arg must be a GSS context");
+  if (!info[1]->IsString())  return Nan::ThrowError("2nd arg must be auth-data string");
+  if (!info[2]->IsFunction())  return Nan::ThrowError("3rd arg must be a callback function");
+
+  // Auth-data string
+  char *auth_data_str = NULL;
+  // Let's unpack the parameters
+  Local<Object> object = info[0]->ToObject();
+  KerberosContext *kerberos_context = KerberosContext::Unwrap<KerberosContext>(object);
+
+  if (!kerberos_context->IsServerInstance()) {
+      return Nan::ThrowError("GSS context is not a server instance");
+  }
+
+  // Unpack the auth_data string
+  Local<String> auth_data = info[1]->ToString();
+  // Convert uri string to c-string
+  auth_data_str = (char *)calloc(auth_data->Utf8Length() + 1, sizeof(char));
+  if(auth_data_str == NULL) die("Memory allocation failed");
+  // Write v8 string to c-string
+  auth_data->WriteUtf8(auth_data_str);
+
+  // Allocate a structure
+  AuthGSSServerStepCall *call = (AuthGSSServerStepCall *)calloc(1, sizeof(AuthGSSServerStepCall));
+  if(call == NULL) die("Memory allocation failed");
+  call->context = kerberos_context;
+  call->auth_data = auth_data_str;
+
+  // Unpack the callback
+  Local<Function> callbackHandle = Local<Function>::Cast(info[2]);
+  Nan::Callback *callback = new Nan::Callback(callbackHandle);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = callback;
+  worker->parameters = call;
+  worker->execute = _authGSSServerStep;
+  worker->mapper = _map_authGSSServerStep;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, Kerberos::Process, (uv_after_work_cb)Kerberos::After);
+
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// UV Lib callbacks
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+void Kerberos::Process(uv_work_t* work_req) {
+  // Grab the worker
+  Worker *worker = static_cast<Worker*>(work_req->data);
+  // Execute the worker code
+  worker->execute(worker);
+}
+
+void Kerberos::After(uv_work_t* work_req) {
+  // Grab the scope of the call from Node
+  Nan::HandleScope scope;
+
+  // Get the worker reference
+  Worker *worker = static_cast<Worker*>(work_req->data);
+
+  // If we have an error
+  if(worker->error) {
+    Local<Value> err = v8::Exception::Error(Nan::New<String>(worker->error_message).ToLocalChecked());
+    Local<Object> obj = err->ToObject();
+    obj->Set(Nan::New<String>("code").ToLocalChecked(), Nan::New<Int32>(worker->error_code));
+    Local<Value> info[2] = { err, Nan::Null() };
+    // Execute the error
+    Nan::TryCatch try_catch;
+
+    // Call the callback
+    worker->callback->Call(ARRAY_SIZE(info), info);
+
+    // If we have an exception handle it as a fatalexception
+    if (try_catch.HasCaught()) {
+      Nan::FatalException(try_catch);
+    }
+  } else {
+    // // Map the data
+    Local<Value> result = worker->mapper(worker);
+    // Set up the callback with a null first
+    #if defined(V8_MAJOR_VERSION) && (V8_MAJOR_VERSION > 4 ||                      \
+      (V8_MAJOR_VERSION == 4 && defined(V8_MINOR_VERSION) && V8_MINOR_VERSION >= 3))
+    Local<Value> info[2] = { Nan::Null(), result};
+    #else
+    Local<Value> info[2] = { Nan::Null(), Nan::New<v8::Value>(result)};
+    #endif
+
+    // Wrap the callback function call in a TryCatch so that we can call
+    // node's FatalException afterwards. This makes it possible to catch
+    // the exception from JavaScript land using the
+    // process.on('uncaughtException') event.
+    Nan::TryCatch try_catch;
+
+    // Call the callback
+    worker->callback->Call(ARRAY_SIZE(info), info);
+
+    // If we have an exception handle it as a fatalexception
+    if (try_catch.HasCaught()) {
+      Nan::FatalException(try_catch);
+    }
+  }
+
+  // Clean up the memory
+  delete worker->callback;
+  delete worker;
+}
+
+// Exporting function
+NAN_MODULE_INIT(init) {
+  Kerberos::Initialize(target);
+  KerberosContext::Initialize(target);
+}
+
+NODE_MODULE(kerberos, init);
diff --git a/lib/kerberos.h b/lib/kerberos.h
new file mode 100644
index 0000000..beafa4d
--- /dev/null
+++ b/lib/kerberos.h
@@ -0,0 +1,50 @@
+#ifndef KERBEROS_H
+#define KERBEROS_H
+
+#include <node.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+#include "nan.h"
+#include <node_object_wrap.h>
+#include <v8.h>
+
+extern "C" {
+  #include "kerberosgss.h"
+}
+
+using namespace v8;
+using namespace node;
+
+class Kerberos : public Nan::ObjectWrap {
+
+public:
+  Kerberos();
+  ~Kerberos() {};
+
+  // Constructor used for creating new Kerberos objects from C++
+  static Nan::Persistent<FunctionTemplate> constructor_template;
+
+  // Initialize function for the object
+  static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+
+  // Method available
+  static NAN_METHOD(AuthGSSClientInit);
+  static NAN_METHOD(AuthGSSClientStep);
+  static NAN_METHOD(AuthGSSClientUnwrap);
+  static NAN_METHOD(AuthGSSClientWrap);
+  static NAN_METHOD(AuthGSSClientClean);
+  static NAN_METHOD(AuthGSSServerInit);
+  static NAN_METHOD(AuthGSSServerClean);
+  static NAN_METHOD(AuthGSSServerStep);
+
+private:
+  static NAN_METHOD(New);
+  // Handles the uv calls
+  static void Process(uv_work_t* work_req);
+  // Called after work is done
+  static void After(uv_work_t* work_req);
+};
+
+#endif
diff --git a/lib/kerberos.js b/lib/kerberos.js
new file mode 100644
index 0000000..4ce8fb1
--- /dev/null
+++ b/lib/kerberos.js
@@ -0,0 +1,128 @@
+var kerberos = require('../build/Release/kerberos')
+  , KerberosNative = kerberos.Kerberos;
+
+var Kerberos = function() {
+  this._native_kerberos = new KerberosNative(); 
+}
+
+// callback takes two arguments, an error string if defined and a new context
+// uri should be given as service at host.  Services are not always defined
+// in a straightforward way.  Use 'HTTP' for SPNEGO / Negotiate authentication. 
+Kerberos.prototype.authGSSClientInit = function(uri, flags, callback) {
+  return this._native_kerberos.authGSSClientInit(uri, flags, callback);
+}
+
+// This will obtain credentials using a credentials cache. To override the default
+// location (posible /tmp/krb5cc_nnnnnn, where nnnn is your numeric uid) use 
+// the environment variable KRB5CNAME. 
+// The credentials (suitable for using in an 'Authenticate: ' header, when prefixed
+// with 'Negotiate ') will be available as context.response inside the callback
+// if no error is indicated.
+// callback takes one argument, an error string if defined
+Kerberos.prototype.authGSSClientStep = function(context, challenge, callback) {
+  if(typeof challenge == 'function') {
+    callback = challenge;
+    challenge = '';
+  }
+
+  return this._native_kerberos.authGSSClientStep(context, challenge, callback);
+}
+
+Kerberos.prototype.authGSSClientUnwrap = function(context, challenge, callback) {
+  if(typeof challenge == 'function') {
+    callback = challenge;
+    challenge = '';
+  }
+
+  return this._native_kerberos.authGSSClientUnwrap(context, challenge, callback);
+}
+
+Kerberos.prototype.authGSSClientWrap = function(context, challenge, user_name, callback) {
+  if(typeof user_name == 'function') {
+    callback = user_name;
+    user_name = '';
+  }
+
+  return this._native_kerberos.authGSSClientWrap(context, challenge, user_name, callback);
+}
+
+// free memory used by a context created using authGSSClientInit.
+// callback takes one argument, an error string if defined.
+Kerberos.prototype.authGSSClientClean = function(context, callback) {
+  return this._native_kerberos.authGSSClientClean(context, callback);
+}
+
+// The server will obtain credentials using a keytab.  To override the 
+// default location (probably /etc/krb5.keytab) set the KRB5_KTNAME
+// environment variable.
+// The service name should be in the form service, or service at host.name
+// e.g. for HTTP, use "HTTP" or "HTTP at my.host.name". See gss_import_name
+// for GSS_C_NT_HOSTBASED_SERVICE.
+// callback takes two arguments, an error string if defined and a new context
+Kerberos.prototype.authGSSServerInit = function(service, callback) {
+  return this._native_kerberos.authGSSServerInit(service, callback);
+};
+
+//callback takes one argument, an error string if defined.
+Kerberos.prototype.authGSSServerClean = function(context, callback) {
+  return this._native_kerberos.authGSSServerClean(context, callback);
+};
+
+// authData should be the base64 encoded authentication data obtained
+// from client, e.g., in the Authorization header (without the leading 
+// "Negotiate " string) during SPNEGO authentication.  The authenticated user 
+// is available in context.username after successful authentication.
+// callback takes one argument, an error string if defined.
+Kerberos.prototype.authGSSServerStep = function(context, authData, callback) {
+  return this._native_kerberos.authGSSServerStep(context, authData, callback);
+};
+
+Kerberos.prototype.acquireAlternateCredentials = function(user_name, password, domain) {
+  return this._native_kerberos.acquireAlternateCredentials(user_name, password, domain); 
+}
+
+Kerberos.prototype.prepareOutboundPackage = function(principal, inputdata) {
+  return this._native_kerberos.prepareOutboundPackage(principal, inputdata); 
+}
+
+Kerberos.prototype.decryptMessage = function(challenge) {
+  return this._native_kerberos.decryptMessage(challenge);
+}
+
+Kerberos.prototype.encryptMessage = function(challenge) {
+  return this._native_kerberos.encryptMessage(challenge); 
+}
+
+Kerberos.prototype.queryContextAttribute = function(attribute) {
+  if(typeof attribute != 'number' && attribute != 0x00) throw new Error("Attribute not supported");
+  return this._native_kerberos.queryContextAttribute(attribute);
+}
+
+// Some useful result codes
+Kerberos.AUTH_GSS_CONTINUE     = 0;
+Kerberos.AUTH_GSS_COMPLETE     = 1;
+     
+// Some useful gss flags 
+Kerberos.GSS_C_DELEG_FLAG      = 1;
+Kerberos.GSS_C_MUTUAL_FLAG     = 2;
+Kerberos.GSS_C_REPLAY_FLAG     = 4;
+Kerberos.GSS_C_SEQUENCE_FLAG   = 8;
+Kerberos.GSS_C_CONF_FLAG       = 16; 
+Kerberos.GSS_C_INTEG_FLAG      = 32;
+Kerberos.GSS_C_ANON_FLAG       = 64;
+Kerberos.GSS_C_PROT_READY_FLAG = 128; 
+Kerberos.GSS_C_TRANS_FLAG      = 256;
+
+// Export Kerberos class
+exports.Kerberos = Kerberos;
+
+// If we have SSPI (windows)
+if(kerberos.SecurityCredentials) {
+  // Put all SSPI classes in it's own namespace
+  exports.SSIP = {
+      SecurityCredentials: require('./win32/wrappers/security_credentials').SecurityCredentials
+    , SecurityContext: require('./win32/wrappers/security_context').SecurityContext
+    , SecurityBuffer: require('./win32/wrappers/security_buffer').SecurityBuffer
+    , SecurityBufferDescriptor: require('./win32/wrappers/security_buffer_descriptor').SecurityBufferDescriptor
+  }
+}
diff --git a/lib/kerberos_context.cc b/lib/kerberos_context.cc
new file mode 100644
index 0000000..f1b164a
--- /dev/null
+++ b/lib/kerberos_context.cc
@@ -0,0 +1,116 @@
+#include "kerberos_context.h"
+
+Nan::Persistent<FunctionTemplate> KerberosContext::constructor_template;
+
+KerberosContext::KerberosContext() : Nan::ObjectWrap() {
+    state = NULL;
+    server_state = NULL;
+}
+
+KerberosContext::~KerberosContext() {
+}
+
+KerberosContext* KerberosContext::New() {
+  Nan::HandleScope scope;
+  Local<Object> obj = Nan::New(constructor_template)->GetFunction()->NewInstance();
+  KerberosContext *kerberos_context = Nan::ObjectWrap::Unwrap<KerberosContext>(obj);
+  return kerberos_context;
+}
+
+NAN_METHOD(KerberosContext::New) {
+  // Create code object
+  KerberosContext *kerberos_context = new KerberosContext();
+  // Wrap it
+  kerberos_context->Wrap(info.This());
+  // Return the object
+  info.GetReturnValue().Set(info.This());
+}
+
+void KerberosContext::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
+  // Grab the scope of the call from Node
+  Nan::HandleScope scope;
+
+  // Define a new function template
+  Local<FunctionTemplate> t = Nan::New<v8::FunctionTemplate>(static_cast<NAN_METHOD((*))>(New));
+  t->InstanceTemplate()->SetInternalFieldCount(1);
+  t->SetClassName(Nan::New<String>("KerberosContext").ToLocalChecked());
+
+  // Get prototype
+  Local<ObjectTemplate> proto = t->PrototypeTemplate();
+
+  // Getter for the response
+  Nan::SetAccessor(proto, Nan::New<String>("response").ToLocalChecked(), KerberosContext::ResponseGetter);
+
+  // Getter for the username
+  Nan::SetAccessor(proto, Nan::New<String>("username").ToLocalChecked(), KerberosContext::UsernameGetter);
+
+  // Getter for the targetname - server side only
+  Nan::SetAccessor(proto, Nan::New<String>("targetname").ToLocalChecked(), KerberosContext::TargetnameGetter);
+
+  // Set persistent
+  constructor_template.Reset(t);
+ // NanAssignPersistent(constructor_template, t);
+
+  // Set the symbol
+  target->ForceSet(Nan::New<String>("KerberosContext").ToLocalChecked(), t->GetFunction());
+}
+
+
+// Response Setter / Getter
+NAN_GETTER(KerberosContext::ResponseGetter) {
+  gss_client_state *client_state;
+  gss_server_state *server_state;
+
+  // Unpack the object
+  KerberosContext *context = Nan::ObjectWrap::Unwrap<KerberosContext>(info.This());
+
+  // Response could come from client or server state...
+  client_state = context->state;
+  server_state = context->server_state;
+
+  // If client state is in use, take response from there, otherwise from server
+  char *response = client_state != NULL ? client_state->response :
+	  server_state != NULL ? server_state->response : NULL;
+
+  if(response == NULL) {
+    info.GetReturnValue().Set(Nan::Null());
+  } else {
+    // Return the response
+    info.GetReturnValue().Set(Nan::New<String>(response).ToLocalChecked());
+  }
+}
+
+// username Getter
+NAN_GETTER(KerberosContext::UsernameGetter) {
+  // Unpack the object
+  KerberosContext *context = Nan::ObjectWrap::Unwrap<KerberosContext>(info.This());
+
+  gss_client_state *client_state = context->state;
+  gss_server_state *server_state = context->server_state;
+
+  // If client state is in use, take response from there, otherwise from server
+  char *username = client_state != NULL ? client_state->username :
+	  server_state != NULL ? server_state->username : NULL;
+
+  if(username == NULL) {
+    info.GetReturnValue().Set(Nan::Null());
+  } else {
+    info.GetReturnValue().Set(Nan::New<String>(username).ToLocalChecked());
+  }
+}
+
+// targetname Getter - server side only
+NAN_GETTER(KerberosContext::TargetnameGetter) {
+  // Unpack the object
+  KerberosContext *context = Nan::ObjectWrap::Unwrap<KerberosContext>(info.This());
+
+  gss_server_state *server_state = context->server_state;
+
+  char *targetname = server_state != NULL ? server_state->targetname : NULL;
+
+  if(targetname == NULL) {
+    info.GetReturnValue().Set(Nan::Null());
+  } else {
+    info.GetReturnValue().Set(Nan::New<String>(targetname).ToLocalChecked());
+  }
+}
diff --git a/lib/kerberos_context.h b/lib/kerberos_context.h
new file mode 100644
index 0000000..b20ff61
--- /dev/null
+++ b/lib/kerberos_context.h
@@ -0,0 +1,63 @@
+#ifndef KERBEROS_CONTEXT_H
+#define KERBEROS_CONTEXT_H
+
+#include <node.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+#include "nan.h"
+#include <node_object_wrap.h>
+#include <v8.h>
+
+extern "C" {
+  #include "kerberosgss.h"
+}
+
+using namespace v8;
+using namespace node;
+
+class KerberosContext : public Nan::ObjectWrap {
+
+public:
+  KerberosContext();
+  ~KerberosContext();
+
+  static inline bool HasInstance(Local<Value> val) {
+    if (!val->IsObject()) return false;
+    Local<Object> obj = val->ToObject();
+    return Nan::New(constructor_template)->HasInstance(obj);
+  };
+
+  inline bool IsClientInstance() {
+      return state != NULL;
+  }
+
+  inline bool IsServerInstance() {
+      return server_state != NULL;
+  }
+
+  // Constructor used for creating new Kerberos objects from C++
+  static Nan::Persistent<FunctionTemplate> constructor_template;
+
+  // Initialize function for the object
+  static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+
+  // Public constructor
+  static KerberosContext* New();
+
+  // Handle to the kerberos client context
+  gss_client_state *state;
+
+  // Handle to the kerberos server context
+  gss_server_state *server_state;
+
+private:
+  static NAN_METHOD(New);
+  // In either client state or server state
+  static NAN_GETTER(ResponseGetter);
+  static NAN_GETTER(UsernameGetter);
+  // Only in the "server_state"
+  static NAN_GETTER(TargetnameGetter);
+};
+#endif
diff --git a/lib/kerberosgss.c b/lib/kerberosgss.c
new file mode 100644
index 0000000..c6983b5
--- /dev/null
+++ b/lib/kerberosgss.c
@@ -0,0 +1,688 @@
+/**
+ * Copyright (c) 2006-2010 Apple Inc. 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.
+ **/
+
+#include "kerberosgss.h"
+
+#include "base64.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+void die1(const char *message) {
+  if(errno) {
+    perror(message);
+  } else {
+    printf("ERROR: %s\n", message);
+  }
+
+  exit(1);
+}
+
+static gss_client_response *gss_error(const char *func, const char *op, OM_uint32 err_maj, OM_uint32 err_min);
+
+/*
+char* server_principal_details(const char* service, const char* hostname)
+{
+    char match[1024];
+    int match_len = 0;
+    char* result = NULL;
+    
+    int code;
+    krb5_context kcontext;
+    krb5_keytab kt = NULL;
+    krb5_kt_cursor cursor = NULL;
+    krb5_keytab_entry entry;
+    char* pname = NULL;
+    
+    // Generate the principal prefix we want to match
+    snprintf(match, 1024, "%s/%s@", service, hostname);
+    match_len = strlen(match);
+    
+    code = krb5_init_context(&kcontext);
+    if (code)
+    {
+        PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))",
+                                                          "Cannot initialize Kerberos5 context", code));
+        return NULL;
+    }
+    
+    if ((code = krb5_kt_default(kcontext, &kt)))
+    {
+        PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))",
+                                                          "Cannot get default keytab", code));
+        goto end;
+    }
+    
+    if ((code = krb5_kt_start_seq_get(kcontext, kt, &cursor)))
+    {
+        PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))",
+                                                          "Cannot get sequence cursor from keytab", code));
+        goto end;
+    }
+    
+    while ((code = krb5_kt_next_entry(kcontext, kt, &entry, &cursor)) == 0)
+    {
+        if ((code = krb5_unparse_name(kcontext, entry.principal, &pname)))
+        {
+            PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))",
+                                                              "Cannot parse principal name from keytab", code));
+            goto end;
+        }
+        
+        if (strncmp(pname, match, match_len) == 0)
+        {
+            result = malloc(strlen(pname) + 1);
+            strcpy(result, pname);
+            krb5_free_unparsed_name(kcontext, pname);
+            krb5_free_keytab_entry_contents(kcontext, &entry);
+            break;
+        }
+        
+        krb5_free_unparsed_name(kcontext, pname);
+        krb5_free_keytab_entry_contents(kcontext, &entry);
+    }
+    
+    if (result == NULL)
+    {
+        PyErr_SetObject(KrbException_class, Py_BuildValue("((s:i))",
+                                                          "Principal not found in keytab", -1));
+    }
+    
+end:
+    if (cursor)
+        krb5_kt_end_seq_get(kcontext, kt, &cursor);
+    if (kt)
+        krb5_kt_close(kcontext, kt);
+    krb5_free_context(kcontext);
+    
+    return result;
+}
+*/
+gss_client_response *authenticate_gss_client_init(const char* service, long int gss_flags, gss_client_state* state) {
+  OM_uint32 maj_stat;
+  OM_uint32 min_stat;
+  gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER;
+  gss_client_response *response = NULL;
+  int ret = AUTH_GSS_COMPLETE;
+
+  state->server_name = GSS_C_NO_NAME;
+  state->context = GSS_C_NO_CONTEXT;
+  state->gss_flags = gss_flags;
+  state->username = NULL;
+  state->response = NULL;
+  
+  // Import server name first
+  name_token.length = strlen(service);
+  name_token.value = (char *)service;
+  
+  maj_stat = gss_import_name(&min_stat, &name_token, gss_krb5_nt_service_name, &state->server_name);
+  
+  if (GSS_ERROR(maj_stat)) {
+    response = gss_error(__func__, "gss_import_name", maj_stat, min_stat);
+    response->return_code = AUTH_GSS_ERROR;
+    goto end;
+  }
+  
+end:
+  if(response == NULL) {
+    response = calloc(1, sizeof(gss_client_response));
+    if(response == NULL) die1("Memory allocation failed");
+    response->return_code = ret;    
+  }
+
+  return response;
+}
+
+gss_client_response *authenticate_gss_client_clean(gss_client_state *state) {
+  OM_uint32 min_stat;
+  int ret = AUTH_GSS_COMPLETE;
+  gss_client_response *response = NULL;
+  
+  if(state->context != GSS_C_NO_CONTEXT)
+    gss_delete_sec_context(&min_stat, &state->context, GSS_C_NO_BUFFER);
+  
+  if(state->server_name != GSS_C_NO_NAME)
+    gss_release_name(&min_stat, &state->server_name);
+  
+  if(state->username != NULL) {
+    free(state->username);
+    state->username = NULL;
+  }
+
+  if (state->response != NULL) {
+    free(state->response);
+    state->response = NULL;
+  }
+  
+  if(response == NULL) {
+    response = calloc(1, sizeof(gss_client_response));
+    if(response == NULL) die1("Memory allocation failed");
+    response->return_code = ret;    
+  }
+
+  return response;
+}
+
+gss_client_response *authenticate_gss_client_step(gss_client_state* state, const char* challenge) {
+  OM_uint32 maj_stat;
+  OM_uint32 min_stat;
+  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+  int ret = AUTH_GSS_CONTINUE;
+  gss_client_response *response = NULL;
+  
+  // Always clear out the old response
+  if (state->response != NULL) {
+    free(state->response);
+    state->response = NULL;
+  }
+  
+  // If there is a challenge (data from the server) we need to give it to GSS
+  if (challenge && *challenge) {
+    int len;
+    input_token.value = base64_decode(challenge, &len);
+    input_token.length = len;
+  }
+  
+  // Do GSSAPI step
+  maj_stat = gss_init_sec_context(&min_stat,
+                                  GSS_C_NO_CREDENTIAL,
+                                  &state->context,
+                                  state->server_name,
+                                  GSS_C_NO_OID,
+                                  (OM_uint32)state->gss_flags,
+                                  0,
+                                  GSS_C_NO_CHANNEL_BINDINGS,
+                                  &input_token,
+                                  NULL,
+                                  &output_token,
+                                  NULL,
+                                  NULL);
+
+  if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED)) {
+    response = gss_error(__func__, "gss_init_sec_context", maj_stat, min_stat);
+    response->return_code = AUTH_GSS_ERROR;
+    goto end;
+  }
+  
+  ret = (maj_stat == GSS_S_COMPLETE) ? AUTH_GSS_COMPLETE : AUTH_GSS_CONTINUE;
+  // Grab the client response to send back to the server
+  if(output_token.length) {
+    state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);
+    maj_stat = gss_release_buffer(&min_stat, &output_token);
+  }
+  
+  // Try to get the user name if we have completed all GSS operations
+  if (ret == AUTH_GSS_COMPLETE) {
+    gss_name_t gssuser = GSS_C_NO_NAME;
+    maj_stat = gss_inquire_context(&min_stat, state->context, &gssuser, NULL, NULL, NULL,  NULL, NULL, NULL);
+    
+    if(GSS_ERROR(maj_stat)) {
+      response = gss_error(__func__, "gss_inquire_context", maj_stat, min_stat);
+      response->return_code = AUTH_GSS_ERROR;
+      goto end;
+    }
+    
+    gss_buffer_desc name_token;
+    name_token.length = 0;
+    maj_stat = gss_display_name(&min_stat, gssuser, &name_token, NULL);
+    
+    if(GSS_ERROR(maj_stat)) {
+      if(name_token.value)
+        gss_release_buffer(&min_stat, &name_token);
+      gss_release_name(&min_stat, &gssuser);
+      
+      response = gss_error(__func__, "gss_display_name", maj_stat, min_stat);
+      response->return_code = AUTH_GSS_ERROR;
+      goto end;
+    } else {
+      state->username = (char *)malloc(name_token.length + 1);
+      if(state->username == NULL) die1("Memory allocation failed");
+      strncpy(state->username, (char*) name_token.value, name_token.length);
+      state->username[name_token.length] = 0;
+      gss_release_buffer(&min_stat, &name_token);
+      gss_release_name(&min_stat, &gssuser);
+    }
+  }
+
+end:
+  if(output_token.value)
+    gss_release_buffer(&min_stat, &output_token);
+  if(input_token.value)
+    free(input_token.value);
+
+  if(response == NULL) {
+    response = calloc(1, sizeof(gss_client_response));
+    if(response == NULL) die1("Memory allocation failed");
+    response->return_code = ret;
+  }
+
+  // Return the response
+  return response;
+}
+
+gss_client_response *authenticate_gss_client_unwrap(gss_client_state *state, const char *challenge) {
+  OM_uint32 maj_stat;
+  OM_uint32 min_stat;
+  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+  gss_client_response *response = NULL;
+  int ret = AUTH_GSS_CONTINUE;
+    
+  // Always clear out the old response
+  if(state->response != NULL) {
+    free(state->response);
+    state->response = NULL;
+  }
+    
+  // If there is a challenge (data from the server) we need to give it to GSS
+  if(challenge && *challenge) {
+    int len;
+    input_token.value = base64_decode(challenge, &len);
+    input_token.length = len;
+  }
+    
+  // Do GSSAPI step
+  maj_stat = gss_unwrap(&min_stat,
+                          state->context,
+                          &input_token,
+                          &output_token,
+                          NULL,
+                          NULL);
+    
+  if(maj_stat != GSS_S_COMPLETE) {
+    response = gss_error(__func__, "gss_unwrap", maj_stat, min_stat);
+    response->return_code = AUTH_GSS_ERROR;
+    goto end;
+  } else {
+    ret = AUTH_GSS_COMPLETE;    
+  }
+    
+  // Grab the client response
+  if(output_token.length) {
+    state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);
+    gss_release_buffer(&min_stat, &output_token);
+  }
+end:
+  if(output_token.value)
+    gss_release_buffer(&min_stat, &output_token);
+  if(input_token.value)
+    free(input_token.value);
+
+  if(response == NULL) {
+    response = calloc(1, sizeof(gss_client_response));
+    if(response == NULL) die1("Memory allocation failed");
+    response->return_code = ret;
+  }
+
+  // Return the response
+  return response;
+}
+
+gss_client_response *authenticate_gss_client_wrap(gss_client_state* state, const char* challenge, const char* user) {
+  OM_uint32 maj_stat;
+  OM_uint32 min_stat;
+  gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+  gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+  int ret = AUTH_GSS_CONTINUE;
+  gss_client_response *response = NULL;
+  char buf[4096], server_conf_flags;
+  unsigned long buf_size;
+    
+  // Always clear out the old response
+  if(state->response != NULL) {
+    free(state->response);
+    state->response = NULL;
+  }
+    
+  if(challenge && *challenge) {
+    int len;
+    input_token.value = base64_decode(challenge, &len);
+    input_token.length = len;
+  }
+    
+  if(user) {
+    // get bufsize
+    server_conf_flags = ((char*) input_token.value)[0];
+    ((char*) input_token.value)[0] = 0;
+    buf_size = ntohl(*((long *) input_token.value));
+    free(input_token.value);
+#ifdef PRINTFS
+    printf("User: %s, %c%c%c\n", user,
+               server_conf_flags & GSS_AUTH_P_NONE      ? 'N' : '-',
+               server_conf_flags & GSS_AUTH_P_INTEGRITY ? 'I' : '-',
+               server_conf_flags & GSS_AUTH_P_PRIVACY   ? 'P' : '-');
+    printf("Maximum GSS token size is %ld\n", buf_size);
+#endif
+        
+    // agree to terms (hack!)
+    buf_size = htonl(buf_size); // not relevant without integrity/privacy
+    memcpy(buf, &buf_size, 4);
+    buf[0] = GSS_AUTH_P_NONE;
+    // server decides if principal can log in as user
+    strncpy(buf + 4, user, sizeof(buf) - 4);
+    input_token.value = buf;
+    input_token.length = 4 + strlen(user);
+  }
+    
+  // Do GSSAPI wrap
+  maj_stat = gss_wrap(&min_stat,
+            state->context,
+            0,
+            GSS_C_QOP_DEFAULT,
+            &input_token,
+            NULL,
+            &output_token);
+    
+  if (maj_stat != GSS_S_COMPLETE) {
+    response = gss_error(__func__, "gss_wrap", maj_stat, min_stat);
+    response->return_code = AUTH_GSS_ERROR;
+    goto end;
+  } else
+    ret = AUTH_GSS_COMPLETE;
+  // Grab the client response to send back to the server
+  if (output_token.length) {
+    state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);;
+    gss_release_buffer(&min_stat, &output_token);
+  }
+end:
+  if (output_token.value)
+    gss_release_buffer(&min_stat, &output_token);
+
+  if(response == NULL) {
+    response = calloc(1, sizeof(gss_client_response));
+    if(response == NULL) die1("Memory allocation failed");
+    response->return_code = ret;
+  }
+
+  // Return the response
+  return response;
+}
+
+gss_client_response *authenticate_gss_server_init(const char *service, gss_server_state *state)
+{
+    OM_uint32 maj_stat;
+    OM_uint32 min_stat;
+    gss_buffer_desc name_token = GSS_C_EMPTY_BUFFER;
+    int ret = AUTH_GSS_COMPLETE;
+    gss_client_response *response = NULL;
+    
+    state->context = GSS_C_NO_CONTEXT;
+    state->server_name = GSS_C_NO_NAME;
+    state->client_name = GSS_C_NO_NAME;
+    state->server_creds = GSS_C_NO_CREDENTIAL;
+    state->client_creds = GSS_C_NO_CREDENTIAL;
+    state->username = NULL;
+    state->targetname = NULL;
+    state->response = NULL;
+
+    // Server name may be empty which means we aren't going to create our own creds
+    size_t service_len = strlen(service);
+    if (service_len != 0)
+    {
+        // Import server name first
+        name_token.length = strlen(service);
+        name_token.value = (char *)service;
+        
+        maj_stat = gss_import_name(&min_stat, &name_token, GSS_C_NT_HOSTBASED_SERVICE, &state->server_name);
+        
+        if (GSS_ERROR(maj_stat))
+        {
+            response = gss_error(__func__, "gss_import_name", maj_stat, min_stat);
+            response->return_code = AUTH_GSS_ERROR;
+            goto end;
+        }
+        
+        // Get credentials
+        maj_stat = gss_acquire_cred(&min_stat, state->server_name, GSS_C_INDEFINITE,
+                                    GSS_C_NO_OID_SET, GSS_C_ACCEPT, &state->server_creds, NULL, NULL);
+        
+        if (GSS_ERROR(maj_stat))
+        {
+            response = gss_error(__func__, "gss_acquire_cred", maj_stat, min_stat);
+            response->return_code = AUTH_GSS_ERROR;
+            goto end;
+        }
+    }
+    
+end:
+    if(response == NULL) {
+      response = calloc(1, sizeof(gss_client_response));
+      if(response == NULL) die1("Memory allocation failed");
+      response->return_code = ret;
+    }
+
+    // Return the response
+    return response;
+}
+
+gss_client_response *authenticate_gss_server_clean(gss_server_state *state)
+{
+    OM_uint32 min_stat;
+    int ret = AUTH_GSS_COMPLETE;
+    gss_client_response *response = NULL;
+    
+    if (state->context != GSS_C_NO_CONTEXT)
+        gss_delete_sec_context(&min_stat, &state->context, GSS_C_NO_BUFFER);
+    if (state->server_name != GSS_C_NO_NAME)
+        gss_release_name(&min_stat, &state->server_name);
+    if (state->client_name != GSS_C_NO_NAME)
+        gss_release_name(&min_stat, &state->client_name);
+    if (state->server_creds != GSS_C_NO_CREDENTIAL)
+        gss_release_cred(&min_stat, &state->server_creds);
+    if (state->client_creds != GSS_C_NO_CREDENTIAL)
+        gss_release_cred(&min_stat, &state->client_creds);
+    if (state->username != NULL)
+    {
+        free(state->username);
+        state->username = NULL;
+    }
+    if (state->targetname != NULL)
+    {
+        free(state->targetname);
+        state->targetname = NULL;
+    }
+    if (state->response != NULL)
+    {
+        free(state->response);
+        state->response = NULL;
+    }
+    
+    if(response == NULL) {
+      response = calloc(1, sizeof(gss_client_response));
+      if(response == NULL) die1("Memory allocation failed");
+      response->return_code = ret;
+    }
+
+    // Return the response
+    return response;
+}
+
+gss_client_response *authenticate_gss_server_step(gss_server_state *state, const char *auth_data)
+{
+    OM_uint32 maj_stat;
+    OM_uint32 min_stat;
+    gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
+    int ret = AUTH_GSS_CONTINUE;
+    gss_client_response *response = NULL;
+    
+    // Always clear out the old response
+    if (state->response != NULL)
+    {
+        free(state->response);
+        state->response = NULL;
+    }
+    
+    if (auth_data && *auth_data)
+    {
+        int len;
+        input_token.value = base64_decode(auth_data, &len);
+        input_token.length = len;
+    }
+    else
+    {
+	response = calloc(1, sizeof(gss_client_response));
+	if(response == NULL) die1("Memory allocation failed");
+        response->message = strdup("No auth_data value in request from client");
+        response->return_code = AUTH_GSS_ERROR;
+        goto end;
+    }
+    
+    maj_stat = gss_accept_sec_context(&min_stat,
+                                      &state->context,
+                                      state->server_creds,
+                                      &input_token,
+                                      GSS_C_NO_CHANNEL_BINDINGS,
+                                      &state->client_name,
+                                      NULL,
+                                      &output_token,
+                                      NULL,
+                                      NULL,
+                                      &state->client_creds);
+    
+    if (GSS_ERROR(maj_stat))
+    {
+        response = gss_error(__func__, "gss_accept_sec_context", maj_stat, min_stat);
+        response->return_code = AUTH_GSS_ERROR;
+        goto end;
+    }
+    
+    // Grab the server response to send back to the client
+    if (output_token.length)
+    {
+        state->response = base64_encode((const unsigned char *)output_token.value, output_token.length);
+        maj_stat = gss_release_buffer(&min_stat, &output_token);
+    }
+    
+    // Get the user name
+    maj_stat = gss_display_name(&min_stat, state->client_name, &output_token, NULL);
+    if (GSS_ERROR(maj_stat))
+    {
+        response = gss_error(__func__, "gss_display_name", maj_stat, min_stat);
+        response->return_code = AUTH_GSS_ERROR;
+        goto end;
+    }
+    state->username = (char *)malloc(output_token.length + 1);
+    strncpy(state->username, (char*) output_token.value, output_token.length);
+    state->username[output_token.length] = 0;
+    
+    // Get the target name if no server creds were supplied
+    if (state->server_creds == GSS_C_NO_CREDENTIAL)
+    {
+        gss_name_t target_name = GSS_C_NO_NAME;
+        maj_stat = gss_inquire_context(&min_stat, state->context, NULL, &target_name, NULL, NULL, NULL, NULL, NULL);
+        if (GSS_ERROR(maj_stat))
+        {
+            response = gss_error(__func__, "gss_inquire_context", maj_stat, min_stat);
+            response->return_code = AUTH_GSS_ERROR;
+            goto end;
+        }
+        maj_stat = gss_display_name(&min_stat, target_name, &output_token, NULL);
+        if (GSS_ERROR(maj_stat))
+        {
+            response = gss_error(__func__, "gss_display_name", maj_stat, min_stat);
+            response->return_code = AUTH_GSS_ERROR;
+            goto end;
+        }
+        state->targetname = (char *)malloc(output_token.length + 1);
+        strncpy(state->targetname, (char*) output_token.value, output_token.length);
+        state->targetname[output_token.length] = 0;
+    }
+
+    ret = AUTH_GSS_COMPLETE;
+    
+end:
+    if (output_token.length)
+        gss_release_buffer(&min_stat, &output_token);
+    if (input_token.value)
+        free(input_token.value);
+
+    if(response == NULL) {
+      response = calloc(1, sizeof(gss_client_response));
+      if(response == NULL) die1("Memory allocation failed");
+      response->return_code = ret;
+    }
+
+    // Return the response
+    return response;
+}
+
+gss_client_response *gss_error(const char *func, const char *op, OM_uint32 err_maj, OM_uint32 err_min) {
+  OM_uint32 maj_stat, min_stat;
+  OM_uint32 msg_ctx = 0;
+  gss_buffer_desc status_string;
+
+  gss_client_response *response = calloc(1, sizeof(gss_client_response));
+  if(response == NULL) die1("Memory allocation failed");
+  
+  char *message = NULL;
+  message = calloc(1024, 1);
+  if(message == NULL) die1("Memory allocation failed");
+
+  response->message = message;
+
+  int nleft = 1024;
+  int n;
+
+  n = snprintf(message, nleft, "%s(%s)", func, op);
+  message += n;
+  nleft -= n;
+
+  do {
+    maj_stat = gss_display_status (&min_stat,
+                                   err_maj,
+                                   GSS_C_GSS_CODE,
+                                   GSS_C_NO_OID,
+                                   &msg_ctx,
+                                   &status_string);
+    if(GSS_ERROR(maj_stat))
+      break;
+    
+    n = snprintf(message, nleft, ": %.*s",
+	    (int)status_string.length, (char*)status_string.value);
+    message += n;
+    nleft -= n;
+
+    gss_release_buffer(&min_stat, &status_string);
+    
+    maj_stat = gss_display_status (&min_stat,
+                                   err_min,
+                                   GSS_C_MECH_CODE,
+                                   GSS_C_NULL_OID,
+                                   &msg_ctx,
+                                   &status_string);
+    if(!GSS_ERROR(maj_stat)) {
+	n = snprintf(message, nleft, ": %.*s",
+		(int)status_string.length, (char*)status_string.value);
+	message += n;
+	nleft -= n;
+
+      gss_release_buffer(&min_stat, &status_string);
+    }
+  } while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
+
+  return response;
+}
+
+#pragma clang diagnostic pop
+
diff --git a/lib/kerberosgss.h b/lib/kerberosgss.h
new file mode 100644
index 0000000..145613f
--- /dev/null
+++ b/lib/kerberosgss.h
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2006-2009 Apple Inc. 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.
+ **/
+#ifndef KERBEROS_GSS_H
+#define KERBEROS_GSS_H
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+#define krb5_get_err_text(context,code) error_message(code)
+
+#define AUTH_GSS_ERROR      -1
+#define AUTH_GSS_COMPLETE    1
+#define AUTH_GSS_CONTINUE    0
+
+#define GSS_AUTH_P_NONE         1
+#define GSS_AUTH_P_INTEGRITY    2
+#define GSS_AUTH_P_PRIVACY      4
+
+typedef struct {
+  int return_code;
+  char *message;
+} gss_client_response;
+
+typedef struct {
+  gss_ctx_id_t     context;
+  gss_name_t       server_name;
+  long int         gss_flags;
+  char*            username;
+  char*            response;
+} gss_client_state;
+
+typedef struct {
+  gss_ctx_id_t     context;
+  gss_name_t       server_name;
+  gss_name_t       client_name;
+  gss_cred_id_t    server_creds;
+  gss_cred_id_t    client_creds;
+  char*            username;
+  char*            targetname;
+  char*            response;
+} gss_server_state;
+
+// char* server_principal_details(const char* service, const char* hostname);
+
+gss_client_response *authenticate_gss_client_init(const char* service, long int gss_flags, gss_client_state* state);
+gss_client_response *authenticate_gss_client_clean(gss_client_state *state);
+gss_client_response *authenticate_gss_client_step(gss_client_state *state, const char *challenge);
+gss_client_response *authenticate_gss_client_unwrap(gss_client_state* state, const char* challenge);
+gss_client_response *authenticate_gss_client_wrap(gss_client_state* state, const char* challenge, const char* user);
+
+gss_client_response *authenticate_gss_server_init(const char* service, gss_server_state* state);
+gss_client_response *authenticate_gss_server_clean(gss_server_state *state);
+gss_client_response *authenticate_gss_server_step(gss_server_state *state, const char *challenge);
+
+#endif
diff --git a/lib/sspi.js b/lib/sspi.js
new file mode 100644
index 0000000..d9120fb
--- /dev/null
+++ b/lib/sspi.js
@@ -0,0 +1,15 @@
+// Load the native SSPI classes
+var kerberos = require('../build/Release/kerberos')
+  , Kerberos = kerberos.Kerberos
+  , SecurityBuffer = require('./win32/wrappers/security_buffer').SecurityBuffer
+  , SecurityBufferDescriptor = require('./win32/wrappers/security_buffer_descriptor').SecurityBufferDescriptor
+  , SecurityCredentials = require('./win32/wrappers/security_credentials').SecurityCredentials
+  , SecurityContext = require('./win32/wrappers/security_context').SecurityContext;
+var SSPI = function() {
+}
+
+exports.SSPI = SSPI;
+exports.SecurityBuffer = SecurityBuffer;
+exports.SecurityBufferDescriptor = SecurityBufferDescriptor;
+exports.SecurityCredentials = SecurityCredentials;
+exports.SecurityContext = SecurityContext;
\ No newline at end of file
diff --git a/lib/win32/base64.c b/lib/win32/base64.c
new file mode 100644
index 0000000..502a021
--- /dev/null
+++ b/lib/win32/base64.c
@@ -0,0 +1,121 @@
+/**
+ * Copyright (c) 2006-2008 Apple Inc. 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.
+ **/
+
+#include "base64.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// base64 tables
+static char basis_64[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static signed char index_64[128] =
+{
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
+    -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
+    52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1,-1,-1,-1,
+    -1, 0, 1, 2,  3, 4, 5, 6,  7, 8, 9,10, 11,12,13,14,
+    15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
+    -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
+    41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
+};
+#define CHAR64(c)  (((c) < 0 || (c) > 127) ? -1 : index_64[(c)])
+
+// base64_encode    :    base64 encode
+//
+// value            :    data to encode
+// vlen             :    length of data
+// (result)         :    new char[] - c-str of result
+char *base64_encode(const unsigned char *value, int vlen)
+{
+    char *result = (char *)malloc((vlen * 4) / 3 + 5);
+    char *out = result;
+    unsigned char oval;
+
+    while (vlen >= 3)
+    {
+        *out++ = basis_64[value[0] >> 2];
+        *out++ = basis_64[((value[0] << 4) & 0x30) | (value[1] >> 4)];
+        *out++ = basis_64[((value[1] << 2) & 0x3C) | (value[2] >> 6)];
+        *out++ = basis_64[value[2] & 0x3F];
+        value += 3;
+        vlen -= 3;
+    }
+    if (vlen > 0)
+    {
+        *out++ = basis_64[value[0] >> 2];
+        oval = (value[0] << 4) & 0x30;
+        if (vlen > 1) oval |= value[1] >> 4;
+        *out++ = basis_64[oval];
+        *out++ = (vlen < 2) ? '=' : basis_64[(value[1] << 2) & 0x3C];
+        *out++ = '=';
+    }
+    *out = '\0';
+
+    return result;
+}
+
+// base64_decode    :    base64 decode
+//
+// value            :    c-str to decode
+// rlen             :    length of decoded result
+// (result)         :    new unsigned char[] - decoded result
+unsigned char *base64_decode(const char *value, int *rlen)
+{
+    int c1, c2, c3, c4;
+    int vlen = (int)strlen(value);
+    unsigned char *result =(unsigned char *)malloc((vlen * 3) / 4 + 1);
+    unsigned char *out = result;
+    *rlen = 0;
+
+    while (1)
+    {
+        if (value[0]==0)
+            return result;
+        c1 = value[0];
+        if (CHAR64(c1) == -1)
+            goto base64_decode_error;;
+        c2 = value[1];
+        if (CHAR64(c2) == -1)
+            goto base64_decode_error;;
+        c3 = value[2];
+        if ((c3 != '=') && (CHAR64(c3) == -1))
+            goto base64_decode_error;;
+        c4 = value[3];
+        if ((c4 != '=') && (CHAR64(c4) == -1))
+            goto base64_decode_error;;
+
+        value += 4;
+        *out++ = (CHAR64(c1) << 2) | (CHAR64(c2) >> 4);
+        *rlen += 1;
+        if (c3 != '=')
+        {
+            *out++ = ((CHAR64(c2) << 4) & 0xf0) | (CHAR64(c3) >> 2);
+            *rlen += 1;
+            if (c4 != '=')
+            {
+                *out++ = ((CHAR64(c3) << 6) & 0xc0) | CHAR64(c4);
+                *rlen += 1;
+            }
+        }
+    }
+
+base64_decode_error:
+    *result = 0;
+    *rlen = 0;
+    return result;
+}
diff --git a/lib/win32/base64.h b/lib/win32/base64.h
new file mode 100644
index 0000000..f0e1f06
--- /dev/null
+++ b/lib/win32/base64.h
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2006-2008 Apple Inc. 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.
+ **/
+
+char *base64_encode(const unsigned char *value, int vlen);
+unsigned char *base64_decode(const char *value, int *rlen);
diff --git a/lib/win32/kerberos.cc b/lib/win32/kerberos.cc
new file mode 100644
index 0000000..c7b583f
--- /dev/null
+++ b/lib/win32/kerberos.cc
@@ -0,0 +1,51 @@
+#include "kerberos.h"
+#include <stdlib.h>
+#include <tchar.h>
+#include "base64.h"
+#include "wrappers/security_buffer.h"
+#include "wrappers/security_buffer_descriptor.h"
+#include "wrappers/security_context.h"
+#include "wrappers/security_credentials.h"
+
+Nan::Persistent<FunctionTemplate> Kerberos::constructor_template;
+
+Kerberos::Kerberos() : Nan::ObjectWrap() {
+}
+
+void Kerberos::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
+  // Grab the scope of the call from Node
+  Nan::HandleScope scope;
+
+  // Define a new function template
+  Local<FunctionTemplate> t = Nan::New<FunctionTemplate>(New);
+  t->InstanceTemplate()->SetInternalFieldCount(1);
+  t->SetClassName(Nan::New<String>("Kerberos").ToLocalChecked());
+
+  // Set persistent
+  constructor_template.Reset(t);
+
+  // Set the symbol
+  Nan::Set(target, Nan::New<String>("Kerberos").ToLocalChecked(), t->GetFunction());
+}
+
+NAN_METHOD(Kerberos::New) {
+  // Load the security.dll library
+  load_library();
+  // Create a Kerberos instance
+  Kerberos *kerberos = new Kerberos();
+  // Return the kerberos object
+  kerberos->Wrap(info.This());
+  // Return the object
+  info.GetReturnValue().Set(info.This());
+}
+
+// Exporting function
+NAN_MODULE_INIT(init) {
+  Kerberos::Initialize(target);
+  SecurityContext::Initialize(target);
+  SecurityBuffer::Initialize(target);
+  SecurityBufferDescriptor::Initialize(target);
+  SecurityCredentials::Initialize(target);
+}
+
+NODE_MODULE(kerberos, init);
diff --git a/lib/win32/kerberos.h b/lib/win32/kerberos.h
new file mode 100644
index 0000000..0fd2760
--- /dev/null
+++ b/lib/win32/kerberos.h
@@ -0,0 +1,60 @@
+#ifndef KERBEROS_H
+#define KERBEROS_H
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <v8.h>
+#include "nan.h"
+
+extern "C" {
+  #include "kerberos_sspi.h"
+  #include "base64.h"
+}
+
+using namespace v8;
+using namespace node;
+
+class Kerberos : public Nan::ObjectWrap {
+
+public:
+  Kerberos();
+  ~Kerberos() {};
+
+  // Constructor used for creating new Kerberos objects from C++
+  static Nan::Persistent<FunctionTemplate> constructor_template;
+
+  // Initialize function for the object
+  static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+
+  // Method available
+  static NAN_METHOD(AcquireAlternateCredentials);
+  static NAN_METHOD(PrepareOutboundPackage);
+  static NAN_METHOD(DecryptMessage);
+  static NAN_METHOD(EncryptMessage);
+  static NAN_METHOD(QueryContextAttributes);
+
+private:
+  static NAN_METHOD(New);
+
+  // Pointer to context object
+  SEC_WINNT_AUTH_IDENTITY m_Identity;
+  // credentials
+  CredHandle m_Credentials;
+  // Expiry time for ticket
+  TimeStamp Expiration;
+  // package info
+  SecPkgInfo m_PkgInfo;
+  // context
+  CtxtHandle m_Context;
+  // Do we have a context
+  bool m_HaveContext;
+  // Attributes
+  DWORD CtxtAttr;
+
+  // Handles the uv calls
+  static void Process(uv_work_t* work_req);
+  // Called after work is done
+  static void After(uv_work_t* work_req);
+};
+
+#endif
\ No newline at end of file
diff --git a/lib/win32/kerberos_sspi.c b/lib/win32/kerberos_sspi.c
new file mode 100644
index 0000000..d75c9ab
--- /dev/null
+++ b/lib/win32/kerberos_sspi.c
@@ -0,0 +1,244 @@
+#include "kerberos_sspi.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+static HINSTANCE _sspi_security_dll = NULL; 
+static HINSTANCE _sspi_secur32_dll = NULL;
+
+/**
+ * Encrypt A Message
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_EncryptMessage(PCtxtHandle phContext, unsigned long fQOP, PSecBufferDesc pMessage, unsigned long MessageSeqNo) {
+  // Create function pointer instance
+  encryptMessage_fn pfn_encryptMessage = NULL;
+
+  // Return error if library not loaded
+  if(_sspi_security_dll == NULL) return -1;
+
+  // Map function to library method
+  pfn_encryptMessage = (encryptMessage_fn)GetProcAddress(_sspi_security_dll, "EncryptMessage");
+  // Check if the we managed to map function pointer
+  if(!pfn_encryptMessage) {
+    printf("GetProcAddress failed.\n");
+    return -2;
+  }
+
+  // Call the function
+  return (*pfn_encryptMessage)(phContext, fQOP, pMessage, MessageSeqNo);
+}
+
+/**
+ * Acquire Credentials
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_AcquireCredentialsHandle(
+  LPSTR pszPrincipal, LPSTR pszPackage, unsigned long fCredentialUse,
+  void * pvLogonId, void * pAuthData, SEC_GET_KEY_FN pGetKeyFn, void * pvGetKeyArgument,
+  PCredHandle phCredential, PTimeStamp ptsExpiry
+) {
+  SECURITY_STATUS     status;
+  // Create function pointer instance
+  acquireCredentialsHandle_fn pfn_acquireCredentialsHandle = NULL;
+
+  // Return error if library not loaded
+  if(_sspi_security_dll == NULL) return -1;
+
+  // Map function
+  #ifdef _UNICODE
+      pfn_acquireCredentialsHandle = (acquireCredentialsHandle_fn)GetProcAddress(_sspi_security_dll, "AcquireCredentialsHandleW");
+  #else
+      pfn_acquireCredentialsHandle = (acquireCredentialsHandle_fn)GetProcAddress(_sspi_security_dll, "AcquireCredentialsHandleA");
+  #endif
+
+  // Check if the we managed to map function pointer
+  if(!pfn_acquireCredentialsHandle) {
+    printf("GetProcAddress failed.\n");
+    return -2;
+  }
+
+  // Status
+  status = (*pfn_acquireCredentialsHandle)(pszPrincipal, pszPackage, fCredentialUse,
+      pvLogonId, pAuthData, pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry
+    );
+
+  // Call the function
+  return status;
+}
+
+/**
+ * Delete Security Context
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_DeleteSecurityContext(PCtxtHandle phContext) {
+  // Create function pointer instance
+  deleteSecurityContext_fn pfn_deleteSecurityContext = NULL;
+
+  // Return error if library not loaded
+  if(_sspi_security_dll == NULL) return -1;
+  // Map function
+  pfn_deleteSecurityContext = (deleteSecurityContext_fn)GetProcAddress(_sspi_security_dll, "DeleteSecurityContext");
+
+  // Check if the we managed to map function pointer
+  if(!pfn_deleteSecurityContext) {
+    printf("GetProcAddress failed.\n");
+    return -2;
+  }
+
+  // Call the function
+  return (*pfn_deleteSecurityContext)(phContext);
+}
+
+/**
+ * Decrypt Message
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_DecryptMessage(PCtxtHandle phContext, PSecBufferDesc pMessage, unsigned long MessageSeqNo, unsigned long pfQOP) {
+  // Create function pointer instance
+  decryptMessage_fn pfn_decryptMessage = NULL;
+
+  // Return error if library not loaded
+  if(_sspi_security_dll == NULL) return -1;
+  // Map function
+  pfn_decryptMessage = (decryptMessage_fn)GetProcAddress(_sspi_security_dll, "DecryptMessage");
+
+  // Check if the we managed to map function pointer
+  if(!pfn_decryptMessage) {
+    printf("GetProcAddress failed.\n");
+    return -2;
+  }
+
+  // Call the function
+  return (*pfn_decryptMessage)(phContext, pMessage, MessageSeqNo, pfQOP);
+}
+
+/**
+ * Initialize Security Context
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_initializeSecurityContext(
+  PCredHandle phCredential, PCtxtHandle phContext,
+  LPSTR pszTargetName, unsigned long fContextReq, 
+  unsigned long Reserved1, unsigned long TargetDataRep, 
+  PSecBufferDesc pInput, unsigned long Reserved2,
+  PCtxtHandle phNewContext, PSecBufferDesc pOutput,
+  unsigned long * pfContextAttr, PTimeStamp ptsExpiry
+) {
+  SECURITY_STATUS status;
+  // Create function pointer instance
+  initializeSecurityContext_fn pfn_initializeSecurityContext = NULL;
+
+  // Return error if library not loaded
+  if(_sspi_security_dll == NULL) return -1;
+  
+  // Map function
+  #ifdef _UNICODE
+    pfn_initializeSecurityContext = (initializeSecurityContext_fn)GetProcAddress(_sspi_security_dll, "InitializeSecurityContextW");
+  #else
+    pfn_initializeSecurityContext = (initializeSecurityContext_fn)GetProcAddress(_sspi_security_dll, "InitializeSecurityContextA");
+  #endif
+
+  // Check if the we managed to map function pointer
+  if(!pfn_initializeSecurityContext) {
+    printf("GetProcAddress failed.\n");
+    return -2;
+  }
+
+  // Execute intialize context
+  status = (*pfn_initializeSecurityContext)(
+    phCredential, phContext, pszTargetName, fContextReq, 
+    Reserved1, TargetDataRep, pInput, Reserved2,
+    phNewContext, pOutput, pfContextAttr, ptsExpiry
+  );
+
+  // Call the function
+  return status;
+}
+/**
+ * Query Context Attributes
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_QueryContextAttributes(
+  PCtxtHandle phContext, unsigned long ulAttribute, void * pBuffer
+) {
+  // Create function pointer instance
+  queryContextAttributes_fn pfn_queryContextAttributes = NULL;
+
+  // Return error if library not loaded
+  if(_sspi_security_dll == NULL) return -1;
+
+  #ifdef _UNICODE
+    pfn_queryContextAttributes = (queryContextAttributes_fn)GetProcAddress(_sspi_security_dll, "QueryContextAttributesW");
+  #else
+    pfn_queryContextAttributes = (queryContextAttributes_fn)GetProcAddress(_sspi_security_dll, "QueryContextAttributesA");
+  #endif
+
+  // Check if the we managed to map function pointer
+  if(!pfn_queryContextAttributes) {
+    printf("GetProcAddress failed.\n");
+    return -2;
+  }
+
+  // Call the function
+  return (*pfn_queryContextAttributes)(
+    phContext, ulAttribute, pBuffer
+  );
+}
+
+/**
+ * InitSecurityInterface
+ */
+PSecurityFunctionTable _ssip_InitSecurityInterface() {
+  INIT_SECURITY_INTERFACE InitSecurityInterface;
+  PSecurityFunctionTable pSecurityInterface = NULL;
+
+  // Return error if library not loaded
+  if(_sspi_security_dll == NULL) return NULL;
+
+  #ifdef _UNICODE
+    // Get the address of the InitSecurityInterface function.
+    InitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress (
+                                          _sspi_secur32_dll, 
+                                          TEXT("InitSecurityInterfaceW"));
+  #else
+    // Get the address of the InitSecurityInterface function.
+    InitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress (
+                                          _sspi_secur32_dll, 
+                                          TEXT("InitSecurityInterfaceA"));
+  #endif
+
+  if(!InitSecurityInterface) {
+    printf (TEXT("Failed in getting the function address, Error: %x"), GetLastError ());
+    return NULL;
+  }
+
+  // Use InitSecurityInterface to get the function table.
+  pSecurityInterface = (*InitSecurityInterface)();
+
+  if(!pSecurityInterface) {
+    printf (TEXT("Failed in getting the function table, Error: %x"), GetLastError ());
+    return NULL;
+  }
+
+  return pSecurityInterface;
+}
+
+/**
+ * Load security.dll dynamically
+ */
+int load_library() {
+  DWORD err;
+  // Load the library
+  _sspi_security_dll = LoadLibrary("security.dll");
+
+  // Check if the library loaded
+  if(_sspi_security_dll == NULL) {
+    err = GetLastError();
+    return err;
+  }
+
+  // Load the library
+  _sspi_secur32_dll = LoadLibrary("secur32.dll");
+
+  // Check if the library loaded
+  if(_sspi_secur32_dll == NULL) {
+    err = GetLastError();
+    return err;
+  }
+
+  return 0;
+}
\ No newline at end of file
diff --git a/lib/win32/kerberos_sspi.h b/lib/win32/kerberos_sspi.h
new file mode 100644
index 0000000..a3008dc
--- /dev/null
+++ b/lib/win32/kerberos_sspi.h
@@ -0,0 +1,106 @@
+#ifndef SSPI_C_H
+#define SSPI_C_H
+
+#define SECURITY_WIN32 1
+
+#include <windows.h>
+#include <sspi.h>
+
+/**
+ * Encrypt A Message
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_EncryptMessage(PCtxtHandle phContext, unsigned long fQOP, PSecBufferDesc pMessage, unsigned long MessageSeqNo);
+
+typedef DWORD (WINAPI *encryptMessage_fn)(PCtxtHandle phContext, ULONG fQOP, PSecBufferDesc pMessage, ULONG MessageSeqNo);  
+
+/**
+ * Acquire Credentials
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_AcquireCredentialsHandle(
+  LPSTR pszPrincipal,                 // Name of principal
+  LPSTR pszPackage,                   // Name of package
+  unsigned long fCredentialUse,       // Flags indicating use
+  void * pvLogonId,                   // Pointer to logon ID
+  void * pAuthData,                   // Package specific data
+  SEC_GET_KEY_FN pGetKeyFn,           // Pointer to GetKey() func
+  void * pvGetKeyArgument,            // Value to pass to GetKey()
+  PCredHandle phCredential,           // (out) Cred Handle
+  PTimeStamp ptsExpiry                // (out) Lifetime (optional)
+);
+
+typedef DWORD (WINAPI *acquireCredentialsHandle_fn)(
+    LPSTR pszPrincipal, LPSTR pszPackage, unsigned long fCredentialUse,
+    void * pvLogonId, void * pAuthData, SEC_GET_KEY_FN pGetKeyFn, void * pvGetKeyArgument,
+    PCredHandle phCredential, PTimeStamp ptsExpiry
+  );
+
+/**
+ * Delete Security Context
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_DeleteSecurityContext(
+  PCtxtHandle phContext               // Context to delete
+);
+
+typedef DWORD (WINAPI *deleteSecurityContext_fn)(PCtxtHandle phContext);  
+
+/**
+ * Decrypt Message
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_DecryptMessage(
+  PCtxtHandle phContext, 
+  PSecBufferDesc pMessage, 
+  unsigned long MessageSeqNo, 
+  unsigned long pfQOP
+);
+
+typedef DWORD (WINAPI *decryptMessage_fn)(
+  PCtxtHandle phContext, PSecBufferDesc pMessage, unsigned long MessageSeqNo, unsigned long pfQOP);
+
+/**
+ * Initialize Security Context
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_initializeSecurityContext(
+  PCredHandle phCredential,       // Cred to base context
+  PCtxtHandle phContext,          // Existing context (OPT)
+  LPSTR pszTargetName,            // Name of target
+  unsigned long fContextReq,      // Context Requirements
+  unsigned long Reserved1,        // Reserved, MBZ
+  unsigned long TargetDataRep,    // Data rep of target
+  PSecBufferDesc pInput,          // Input Buffers
+  unsigned long Reserved2,        // Reserved, MBZ
+  PCtxtHandle phNewContext,       // (out) New Context handle
+  PSecBufferDesc pOutput,         // (inout) Output Buffers
+  unsigned long * pfContextAttr,  // (out) Context attrs
+  PTimeStamp ptsExpiry            // (out) Life span (OPT)
+);
+
+typedef DWORD (WINAPI *initializeSecurityContext_fn)(
+  PCredHandle phCredential, PCtxtHandle phContext, LPSTR pszTargetName, unsigned long fContextReq, 
+  unsigned long Reserved1, unsigned long TargetDataRep, PSecBufferDesc pInput, unsigned long Reserved2,
+  PCtxtHandle phNewContext, PSecBufferDesc pOutput, unsigned long * pfContextAttr, PTimeStamp ptsExpiry);
+
+/**
+ * Query Context Attributes
+ */
+SECURITY_STATUS SEC_ENTRY _sspi_QueryContextAttributes(
+  PCtxtHandle phContext,          // Context to query
+  unsigned long ulAttribute,      // Attribute to query
+  void * pBuffer                  // Buffer for attributes
+);
+
+typedef DWORD (WINAPI *queryContextAttributes_fn)(
+  PCtxtHandle phContext, unsigned long ulAttribute, void * pBuffer);
+
+/**
+ * InitSecurityInterface
+ */
+PSecurityFunctionTable _ssip_InitSecurityInterface();
+
+typedef DWORD (WINAPI *initSecurityInterface_fn) ();
+
+/**
+ * Load security.dll dynamically
+ */
+int load_library();
+
+#endif
\ No newline at end of file
diff --git a/lib/win32/worker.cc b/lib/win32/worker.cc
new file mode 100644
index 0000000..e7a472f
--- /dev/null
+++ b/lib/win32/worker.cc
@@ -0,0 +1,7 @@
+#include "worker.h"
+
+Worker::Worker() {
+}
+
+Worker::~Worker() {  
+}
\ No newline at end of file
diff --git a/lib/win32/worker.h b/lib/win32/worker.h
new file mode 100644
index 0000000..c2ccb6b
--- /dev/null
+++ b/lib/win32/worker.h
@@ -0,0 +1,38 @@
+#ifndef WORKER_H_
+#define WORKER_H_
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <v8.h>
+#include <nan.h>
+
+using namespace node;
+using namespace v8;
+
+class Worker {
+  public:
+    Worker();
+    virtual ~Worker();
+
+    // libuv's request struct.
+    uv_work_t request;
+    // Callback
+    Nan::Callback *callback;
+    // Parameters
+    void *parameters;
+    // Results
+    void *return_value;
+    // Did we raise an error
+    bool error;
+    // The error message
+    char *error_message;
+    // Error code if not message
+    int error_code;
+    // Any return code
+    int return_code;
+    // Method we are going to fire
+    void (*execute)(Worker *worker);
+    Local<Value> (*mapper)(Worker *worker);
+};
+
+#endif  // WORKER_H_
diff --git a/lib/win32/wrappers/security_buffer.cc b/lib/win32/wrappers/security_buffer.cc
new file mode 100644
index 0000000..fdf8e49
--- /dev/null
+++ b/lib/win32/wrappers/security_buffer.cc
@@ -0,0 +1,101 @@
+#include <node.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <v8.h>
+#include <node_buffer.h>
+#include <cstring>
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+
+#include "security_buffer.h"
+
+using namespace node;
+
+Nan::Persistent<FunctionTemplate> SecurityBuffer::constructor_template;
+
+SecurityBuffer::SecurityBuffer(uint32_t security_type, size_t size) : Nan::ObjectWrap() {
+  this->size = size;
+  this->data = calloc(size, sizeof(char));
+  this->security_type = security_type;
+  // Set up the data in the sec_buffer
+  this->sec_buffer.BufferType = security_type;
+  this->sec_buffer.cbBuffer = (unsigned long)size;
+  this->sec_buffer.pvBuffer = this->data;
+}
+
+SecurityBuffer::SecurityBuffer(uint32_t security_type, size_t size, void *data) : Nan::ObjectWrap() {
+  this->size = size;
+  this->data = data;
+  this->security_type = security_type;
+  // Set up the data in the sec_buffer
+  this->sec_buffer.BufferType = security_type;
+  this->sec_buffer.cbBuffer = (unsigned long)size;
+  this->sec_buffer.pvBuffer = this->data;
+}
+
+SecurityBuffer::~SecurityBuffer() {
+  free(this->data);
+}
+
+NAN_METHOD(SecurityBuffer::New) {
+  SecurityBuffer *security_obj;
+
+  if(info.Length() != 2)
+    return Nan::ThrowError("Two parameters needed integer buffer type and  [32 bit integer/Buffer] required");
+
+  if(!info[0]->IsInt32())
+    return Nan::ThrowError("Two parameters needed integer buffer type and  [32 bit integer/Buffer] required");
+
+  if(!info[1]->IsInt32() && !Buffer::HasInstance(info[1]))
+    return Nan::ThrowError("Two parameters needed integer buffer type and  [32 bit integer/Buffer] required");
+
+  // Unpack buffer type
+  uint32_t buffer_type = info[0]->ToUint32()->Value();
+
+  // If we have an integer
+  if(info[1]->IsInt32()) {
+    security_obj = new SecurityBuffer(buffer_type, info[1]->ToUint32()->Value());
+  } else {
+    // Get the length of the Buffer
+    size_t length = Buffer::Length(info[1]->ToObject());
+    // Allocate space for the internal void data pointer
+    void *data = calloc(length, sizeof(char));
+    // Write the data to out of V8 heap space
+    memcpy(data, Buffer::Data(info[1]->ToObject()), length);
+    // Create new SecurityBuffer
+    security_obj = new SecurityBuffer(buffer_type, length, data);
+  }
+
+  // Wrap it
+  security_obj->Wrap(info.This());
+  // Return the object
+  info.GetReturnValue().Set(info.This());
+}
+
+NAN_METHOD(SecurityBuffer::ToBuffer) {
+  // Unpack the Security Buffer object
+  SecurityBuffer *security_obj = Nan::ObjectWrap::Unwrap<SecurityBuffer>(info.This());
+  // Create a Buffer
+  Local<Object> buffer = Nan::CopyBuffer((char *)security_obj->data, (uint32_t)security_obj->size).ToLocalChecked();
+  // Return the buffer
+  info.GetReturnValue().Set(buffer);
+}
+
+void SecurityBuffer::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
+  // Define a new function template
+  Local<FunctionTemplate> t = Nan::New<FunctionTemplate>(New);
+  t->InstanceTemplate()->SetInternalFieldCount(1);
+  t->SetClassName(Nan::New<String>("SecurityBuffer").ToLocalChecked());
+
+  // Class methods
+  Nan::SetPrototypeMethod(t, "toBuffer", ToBuffer);
+
+  // Set persistent
+  constructor_template.Reset(t);
+
+  // Set the symbol
+  target->ForceSet(Nan::New<String>("SecurityBuffer").ToLocalChecked(), t->GetFunction());
+}
diff --git a/lib/win32/wrappers/security_buffer.h b/lib/win32/wrappers/security_buffer.h
new file mode 100644
index 0000000..0c97d56
--- /dev/null
+++ b/lib/win32/wrappers/security_buffer.h
@@ -0,0 +1,48 @@
+#ifndef SECURITY_BUFFER_H
+#define SECURITY_BUFFER_H
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <v8.h>
+
+#define SECURITY_WIN32 1
+
+#include <winsock2.h>
+#include <windows.h>
+#include <sspi.h>
+#include <nan.h>
+
+using namespace v8;
+using namespace node;
+
+class SecurityBuffer : public Nan::ObjectWrap {
+  public:
+    SecurityBuffer(uint32_t security_type, size_t size);
+    SecurityBuffer(uint32_t security_type, size_t size, void *data);
+    ~SecurityBuffer();
+
+    // Internal values
+    void *data;
+    size_t size;
+    uint32_t security_type;
+    SecBuffer sec_buffer;
+
+    // Has instance check
+    static inline bool HasInstance(Local<Value> val) {
+      if (!val->IsObject()) return false;
+      Local<Object> obj = val->ToObject();
+      return Nan::New(constructor_template)->HasInstance(obj);
+    };
+
+    // Functions available from V8
+    static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+    static NAN_METHOD(ToBuffer);
+
+    // Constructor used for creating new Long objects from C++
+    static Nan::Persistent<FunctionTemplate> constructor_template;
+
+  private:
+    static NAN_METHOD(New);
+};
+
+#endif
\ No newline at end of file
diff --git a/lib/win32/wrappers/security_buffer.js b/lib/win32/wrappers/security_buffer.js
new file mode 100644
index 0000000..4996163
--- /dev/null
+++ b/lib/win32/wrappers/security_buffer.js
@@ -0,0 +1,12 @@
+var SecurityBufferNative = require('../../../build/Release/kerberos').SecurityBuffer;
+
+// Add some attributes
+SecurityBufferNative.VERSION  = 0;
+SecurityBufferNative.EMPTY    = 0;
+SecurityBufferNative.DATA     = 1;
+SecurityBufferNative.TOKEN    = 2;
+SecurityBufferNative.PADDING  = 9;
+SecurityBufferNative.STREAM   = 10;
+
+// Export the modified class
+exports.SecurityBuffer = SecurityBufferNative;
\ No newline at end of file
diff --git a/lib/win32/wrappers/security_buffer_descriptor.cc b/lib/win32/wrappers/security_buffer_descriptor.cc
new file mode 100644
index 0000000..fce0d81
--- /dev/null
+++ b/lib/win32/wrappers/security_buffer_descriptor.cc
@@ -0,0 +1,182 @@
+#include <node.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <v8.h>
+#include <node_buffer.h>
+#include <cstring>
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+
+#define SECURITY_WIN32 1
+
+#include "security_buffer_descriptor.h"
+#include "security_buffer.h"
+
+Nan::Persistent<FunctionTemplate> SecurityBufferDescriptor::constructor_template;
+
+SecurityBufferDescriptor::SecurityBufferDescriptor() : Nan::ObjectWrap() {
+}
+
+SecurityBufferDescriptor::SecurityBufferDescriptor(const Nan::Persistent<Array>& arrayObjectPersistent) : Nan::ObjectWrap() {
+  SecurityBuffer *security_obj = NULL;
+  // Get the Local value
+  Local<Array> arrayObject = Nan::New(arrayObjectPersistent);
+
+  // Safe reference to array
+  this->arrayObject = arrayObject;
+
+  // Unpack the array and ensure we have a valid descriptor
+  this->secBufferDesc.cBuffers = arrayObject->Length();
+  this->secBufferDesc.ulVersion = SECBUFFER_VERSION;
+
+  if(arrayObject->Length() == 1) {
+    // Unwrap  the buffer
+    security_obj = Nan::ObjectWrap::Unwrap<SecurityBuffer>(arrayObject->Get(0)->ToObject());
+    // Assign the buffer
+    this->secBufferDesc.pBuffers = &security_obj->sec_buffer;
+  } else {
+    this->secBufferDesc.pBuffers = new SecBuffer[arrayObject->Length()];
+    this->secBufferDesc.cBuffers = arrayObject->Length();
+
+    // Assign the buffers
+    for(uint32_t i = 0; i < arrayObject->Length(); i++) {
+      security_obj = Nan::ObjectWrap::Unwrap<SecurityBuffer>(arrayObject->Get(i)->ToObject());
+      this->secBufferDesc.pBuffers[i].BufferType = security_obj->sec_buffer.BufferType;
+      this->secBufferDesc.pBuffers[i].pvBuffer = security_obj->sec_buffer.pvBuffer;
+      this->secBufferDesc.pBuffers[i].cbBuffer = security_obj->sec_buffer.cbBuffer;
+    }
+  }
+}
+
+SecurityBufferDescriptor::~SecurityBufferDescriptor() {
+}
+
+size_t SecurityBufferDescriptor::bufferSize() {
+  SecurityBuffer *security_obj = NULL;
+
+  if(this->secBufferDesc.cBuffers == 1) {
+    security_obj = Nan::ObjectWrap::Unwrap<SecurityBuffer>(arrayObject->Get(0)->ToObject());
+    return security_obj->size;
+  } else {
+    int bytesToAllocate = 0;
+
+    for(unsigned int i = 0; i < this->secBufferDesc.cBuffers; i++) {
+      bytesToAllocate += this->secBufferDesc.pBuffers[i].cbBuffer;
+    }
+
+    // Return total size
+    return bytesToAllocate;
+  }
+}
+
+char *SecurityBufferDescriptor::toBuffer() {
+  SecurityBuffer *security_obj = NULL;
+  char *data = NULL;
+
+  if(this->secBufferDesc.cBuffers == 1) {
+    security_obj = Nan::ObjectWrap::Unwrap<SecurityBuffer>(arrayObject->Get(0)->ToObject());
+    data = (char *)malloc(security_obj->size * sizeof(char));
+    memcpy(data, security_obj->data, security_obj->size);
+  } else {
+    size_t bytesToAllocate = this->bufferSize();
+    char *data = (char *)calloc(bytesToAllocate, sizeof(char));
+    int offset = 0;
+
+    for(unsigned int i = 0; i < this->secBufferDesc.cBuffers; i++) {
+      memcpy((data + offset), this->secBufferDesc.pBuffers[i].pvBuffer, this->secBufferDesc.pBuffers[i].cbBuffer);
+      offset +=this->secBufferDesc.pBuffers[i].cbBuffer;
+    }
+
+    // Return the data
+    return data;
+  }
+
+  return data;
+}
+
+NAN_METHOD(SecurityBufferDescriptor::New) {
+  SecurityBufferDescriptor *security_obj;
+  Nan::Persistent<Array> arrayObject;
+
+  if(info.Length() != 1)
+    return Nan::ThrowError("There must be 1 argument passed in where the first argument is a [int32 or an Array of SecurityBuffers]");
+
+  if(!info[0]->IsInt32() && !info[0]->IsArray())
+    return Nan::ThrowError("There must be 1 argument passed in where the first argument is a [int32 or an Array of SecurityBuffers]");
+
+  if(info[0]->IsArray()) {
+    Local<Array> array = Local<Array>::Cast(info[0]);
+    // Iterate over all items and ensure we the right type
+    for(uint32_t i = 0; i < array->Length(); i++) {
+      if(!SecurityBuffer::HasInstance(array->Get(i))) {
+        return Nan::ThrowError("There must be 1 argument passed in where the first argument is a [int32 or an Array of SecurityBuffers]");
+      }
+    }
+  }
+
+  // We have a single integer
+  if(info[0]->IsInt32()) {
+    // Create new SecurityBuffer instance
+    Local<Value> argv[] = {Nan::New<Int32>(0x02), info[0]};
+    Local<Value> security_buffer = Nan::New(SecurityBuffer::constructor_template)->GetFunction()->NewInstance(2, argv);
+    // Create a new array
+    Local<Array> array = Nan::New<Array>(1);
+    // Set the first value
+    array->Set(0, security_buffer);
+
+    // Create persistent handle
+    Nan::Persistent<Array> persistenHandler;
+    persistenHandler.Reset(array);
+
+    // Create descriptor
+    security_obj = new SecurityBufferDescriptor(persistenHandler);
+  } else {
+    // Create a persistent handler
+    Nan::Persistent<Array> persistenHandler;
+    persistenHandler.Reset(Local<Array>::Cast(info[0]));
+    // Create a descriptor
+    security_obj = new SecurityBufferDescriptor(persistenHandler);
+  }
+
+  // Wrap it
+  security_obj->Wrap(info.This());
+  // Return the object
+  info.GetReturnValue().Set(info.This());
+}
+
+NAN_METHOD(SecurityBufferDescriptor::ToBuffer) {
+  // Unpack the Security Buffer object
+  SecurityBufferDescriptor *security_obj = Nan::ObjectWrap::Unwrap<SecurityBufferDescriptor>(info.This());
+
+  // Get the buffer
+  char *buffer_data = security_obj->toBuffer();
+  size_t buffer_size = security_obj->bufferSize();
+
+  // Create a Buffer
+  Local<Object> buffer = Nan::CopyBuffer(buffer_data, (uint32_t)buffer_size).ToLocalChecked();
+
+  // Return the buffer
+  info.GetReturnValue().Set(buffer);
+}
+
+void SecurityBufferDescriptor::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
+  // Grab the scope of the call from Node
+  Nan::HandleScope scope;
+
+  // Define a new function template
+  Local<FunctionTemplate> t = Nan::New<FunctionTemplate>(New);
+  t->InstanceTemplate()->SetInternalFieldCount(1);
+  t->SetClassName(Nan::New<String>("SecurityBufferDescriptor").ToLocalChecked());
+
+  // Class methods
+  Nan::SetPrototypeMethod(t, "toBuffer", ToBuffer);
+
+  // Set persistent
+  constructor_template.Reset(t);
+
+  // Set the symbol
+  target->ForceSet(Nan::New<String>("SecurityBufferDescriptor").ToLocalChecked(), t->GetFunction());
+}
diff --git a/lib/win32/wrappers/security_buffer_descriptor.h b/lib/win32/wrappers/security_buffer_descriptor.h
new file mode 100644
index 0000000..dc28f7e
--- /dev/null
+++ b/lib/win32/wrappers/security_buffer_descriptor.h
@@ -0,0 +1,46 @@
+#ifndef SECURITY_BUFFER_DESCRIPTOR_H
+#define SECURITY_BUFFER_DESCRIPTOR_H
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <v8.h>
+
+#include <WinSock2.h>
+#include <windows.h>
+#include <sspi.h>
+#include <nan.h>
+
+using namespace v8;
+using namespace node;
+
+class SecurityBufferDescriptor : public Nan::ObjectWrap {
+  public:
+    Local<Array> arrayObject;
+    SecBufferDesc secBufferDesc;
+
+    SecurityBufferDescriptor();
+    SecurityBufferDescriptor(const Nan::Persistent<Array>& arrayObjectPersistent);
+    ~SecurityBufferDescriptor();
+
+    // Has instance check
+    static inline bool HasInstance(Local<Value> val) {
+      if (!val->IsObject()) return false;
+      Local<Object> obj = val->ToObject();
+      return Nan::New(constructor_template)->HasInstance(obj);
+    };
+
+    char *toBuffer();
+    size_t bufferSize();
+
+    // Functions available from V8
+    static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+    static NAN_METHOD(ToBuffer);
+
+    // Constructor used for creating new Long objects from C++
+    static Nan::Persistent<FunctionTemplate> constructor_template;
+
+  private:
+    static NAN_METHOD(New);
+};
+
+#endif
\ No newline at end of file
diff --git a/lib/win32/wrappers/security_buffer_descriptor.js b/lib/win32/wrappers/security_buffer_descriptor.js
new file mode 100644
index 0000000..9421392
--- /dev/null
+++ b/lib/win32/wrappers/security_buffer_descriptor.js
@@ -0,0 +1,3 @@
+var SecurityBufferDescriptorNative = require('../../../build/Release/kerberos').SecurityBufferDescriptor;
+// Export the modified class
+exports.SecurityBufferDescriptor = SecurityBufferDescriptorNative;
\ No newline at end of file
diff --git a/lib/win32/wrappers/security_context.cc b/lib/win32/wrappers/security_context.cc
new file mode 100644
index 0000000..5d7ad54
--- /dev/null
+++ b/lib/win32/wrappers/security_context.cc
@@ -0,0 +1,856 @@
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <node.h>
+#include <v8.h>
+#include <node_buffer.h>
+#include <cstring>
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+
+#include "security_context.h"
+#include "security_buffer_descriptor.h"
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0]))
+#endif
+
+static LPSTR DisplaySECError(DWORD ErrCode);
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// UV Lib callbacks
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+static void Process(uv_work_t* work_req) {
+  // Grab the worker
+  Worker *worker = static_cast<Worker*>(work_req->data);
+  // Execute the worker code
+  worker->execute(worker);
+}
+
+static void After(uv_work_t* work_req) {
+  // Grab the scope of the call from Node
+  Nan::HandleScope scope;
+
+  // Get the worker reference
+  Worker *worker = static_cast<Worker*>(work_req->data);
+
+  // If we have an error
+  if(worker->error) {
+    Local<Value> err = v8::Exception::Error(Nan::New<String>(worker->error_message).ToLocalChecked());
+    Local<Object> obj = err->ToObject();
+    obj->Set(Nan::New<String>("code").ToLocalChecked(), Nan::New<Int32>(worker->error_code));
+    Local<Value> info[2] = { err, Nan::Null() };
+    // Execute the error
+    Nan::TryCatch try_catch;
+
+    // Call the callback
+    worker->callback->Call(ARRAY_SIZE(info), info);
+
+    // If we have an exception handle it as a fatalexception
+    if (try_catch.HasCaught()) {
+      Nan::FatalException(try_catch);
+    }
+  } else {
+    // // Map the data
+    Local<Value> result = worker->mapper(worker);
+    // Set up the callback with a null first
+    Local<Value> info[2] = { Nan::Null(), result};
+    // Wrap the callback function call in a TryCatch so that we can call
+    // node's FatalException afterwards. This makes it possible to catch
+    // the exception from JavaScript land using the
+    // process.on('uncaughtException') event.
+    Nan::TryCatch try_catch;
+
+    // Call the callback
+    worker->callback->Call(ARRAY_SIZE(info), info);
+
+    // If we have an exception handle it as a fatalexception
+    if (try_catch.HasCaught()) {
+      Nan::FatalException(try_catch);
+    }
+  }
+
+  // Clean up the memory
+  delete worker->callback;
+  delete worker;
+}
+
+Nan::Persistent<FunctionTemplate> SecurityContext::constructor_template;
+
+SecurityContext::SecurityContext() : Nan::ObjectWrap() {
+}
+
+SecurityContext::~SecurityContext() {
+  if(this->hasContext) {
+    _sspi_DeleteSecurityContext(&this->m_Context);
+  }
+}
+
+NAN_METHOD(SecurityContext::New) {
+  PSecurityFunctionTable pSecurityInterface = NULL;
+  DWORD dwNumOfPkgs;
+  SECURITY_STATUS status;
+
+  // Create code object
+  SecurityContext *security_obj = new SecurityContext();
+  // Get security table interface
+  pSecurityInterface = _ssip_InitSecurityInterface();
+  // Call the security interface
+  status = (*pSecurityInterface->EnumerateSecurityPackages)(
+                                                    &dwNumOfPkgs,
+                                                    &security_obj->m_PkgInfo);
+  if(status != SEC_E_OK) {
+    printf(TEXT("Failed in retrieving security packages, Error: %x"), GetLastError());
+    return Nan::ThrowError("Failed in retrieving security packages");
+  }
+
+  // Wrap it
+  security_obj->Wrap(info.This());
+  // Return the object
+  info.GetReturnValue().Set(info.This());
+}
+
+//
+//  Async InitializeContext
+//
+typedef struct SecurityContextStaticInitializeCall {
+  char *service_principal_name_str;
+  char *decoded_input_str;
+  int decoded_input_str_length;
+  SecurityContext *context;
+} SecurityContextStaticInitializeCall;
+
+static void _initializeContext(Worker *worker) {
+  // Status of operation
+  SECURITY_STATUS status;
+  BYTE *out_bound_data_str = NULL;
+  SecurityContextStaticInitializeCall *call = (SecurityContextStaticInitializeCall *)worker->parameters;
+
+  // Structures used for c calls
+  SecBufferDesc ibd, obd;
+  SecBuffer ib, ob;
+
+  //
+  // Prepare data structure for returned data from SSPI
+  ob.BufferType = SECBUFFER_TOKEN;
+  ob.cbBuffer = call->context->m_PkgInfo->cbMaxToken;
+  // Allocate space for return data
+  out_bound_data_str = new BYTE[ob.cbBuffer + sizeof(DWORD)];
+  ob.pvBuffer = out_bound_data_str;
+  // prepare buffer description
+  obd.cBuffers  = 1;
+  obd.ulVersion = SECBUFFER_VERSION;
+  obd.pBuffers  = &ob;
+
+  //
+  // Prepare the data we are passing to the SSPI method
+  if(call->decoded_input_str_length > 0) {
+    ib.BufferType = SECBUFFER_TOKEN;
+    ib.cbBuffer   = call->decoded_input_str_length;
+    ib.pvBuffer   = call->decoded_input_str;
+    // prepare buffer description
+    ibd.cBuffers  = 1;
+    ibd.ulVersion = SECBUFFER_VERSION;
+    ibd.pBuffers  = &ib;
+  }
+
+  // Perform initialization step
+  status = _sspi_initializeSecurityContext(
+      &call->context->security_credentials->m_Credentials
+    , NULL
+    , const_cast<TCHAR*>(call->service_principal_name_str)
+    , 0x02  // MUTUAL
+    , 0
+    , 0     // Network
+    , call->decoded_input_str_length > 0 ? &ibd : NULL
+    , 0
+    , &call->context->m_Context
+    , &obd
+    , &call->context->CtxtAttr
+    , &call->context->Expiration
+  );
+
+  // If we have a ok or continue let's prepare the result
+  if(status == SEC_E_OK
+    || status == SEC_I_COMPLETE_NEEDED
+    || status == SEC_I_CONTINUE_NEEDED
+    || status == SEC_I_COMPLETE_AND_CONTINUE
+  ) {
+    call->context->hasContext = true;
+    call->context->payload = base64_encode((const unsigned char *)ob.pvBuffer, ob.cbBuffer);
+
+    // Set the context
+    worker->return_code = status;
+    worker->return_value = call->context;
+  } else if(status == SEC_E_INSUFFICIENT_MEMORY) {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = "SEC_E_INSUFFICIENT_MEMORY There is not enough memory available to complete the requested action.";
+  } else if(status == SEC_E_INTERNAL_ERROR) {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = "SEC_E_INTERNAL_ERROR An error occurred that did not map to an SSPI error code.";
+  } else if(status == SEC_E_INVALID_HANDLE) {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = "SEC_E_INVALID_HANDLE The handle passed to the function is not valid.";
+  } else if(status == SEC_E_INVALID_TOKEN) {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = "SEC_E_INVALID_TOKEN The error is due to a malformed input token, such as a token corrupted in transit, a token of incorrect size, or a token passed into the wrong security package. Passing a token to the wrong package can happen if the client and server did not negotiate the proper security package.";
+  } else if(status == SEC_E_LOGON_DENIED) {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = "SEC_E_LOGON_DENIED The logon failed.";
+  } else if(status == SEC_E_NO_AUTHENTICATING_AUTHORITY) {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = "SEC_E_NO_AUTHENTICATING_AUTHORITY No authority could be contacted for authentication. The domain name of the authenticating party could be wrong, the domain could be unreachable, or there might have been a trust relationship failure.";
+  } else if(status == SEC_E_NO_CREDENTIALS) {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = "SEC_E_NO_CREDENTIALS No credentials are available in the security package.";
+  } else if(status == SEC_E_TARGET_UNKNOWN) {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = "SEC_E_TARGET_UNKNOWN The target was not recognized.";
+  } else if(status == SEC_E_UNSUPPORTED_FUNCTION) {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = "SEC_E_UNSUPPORTED_FUNCTION A context attribute flag that is not valid (ISC_REQ_DELEGATE or ISC_REQ_PROMPT_FOR_CREDS) was specified in the fContextReq parameter.";
+  } else if(status == SEC_E_WRONG_PRINCIPAL) {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = "SEC_E_WRONG_PRINCIPAL The principal that received the authentication request is not the same as the one passed into the pszTargetName parameter. This indicates a failure in mutual authentication.";
+  } else {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = DisplaySECError(status);
+  }
+
+  // Clean up data
+  if(call->decoded_input_str != NULL) free(call->decoded_input_str);
+  if(call->service_principal_name_str != NULL) free(call->service_principal_name_str);
+}
+
+static Local<Value> _map_initializeContext(Worker *worker) {
+  // Unwrap the security context
+  SecurityContext *context = (SecurityContext *)worker->return_value;
+  // Return the value
+  return context->handle();
+}
+
+NAN_METHOD(SecurityContext::InitializeContext) {
+  char *service_principal_name_str = NULL, *input_str = NULL, *decoded_input_str = NULL;
+  int decoded_input_str_length = NULL;
+  // Store reference to security credentials
+  SecurityCredentials *security_credentials = NULL;
+
+  // We need 3 parameters
+  if(info.Length() != 4)
+    return Nan::ThrowError("Initialize must be called with [credential:SecurityCredential, servicePrincipalName:string, input:string, callback:function]");
+
+  // First parameter must be an instance of SecurityCredentials
+  if(!SecurityCredentials::HasInstance(info[0]))
+    return Nan::ThrowError("First parameter for Initialize must be an instance of SecurityCredentials");
+
+  // Second parameter must be a string
+  if(!info[1]->IsString())
+    return Nan::ThrowError("Second parameter for Initialize must be a string");
+
+  // Third parameter must be a base64 encoded string
+  if(!info[2]->IsString())
+    return Nan::ThrowError("Second parameter for Initialize must be a string");
+
+  // Third parameter must be a callback
+  if(!info[3]->IsFunction())
+    return Nan::ThrowError("Third parameter for Initialize must be a callback function");
+
+  // Let's unpack the values
+  Local<String> service_principal_name = info[1]->ToString();
+  service_principal_name_str = (char *)calloc(service_principal_name->Utf8Length() + 1, sizeof(char));
+  service_principal_name->WriteUtf8(service_principal_name_str);
+
+  // Unpack the user name
+  Local<String> input = info[2]->ToString();
+
+  if(input->Utf8Length() > 0) {
+    input_str = (char *)calloc(input->Utf8Length() + 1, sizeof(char));
+    input->WriteUtf8(input_str);
+
+    // Now let's get the base64 decoded string
+    decoded_input_str = (char *)base64_decode(input_str, &decoded_input_str_length);
+    // Free original allocation
+    free(input_str);
+  }
+
+  // Unpack the Security credentials
+  security_credentials = Nan::ObjectWrap::Unwrap<SecurityCredentials>(info[0]->ToObject());
+  // Create Security context instance
+  Local<Object> security_context_value = Nan::New(constructor_template)->GetFunction()->NewInstance();
+  // Unwrap the security context
+  SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(security_context_value);
+  // Add a reference to the security_credentials
+  security_context->security_credentials = security_credentials;
+
+  // Build the call function
+  SecurityContextStaticInitializeCall *call = (SecurityContextStaticInitializeCall *)calloc(1, sizeof(SecurityContextStaticInitializeCall));
+  call->context = security_context;
+  call->decoded_input_str = decoded_input_str;
+  call->decoded_input_str_length = decoded_input_str_length;
+  call->service_principal_name_str = service_principal_name_str;
+
+  // Callback
+  Local<Function> callback = Local<Function>::Cast(info[3]);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = new Nan::Callback(callback);
+  worker->parameters = call;
+  worker->execute = _initializeContext;
+  worker->mapper = _map_initializeContext;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
+
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+NAN_GETTER(SecurityContext::PayloadGetter) {
+  // Unpack the context object
+  SecurityContext *context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
+  // Return the low bits
+  info.GetReturnValue().Set(Nan::New<String>(context->payload).ToLocalChecked());
+}
+
+NAN_GETTER(SecurityContext::HasContextGetter) {
+  // Unpack the context object
+  SecurityContext *context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
+  // Return the low bits
+  info.GetReturnValue().Set(Nan::New<Boolean>(context->hasContext));
+}
+
+//
+//  Async InitializeContextStep
+//
+typedef struct SecurityContextStepStaticInitializeCall {
+  char *service_principal_name_str;
+  char *decoded_input_str;
+  int decoded_input_str_length;
+  SecurityContext *context;
+} SecurityContextStepStaticInitializeCall;
+
+static void _initializeContextStep(Worker *worker) {
+  // Outbound data array
+  BYTE *out_bound_data_str = NULL;
+  // Status of operation
+  SECURITY_STATUS status;
+  // Unpack data
+  SecurityContextStepStaticInitializeCall *call = (SecurityContextStepStaticInitializeCall *)worker->parameters;
+  SecurityContext *context = call->context;
+  // Structures used for c calls
+  SecBufferDesc ibd, obd;
+  SecBuffer ib, ob;
+
+  //
+  // Prepare data structure for returned data from SSPI
+  ob.BufferType = SECBUFFER_TOKEN;
+  ob.cbBuffer = context->m_PkgInfo->cbMaxToken;
+  // Allocate space for return data
+  out_bound_data_str = new BYTE[ob.cbBuffer + sizeof(DWORD)];
+  ob.pvBuffer = out_bound_data_str;
+  // prepare buffer description
+  obd.cBuffers  = 1;
+  obd.ulVersion = SECBUFFER_VERSION;
+  obd.pBuffers  = &ob;
+
+  //
+  // Prepare the data we are passing to the SSPI method
+  if(call->decoded_input_str_length > 0) {
+    ib.BufferType = SECBUFFER_TOKEN;
+    ib.cbBuffer   = call->decoded_input_str_length;
+    ib.pvBuffer   = call->decoded_input_str;
+    // prepare buffer description
+    ibd.cBuffers  = 1;
+    ibd.ulVersion = SECBUFFER_VERSION;
+    ibd.pBuffers  = &ib;
+  }
+
+  // Perform initialization step
+  status = _sspi_initializeSecurityContext(
+      &context->security_credentials->m_Credentials
+    , context->hasContext == true ? &context->m_Context : NULL
+    , const_cast<TCHAR*>(call->service_principal_name_str)
+    , 0x02  // MUTUAL
+    , 0
+    , 0     // Network
+    , call->decoded_input_str_length ? &ibd : NULL
+    , 0
+    , &context->m_Context
+    , &obd
+    , &context->CtxtAttr
+    , &context->Expiration
+  );
+
+  // If we have a ok or continue let's prepare the result
+  if(status == SEC_E_OK
+    || status == SEC_I_COMPLETE_NEEDED
+    || status == SEC_I_CONTINUE_NEEDED
+    || status == SEC_I_COMPLETE_AND_CONTINUE
+  ) {
+    // Set the new payload
+    if(context->payload != NULL) free(context->payload);
+    context->payload = base64_encode((const unsigned char *)ob.pvBuffer, ob.cbBuffer);
+    worker->return_code = status;
+    worker->return_value = context;
+  } else {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = DisplaySECError(status);
+  }
+
+  // Clean up data
+  if(call->decoded_input_str != NULL) free(call->decoded_input_str);
+  if(call->service_principal_name_str != NULL) free(call->service_principal_name_str);
+}
+
+static Local<Value> _map_initializeContextStep(Worker *worker) {
+  // Unwrap the security context
+  SecurityContext *context = (SecurityContext *)worker->return_value;
+  // Return the value
+  return context->handle();
+}
+
+NAN_METHOD(SecurityContext::InitalizeStep) {
+  char *service_principal_name_str = NULL, *input_str = NULL, *decoded_input_str = NULL;
+  int decoded_input_str_length = NULL;
+
+  // We need 3 parameters
+  if(info.Length() != 3)
+    return Nan::ThrowError("Initialize must be called with [servicePrincipalName:string, input:string, callback:function]");
+
+  // Second parameter must be a string
+  if(!info[0]->IsString())
+    return Nan::ThrowError("First parameter for Initialize must be a string");
+
+  // Third parameter must be a base64 encoded string
+  if(!info[1]->IsString())
+    return Nan::ThrowError("Second parameter for Initialize must be a string");
+
+  // Third parameter must be a base64 encoded string
+  if(!info[2]->IsFunction())
+    return Nan::ThrowError("Third parameter for Initialize must be a callback function");
+
+  // Let's unpack the values
+  Local<String> service_principal_name = info[0]->ToString();
+  service_principal_name_str = (char *)calloc(service_principal_name->Utf8Length() + 1, sizeof(char));
+  service_principal_name->WriteUtf8(service_principal_name_str);
+
+  // Unpack the user name
+  Local<String> input = info[1]->ToString();
+
+  if(input->Utf8Length() > 0) {
+    input_str = (char *)calloc(input->Utf8Length() + 1, sizeof(char));
+    input->WriteUtf8(input_str);
+    // Now let's get the base64 decoded string
+    decoded_input_str = (char *)base64_decode(input_str, &decoded_input_str_length);
+    // Free input string
+    free(input_str);
+  }
+
+  // Unwrap the security context
+  SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
+
+  // Create call structure
+  SecurityContextStepStaticInitializeCall *call = (SecurityContextStepStaticInitializeCall *)calloc(1, sizeof(SecurityContextStepStaticInitializeCall));
+  call->context = security_context;
+  call->decoded_input_str = decoded_input_str;
+  call->decoded_input_str_length = decoded_input_str_length;
+  call->service_principal_name_str = service_principal_name_str;
+
+  // Callback
+  Local<Function> callback = Local<Function>::Cast(info[2]);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = new Nan::Callback(callback);
+  worker->parameters = call;
+  worker->execute = _initializeContextStep;
+  worker->mapper = _map_initializeContextStep;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
+
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+//
+//  Async EncryptMessage
+//
+typedef struct SecurityContextEncryptMessageCall {
+  SecurityContext *context;
+  SecurityBufferDescriptor *descriptor;
+  unsigned long flags;
+} SecurityContextEncryptMessageCall;
+
+static void _encryptMessage(Worker *worker) {
+  SECURITY_STATUS status;
+  // Unpack call
+  SecurityContextEncryptMessageCall *call = (SecurityContextEncryptMessageCall *)worker->parameters;
+  // Unpack the security context
+  SecurityContext *context = call->context;
+  SecurityBufferDescriptor *descriptor = call->descriptor;
+
+  // Let's execute encryption
+  status = _sspi_EncryptMessage(
+      &context->m_Context
+    , call->flags
+    , &descriptor->secBufferDesc
+    , 0
+  );
+
+  // We've got ok
+  if(status == SEC_E_OK) {
+    int bytesToAllocate = (int)descriptor->bufferSize();
+    // Free up existing payload
+    if(context->payload != NULL) free(context->payload);
+    // Save the payload
+    context->payload = base64_encode((unsigned char *)descriptor->toBuffer(), bytesToAllocate);
+    // Set result
+    worker->return_code = status;
+    worker->return_value = context;
+  } else {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = DisplaySECError(status);
+  }
+}
+
+static Local<Value> _map_encryptMessage(Worker *worker) {
+  // Unwrap the security context
+  SecurityContext *context = (SecurityContext *)worker->return_value;
+  // Return the value
+  return context->handle();
+}
+
+NAN_METHOD(SecurityContext::EncryptMessage) {
+  if(info.Length() != 3)
+    return Nan::ThrowError("EncryptMessage takes an instance of SecurityBufferDescriptor, an integer flag and a callback function");
+  if(!SecurityBufferDescriptor::HasInstance(info[0]))
+    return Nan::ThrowError("EncryptMessage takes an instance of SecurityBufferDescriptor, an integer flag and a callback function");
+  if(!info[1]->IsUint32())
+    return Nan::ThrowError("EncryptMessage takes an instance of SecurityBufferDescriptor, an integer flag and a callback function");
+  if(!info[2]->IsFunction())
+    return Nan::ThrowError("EncryptMessage takes an instance of SecurityBufferDescriptor, an integer flag and a callback function");
+
+  // Unpack the security context
+  SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
+
+  // Unpack the descriptor
+  SecurityBufferDescriptor *descriptor = Nan::ObjectWrap::Unwrap<SecurityBufferDescriptor>(info[0]->ToObject());
+
+  // Create call structure
+  SecurityContextEncryptMessageCall *call = (SecurityContextEncryptMessageCall *)calloc(1, sizeof(SecurityContextEncryptMessageCall));
+  call->context = security_context;
+  call->descriptor = descriptor;
+  call->flags = (unsigned long)info[1]->ToInteger()->Value();
+
+  // Callback
+  Local<Function> callback = Local<Function>::Cast(info[2]);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = new Nan::Callback(callback);
+  worker->parameters = call;
+  worker->execute = _encryptMessage;
+  worker->mapper = _map_encryptMessage;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
+
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+//
+//  Async DecryptMessage
+//
+typedef struct SecurityContextDecryptMessageCall {
+  SecurityContext *context;
+  SecurityBufferDescriptor *descriptor;
+} SecurityContextDecryptMessageCall;
+
+static void _decryptMessage(Worker *worker) {
+  unsigned long quality = 0;
+  SECURITY_STATUS status;
+
+  // Unpack parameters
+  SecurityContextDecryptMessageCall *call = (SecurityContextDecryptMessageCall *)worker->parameters;
+  SecurityContext *context = call->context;
+  SecurityBufferDescriptor *descriptor = call->descriptor;
+
+  // Let's execute encryption
+  status = _sspi_DecryptMessage(
+      &context->m_Context
+    , &descriptor->secBufferDesc
+    , 0
+    , (unsigned long)&quality
+  );
+
+  // We've got ok
+  if(status == SEC_E_OK) {
+    int bytesToAllocate = (int)descriptor->bufferSize();
+    // Free up existing payload
+    if(context->payload != NULL) free(context->payload);
+    // Save the payload
+    context->payload = base64_encode((unsigned char *)descriptor->toBuffer(), bytesToAllocate);
+    // Set return values
+    worker->return_code = status;
+    worker->return_value = context;
+  } else {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = DisplaySECError(status);
+  }
+}
+
+static Local<Value> _map_decryptMessage(Worker *worker) {
+  // Unwrap the security context
+  SecurityContext *context = (SecurityContext *)worker->return_value;
+  // Return the value
+  return context->handle();
+}
+
+NAN_METHOD(SecurityContext::DecryptMessage) {
+  if(info.Length() != 2)
+    return Nan::ThrowError("DecryptMessage takes an instance of SecurityBufferDescriptor and a callback function");
+  if(!SecurityBufferDescriptor::HasInstance(info[0]))
+    return Nan::ThrowError("DecryptMessage takes an instance of SecurityBufferDescriptor and a callback function");
+  if(!info[1]->IsFunction())
+    return Nan::ThrowError("DecryptMessage takes an instance of SecurityBufferDescriptor and a callback function");
+
+  // Unpack the security context
+  SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
+  // Unpack the descriptor
+  SecurityBufferDescriptor *descriptor = Nan::ObjectWrap::Unwrap<SecurityBufferDescriptor>(info[0]->ToObject());
+  // Create call structure
+  SecurityContextDecryptMessageCall *call = (SecurityContextDecryptMessageCall *)calloc(1, sizeof(SecurityContextDecryptMessageCall));
+  call->context = security_context;
+  call->descriptor = descriptor;
+
+  // Callback
+  Local<Function> callback = Local<Function>::Cast(info[1]);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = new Nan::Callback(callback);
+  worker->parameters = call;
+  worker->execute = _decryptMessage;
+  worker->mapper = _map_decryptMessage;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
+
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+//
+//  Async QueryContextAttributes
+//
+typedef struct SecurityContextQueryContextAttributesCall {
+  SecurityContext *context;
+  uint32_t attribute;
+} SecurityContextQueryContextAttributesCall;
+
+static void _queryContextAttributes(Worker *worker) {
+  SECURITY_STATUS status;
+
+  // Cast to data structure
+  SecurityContextQueryContextAttributesCall *call = (SecurityContextQueryContextAttributesCall *)worker->parameters;
+
+  // Allocate some space
+  SecPkgContext_Sizes *sizes = (SecPkgContext_Sizes *)calloc(1, sizeof(SecPkgContext_Sizes));
+  // Let's grab the query context attribute
+  status = _sspi_QueryContextAttributes(
+    &call->context->m_Context,
+    call->attribute,
+    sizes
+  );
+
+  if(status == SEC_E_OK) {
+    worker->return_code = status;
+    worker->return_value = sizes;
+  } else {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = DisplaySECError(status);
+  }
+}
+
+static Local<Value> _map_queryContextAttributes(Worker *worker) {
+  // Cast to data structure
+  SecurityContextQueryContextAttributesCall *call = (SecurityContextQueryContextAttributesCall *)worker->parameters;
+  // Unpack the attribute
+  uint32_t attribute = call->attribute;
+
+  // Convert data
+  if(attribute == SECPKG_ATTR_SIZES) {
+    SecPkgContext_Sizes *sizes = (SecPkgContext_Sizes *)worker->return_value;
+    // Create object
+    Local<Object> value = Nan::New<Object>();
+    value->Set(Nan::New<String>("maxToken").ToLocalChecked(), Nan::New<Integer>(uint32_t(sizes->cbMaxToken)));
+    value->Set(Nan::New<String>("maxSignature").ToLocalChecked(), Nan::New<Integer>(uint32_t(sizes->cbMaxSignature)));
+    value->Set(Nan::New<String>("blockSize").ToLocalChecked(), Nan::New<Integer>(uint32_t(sizes->cbBlockSize)));
+    value->Set(Nan::New<String>("securityTrailer").ToLocalChecked(), Nan::New<Integer>(uint32_t(sizes->cbSecurityTrailer)));
+    return value;
+  }
+
+  // Return the value
+  return Nan::Null();
+}
+
+NAN_METHOD(SecurityContext::QueryContextAttributes) {
+  if(info.Length() != 2)
+    return Nan::ThrowError("QueryContextAttributes method takes a an integer Attribute specifier and a callback function");
+  if(!info[0]->IsInt32())
+    return Nan::ThrowError("QueryContextAttributes method takes a an integer Attribute specifier and a callback function");
+  if(!info[1]->IsFunction())
+    return Nan::ThrowError("QueryContextAttributes method takes a an integer Attribute specifier and a callback function");
+
+  // Unpack the security context
+  SecurityContext *security_context = Nan::ObjectWrap::Unwrap<SecurityContext>(info.This());
+
+  // Unpack the int value
+  uint32_t attribute = info[0]->ToInt32()->Value();
+
+  // Check that we have a supported attribute
+  if(attribute != SECPKG_ATTR_SIZES)
+    return Nan::ThrowError("QueryContextAttributes only supports the SECPKG_ATTR_SIZES attribute");
+
+  // Create call structure
+  SecurityContextQueryContextAttributesCall *call = (SecurityContextQueryContextAttributesCall *)calloc(1, sizeof(SecurityContextQueryContextAttributesCall));
+  call->attribute = attribute;
+  call->context = security_context;
+
+  // Callback
+  Local<Function> callback = Local<Function>::Cast(info[1]);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = new Nan::Callback(callback);
+  worker->parameters = call;
+  worker->execute = _queryContextAttributes;
+  worker->mapper = _map_queryContextAttributes;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, Process, (uv_after_work_cb)After);
+
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+void SecurityContext::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
+  // Grab the scope of the call from Node
+  Nan::HandleScope scope;
+
+  // Define a new function template
+  Local<FunctionTemplate> t = Nan::New<v8::FunctionTemplate>(static_cast<NAN_METHOD((*))>(SecurityContext::New));
+  t->InstanceTemplate()->SetInternalFieldCount(1);
+  t->SetClassName(Nan::New<String>("SecurityContext").ToLocalChecked());
+
+  // Class methods
+  Nan::SetMethod(t, "initialize", SecurityContext::InitializeContext);
+
+  // Set up method for the instance
+  Nan::SetPrototypeMethod(t, "initialize", SecurityContext::InitalizeStep);
+  Nan::SetPrototypeMethod(t, "decryptMessage", SecurityContext::DecryptMessage);
+  Nan::SetPrototypeMethod(t, "queryContextAttributes", SecurityContext::QueryContextAttributes);
+  Nan::SetPrototypeMethod(t, "encryptMessage", SecurityContext::EncryptMessage);
+
+  // Get prototype
+  Local<ObjectTemplate> proto = t->PrototypeTemplate();
+
+  // Getter for the response
+  Nan::SetAccessor(proto, Nan::New<String>("payload").ToLocalChecked(), SecurityContext::PayloadGetter);
+  Nan::SetAccessor(proto, Nan::New<String>("hasContext").ToLocalChecked(), SecurityContext::HasContextGetter);
+
+  // Set persistent
+  SecurityContext::constructor_template.Reset(t);
+
+  // Set the symbol
+  target->ForceSet(Nan::New<String>("SecurityContext").ToLocalChecked(), t->GetFunction());
+}
+
+static LPSTR DisplaySECError(DWORD ErrCode) {
+  LPSTR pszName = NULL; // WinError.h
+
+  switch(ErrCode) {
+    case SEC_E_BUFFER_TOO_SMALL:
+      pszName = "SEC_E_BUFFER_TOO_SMALL - The message buffer is too small. Used with the Digest SSP.";
+      break;
+
+    case SEC_E_CRYPTO_SYSTEM_INVALID:
+      pszName = "SEC_E_CRYPTO_SYSTEM_INVALID - The cipher chosen for the security context is not supported. Used with the Digest SSP.";
+      break;
+    case SEC_E_INCOMPLETE_MESSAGE:
+      pszName = "SEC_E_INCOMPLETE_MESSAGE - The data in the input buffer is incomplete. The application needs to read more data from the server and call DecryptMessageSync (General) again.";
+      break;
+
+    case SEC_E_INVALID_HANDLE:
+      pszName = "SEC_E_INVALID_HANDLE - A context handle that is not valid was specified in the phContext parameter. Used with the Digest and Schannel SSPs.";
+      break;
+
+    case SEC_E_INVALID_TOKEN:
+      pszName = "SEC_E_INVALID_TOKEN - The buffers are of the wrong type or no buffer of type SECBUFFER_DATA was found. Used with the Schannel SSP.";
+      break;
+
+    case SEC_E_MESSAGE_ALTERED:
+      pszName = "SEC_E_MESSAGE_ALTERED - The message has been altered. Used with the Digest and Schannel SSPs.";
+      break;
+
+    case SEC_E_OUT_OF_SEQUENCE:
+      pszName = "SEC_E_OUT_OF_SEQUENCE - The message was not received in the correct sequence.";
+      break;
+
+    case SEC_E_QOP_NOT_SUPPORTED:
+      pszName = "SEC_E_QOP_NOT_SUPPORTED - Neither confidentiality nor integrity are supported by the security context. Used with the Digest SSP.";
+      break;
+
+    case SEC_I_CONTEXT_EXPIRED:
+      pszName = "SEC_I_CONTEXT_EXPIRED - The message sender has finished using the connection and has initiated a shutdown.";
+      break;
+
+    case SEC_I_RENEGOTIATE:
+      pszName = "SEC_I_RENEGOTIATE - The remote party requires a new handshake sequence or the application has just initiated a shutdown.";
+      break;
+
+    case SEC_E_ENCRYPT_FAILURE:
+      pszName = "SEC_E_ENCRYPT_FAILURE - The specified data could not be encrypted.";
+      break;
+
+    case SEC_E_DECRYPT_FAILURE:
+      pszName = "SEC_E_DECRYPT_FAILURE - The specified data could not be decrypted.";
+      break;
+    case -1:
+      pszName = "Failed to load security.dll library";
+      break;
+  }
+
+  return pszName;
+}
+
diff --git a/lib/win32/wrappers/security_context.h b/lib/win32/wrappers/security_context.h
new file mode 100644
index 0000000..1d9387d
--- /dev/null
+++ b/lib/win32/wrappers/security_context.h
@@ -0,0 +1,74 @@
+#ifndef SECURITY_CONTEXT_H
+#define SECURITY_CONTEXT_H
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <v8.h>
+
+#define SECURITY_WIN32 1
+
+#include <winsock2.h>
+#include <windows.h>
+#include <sspi.h>
+#include <tchar.h>
+#include "security_credentials.h"
+#include "../worker.h"
+#include <nan.h>
+
+extern "C" {
+  #include "../kerberos_sspi.h"
+  #include "../base64.h"
+}
+
+using namespace v8;
+using namespace node;
+
+class SecurityContext : public Nan::ObjectWrap {
+  public:
+    SecurityContext();
+    ~SecurityContext();
+
+    // Security info package
+    PSecPkgInfo m_PkgInfo;
+    // Do we have a context
+    bool hasContext;
+    // Reference to security credentials
+    SecurityCredentials *security_credentials;
+    // Security context
+    CtxtHandle m_Context;
+    // Attributes
+    DWORD CtxtAttr;
+    // Expiry time for ticket
+    TimeStamp Expiration;
+    // Payload
+    char *payload;
+
+    // Has instance check
+    static inline bool HasInstance(Local<Value> val) {
+      if (!val->IsObject()) return false;
+      Local<Object> obj = val->ToObject();
+      return Nan::New(constructor_template)->HasInstance(obj);
+    };
+
+    // Functions available from V8
+    static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+    static NAN_METHOD(InitializeContext);
+    static NAN_METHOD(InitalizeStep);
+    static NAN_METHOD(DecryptMessage);
+    static NAN_METHOD(QueryContextAttributes);
+    static NAN_METHOD(EncryptMessage);
+
+    // Payload getter
+    static NAN_GETTER(PayloadGetter);
+    // hasContext getter
+    static NAN_GETTER(HasContextGetter);
+
+    // Constructor used for creating new Long objects from C++
+    static Nan::Persistent<FunctionTemplate> constructor_template;
+
+ // private:
+    // Create a new instance
+    static NAN_METHOD(New);
+};
+
+#endif
diff --git a/lib/win32/wrappers/security_context.js b/lib/win32/wrappers/security_context.js
new file mode 100644
index 0000000..ef04e92
--- /dev/null
+++ b/lib/win32/wrappers/security_context.js
@@ -0,0 +1,3 @@
+var SecurityContextNative = require('../../../build/Release/kerberos').SecurityContext;
+// Export the modified class
+exports.SecurityContext = SecurityContextNative;
\ No newline at end of file
diff --git a/lib/win32/wrappers/security_credentials.cc b/lib/win32/wrappers/security_credentials.cc
new file mode 100644
index 0000000..732af3f
--- /dev/null
+++ b/lib/win32/wrappers/security_credentials.cc
@@ -0,0 +1,348 @@
+#include <node.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <v8.h>
+#include <node_buffer.h>
+#include <cstring>
+#include <cmath>
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+
+#include "security_credentials.h"
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0]))
+#endif
+
+static LPSTR DisplaySECError(DWORD ErrCode);
+
+Nan::Persistent<FunctionTemplate> SecurityCredentials::constructor_template;
+
+SecurityCredentials::SecurityCredentials() : Nan::ObjectWrap() {
+}
+
+SecurityCredentials::~SecurityCredentials() {
+}
+
+NAN_METHOD(SecurityCredentials::New) {
+  // Create security credentials instance
+  SecurityCredentials *security_credentials = new SecurityCredentials();
+  // Wrap it
+  security_credentials->Wrap(info.This());
+  // Return the object
+  info.GetReturnValue().Set(info.This());
+}
+
+// Call structs
+typedef struct SecurityCredentialCall {
+  char *package_str;
+  char *username_str;
+  char *password_str;
+  char *domain_str;
+  SecurityCredentials *credentials;
+} SecurityCredentialCall;
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// authGSSClientInit
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+static void _authSSPIAquire(Worker *worker) {
+  // Status of operation
+  SECURITY_STATUS status;
+
+  // Unpack data
+  SecurityCredentialCall *call = (SecurityCredentialCall *)worker->parameters;
+
+  // // Unwrap the credentials
+  // SecurityCredentials *security_credentials = (SecurityCredentials *)call->credentials;
+  SecurityCredentials *security_credentials = new SecurityCredentials();
+
+  // If we have domain string
+  if(call->domain_str != NULL) {
+    security_credentials->m_Identity.Domain = USTR(_tcsdup(call->domain_str));
+    security_credentials->m_Identity.DomainLength = (unsigned long)_tcslen(call->domain_str);
+  } else {
+    security_credentials->m_Identity.Domain = NULL;
+    security_credentials->m_Identity.DomainLength = 0;
+  }
+
+  // Set up the user
+  security_credentials->m_Identity.User = USTR(_tcsdup(call->username_str));
+  security_credentials->m_Identity.UserLength = (unsigned long)_tcslen(call->username_str);
+
+  // If we have a password string
+  if(call->password_str != NULL) {
+    // Set up the password
+    security_credentials->m_Identity.Password = USTR(_tcsdup(call->password_str));
+    security_credentials->m_Identity.PasswordLength = (unsigned long)_tcslen(call->password_str);
+  }
+
+  #ifdef _UNICODE
+    security_credentials->m_Identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+  #else
+    security_credentials->m_Identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
+  #endif
+
+  // Attempt to acquire credentials
+  status = _sspi_AcquireCredentialsHandle(
+    NULL,
+    call->package_str,
+    SECPKG_CRED_OUTBOUND,
+    NULL,
+    call->password_str != NULL ? &security_credentials->m_Identity : NULL,
+    NULL, NULL,
+    &security_credentials->m_Credentials,
+    &security_credentials->Expiration
+  );
+
+  // We have an error
+  if(status != SEC_E_OK) {
+    worker->error = TRUE;
+    worker->error_code = status;
+    worker->error_message = DisplaySECError(status);
+  } else {
+    worker->return_code = status;
+    worker->return_value = security_credentials;
+  }
+
+  // Free up parameter structure
+  if(call->package_str != NULL) free(call->package_str);
+  if(call->domain_str != NULL) free(call->domain_str);
+  if(call->password_str != NULL) free(call->password_str);
+  if(call->username_str != NULL) free(call->username_str);
+  free(call);
+}
+
+static Local<Value> _map_authSSPIAquire(Worker *worker) {
+  return Nan::Null();
+}
+
+NAN_METHOD(SecurityCredentials::Aquire) {
+  char *package_str = NULL, *username_str = NULL, *password_str = NULL, *domain_str = NULL;
+  // Unpack the variables
+  if(info.Length() != 2 && info.Length() != 3 && info.Length() != 4 && info.Length() != 5)
+    return Nan::ThrowError("Aquire must be called with either [package:string, username:string, [password:string, domain:string], callback:function]");
+
+  if(!info[0]->IsString())
+    return Nan::ThrowError("Aquire must be called with either [package:string, username:string, [password:string, domain:string], callback:function]");
+
+  if(!info[1]->IsString())
+    return Nan::ThrowError("Aquire must be called with either [package:string, username:string, [password:string, domain:string], callback:function]");
+
+  if(info.Length() == 3 && (!info[2]->IsString() && !info[2]->IsFunction()))
+    return Nan::ThrowError("Aquire must be called with either [package:string, username:string, [password:string, domain:string], callback:function]");
+
+  if(info.Length() == 4 && (!info[3]->IsString() && !info[3]->IsUndefined() && !info[3]->IsNull()) && !info[3]->IsFunction())
+    return Nan::ThrowError("Aquire must be called with either [package:string, username:string, [password:string, domain:string], callback:function]");
+
+  if(info.Length() == 5 && !info[4]->IsFunction())
+    return Nan::ThrowError("Aquire must be called with either [package:string, username:string, [password:string, domain:string], callback:function]");
+
+  Local<Function> callbackHandle;
+
+  // Figure out which parameter is the callback
+  if(info.Length() == 5) {
+    callbackHandle = Local<Function>::Cast(info[4]);
+  } else if(info.Length() == 4) {
+    callbackHandle = Local<Function>::Cast(info[3]);
+  } else if(info.Length() == 3) {
+    callbackHandle = Local<Function>::Cast(info[2]);
+  }
+
+  // Unpack the package
+  Local<String> package = info[0]->ToString();
+  package_str = (char *)calloc(package->Utf8Length() + 1, sizeof(char));
+  package->WriteUtf8(package_str);
+
+  // Unpack the user name
+  Local<String> username = info[1]->ToString();
+  username_str = (char *)calloc(username->Utf8Length() + 1, sizeof(char));
+  username->WriteUtf8(username_str);
+
+  // If we have a password
+  if(info.Length() == 3 || info.Length() == 4 || info.Length() == 5) {
+    Local<String> password = info[2]->ToString();
+    password_str = (char *)calloc(password->Utf8Length() + 1, sizeof(char));
+    password->WriteUtf8(password_str);
+  }
+
+  // If we have a domain
+  if((info.Length() == 4 || info.Length() == 5) && info[3]->IsString()) {
+    Local<String> domain = info[3]->ToString();
+    domain_str = (char *)calloc(domain->Utf8Length() + 1, sizeof(char));
+    domain->WriteUtf8(domain_str);
+  }
+
+  // Allocate call structure
+  SecurityCredentialCall *call = (SecurityCredentialCall *)calloc(1, sizeof(SecurityCredentialCall));
+  call->domain_str = domain_str;
+  call->package_str = package_str;
+  call->password_str = password_str;
+  call->username_str = username_str;
+
+  // Unpack the callback
+  Nan::Callback *callback = new Nan::Callback(callbackHandle);
+
+  // Let's allocate some space
+  Worker *worker = new Worker();
+  worker->error = false;
+  worker->request.data = worker;
+  worker->callback = callback;
+  worker->parameters = call;
+  worker->execute = _authSSPIAquire;
+  worker->mapper = _map_authSSPIAquire;
+
+  // Schedule the worker with lib_uv
+  uv_queue_work(uv_default_loop(), &worker->request, SecurityCredentials::Process, (uv_after_work_cb)SecurityCredentials::After);
+
+  // Return no value as it's callback based
+  info.GetReturnValue().Set(Nan::Undefined());
+}
+
+void SecurityCredentials::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
+  // Grab the scope of the call from Node
+  Nan::HandleScope scope;
+
+  // Define a new function template
+  Local<FunctionTemplate> t = Nan::New<FunctionTemplate>(New);
+  t->InstanceTemplate()->SetInternalFieldCount(1);
+  t->SetClassName(Nan::New<String>("SecurityCredentials").ToLocalChecked());
+
+  // Class methods
+  Nan::SetMethod(t, "aquire", Aquire);
+
+  // Set persistent
+  constructor_template.Reset(t);
+
+  // Set the symbol
+  target->ForceSet(Nan::New<String>("SecurityCredentials").ToLocalChecked(), t->GetFunction());
+
+  // Attempt to load the security.dll library
+  load_library();
+}
+
+static LPSTR DisplaySECError(DWORD ErrCode) {
+  LPSTR pszName = NULL; // WinError.h
+
+  switch(ErrCode) {
+    case SEC_E_BUFFER_TOO_SMALL:
+      pszName = "SEC_E_BUFFER_TOO_SMALL - The message buffer is too small. Used with the Digest SSP.";
+      break;
+
+    case SEC_E_CRYPTO_SYSTEM_INVALID:
+      pszName = "SEC_E_CRYPTO_SYSTEM_INVALID - The cipher chosen for the security context is not supported. Used with the Digest SSP.";
+      break;
+    case SEC_E_INCOMPLETE_MESSAGE:
+      pszName = "SEC_E_INCOMPLETE_MESSAGE - The data in the input buffer is incomplete. The application needs to read more data from the server and call DecryptMessage (General) again.";
+      break;
+
+    case SEC_E_INVALID_HANDLE:
+      pszName = "SEC_E_INVALID_HANDLE - A context handle that is not valid was specified in the phContext parameter. Used with the Digest and Schannel SSPs.";
+      break;
+
+    case SEC_E_INVALID_TOKEN:
+      pszName = "SEC_E_INVALID_TOKEN - The buffers are of the wrong type or no buffer of type SECBUFFER_DATA was found. Used with the Schannel SSP.";
+      break;
+
+    case SEC_E_MESSAGE_ALTERED:
+      pszName = "SEC_E_MESSAGE_ALTERED - The message has been altered. Used with the Digest and Schannel SSPs.";
+      break;
+
+    case SEC_E_OUT_OF_SEQUENCE:
+      pszName = "SEC_E_OUT_OF_SEQUENCE - The message was not received in the correct sequence.";
+      break;
+
+    case SEC_E_QOP_NOT_SUPPORTED:
+      pszName = "SEC_E_QOP_NOT_SUPPORTED - Neither confidentiality nor integrity are supported by the security context. Used with the Digest SSP.";
+      break;
+
+    case SEC_I_CONTEXT_EXPIRED:
+      pszName = "SEC_I_CONTEXT_EXPIRED - The message sender has finished using the connection and has initiated a shutdown.";
+      break;
+
+    case SEC_I_RENEGOTIATE:
+      pszName = "SEC_I_RENEGOTIATE - The remote party requires a new handshake sequence or the application has just initiated a shutdown.";
+      break;
+
+    case SEC_E_ENCRYPT_FAILURE:
+      pszName = "SEC_E_ENCRYPT_FAILURE - The specified data could not be encrypted.";
+      break;
+
+    case SEC_E_DECRYPT_FAILURE:
+      pszName = "SEC_E_DECRYPT_FAILURE - The specified data could not be decrypted.";
+      break;
+    case -1:
+      pszName = "Failed to load security.dll library";
+      break;
+
+  }
+
+  return pszName;
+}
+
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// UV Lib callbacks
+// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+void SecurityCredentials::Process(uv_work_t* work_req) {
+  // Grab the worker
+  Worker *worker = static_cast<Worker*>(work_req->data);
+  // Execute the worker code
+  worker->execute(worker);
+}
+
+void SecurityCredentials::After(uv_work_t* work_req) {
+  // Grab the scope of the call from Node
+  Nan::HandleScope scope;
+
+  // Get the worker reference
+  Worker *worker = static_cast<Worker*>(work_req->data);
+
+  // If we have an error
+  if(worker->error) {
+    Local<Value> err = v8::Exception::Error(Nan::New<String>(worker->error_message).ToLocalChecked());
+    Local<Object> obj = err->ToObject();
+    obj->Set(Nan::New<String>("code").ToLocalChecked(), Nan::New<Int32>(worker->error_code));
+    Local<Value> info[2] = { err, Nan::Null() };
+    // Execute the error
+    Nan::TryCatch try_catch;
+
+    // Call the callback
+    worker->callback->Call(ARRAY_SIZE(info), info);
+
+    // If we have an exception handle it as a fatalexception
+    if (try_catch.HasCaught()) {
+      Nan::FatalException(try_catch);
+    }
+  } else {
+    SecurityCredentials *return_value = (SecurityCredentials *)worker->return_value;
+    // Create a new instance
+    Local<Object> result = Nan::New(constructor_template)->GetFunction()->NewInstance();
+    // Unwrap the credentials
+    SecurityCredentials *security_credentials = Nan::ObjectWrap::Unwrap<SecurityCredentials>(result);
+    // Set the values
+    security_credentials->m_Identity = return_value->m_Identity;
+    security_credentials->m_Credentials = return_value->m_Credentials;
+    security_credentials->Expiration = return_value->Expiration;
+    // Set up the callback with a null first
+    Local<Value> info[2] = { Nan::Null(), result};
+    // Wrap the callback function call in a TryCatch so that we can call
+    // node's FatalException afterwards. This makes it possible to catch
+    // the exception from JavaScript land using the
+    // process.on('uncaughtException') event.
+    Nan::TryCatch try_catch;
+
+    // Call the callback
+    worker->callback->Call(ARRAY_SIZE(info), info);
+
+    // If we have an exception handle it as a fatalexception
+    if (try_catch.HasCaught()) {
+      Nan::FatalException(try_catch);
+    }
+  }
+
+  // Clean up the memory
+  delete worker->callback;
+  delete worker;
+}
+
diff --git a/lib/win32/wrappers/security_credentials.h b/lib/win32/wrappers/security_credentials.h
new file mode 100644
index 0000000..71751a0
--- /dev/null
+++ b/lib/win32/wrappers/security_credentials.h
@@ -0,0 +1,68 @@
+#ifndef SECURITY_CREDENTIALS_H
+#define SECURITY_CREDENTIALS_H
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <v8.h>
+
+#define SECURITY_WIN32 1
+
+#include <WinSock2.h>
+#include <windows.h>
+#include <sspi.h>
+#include <tchar.h>
+#include <nan.h>
+#include "../worker.h"
+#include <uv.h>
+
+extern "C" {
+  #include "../kerberos_sspi.h"
+}
+
+// SEC_WINNT_AUTH_IDENTITY makes it unusually hard
+// to compile for both Unicode and ansi, so I use this macro:
+#ifdef _UNICODE
+#define USTR(str) (str)
+#else
+#define USTR(str) ((unsigned char*)(str))
+#endif
+
+using namespace v8;
+using namespace node;
+
+class SecurityCredentials : public Nan::ObjectWrap {
+  public:
+    SecurityCredentials();
+    ~SecurityCredentials();
+
+    // Pointer to context object
+    SEC_WINNT_AUTH_IDENTITY m_Identity;
+    // credentials
+    CredHandle m_Credentials;
+    // Expiry time for ticket
+    TimeStamp Expiration;
+
+    // Has instance check
+    static inline bool HasInstance(Local<Value> val) {
+      if (!val->IsObject()) return false;
+      Local<Object> obj = val->ToObject();
+      return Nan::New(constructor_template)->HasInstance(obj);
+    };
+
+    // Functions available from V8
+    static void Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target);
+    static NAN_METHOD(Aquire);
+
+    // Constructor used for creating new Long objects from C++
+    static Nan::Persistent<FunctionTemplate> constructor_template;
+
+  private:
+    // Create a new instance
+    static NAN_METHOD(New);
+    // Handles the uv calls
+    static void Process(uv_work_t* work_req);
+    // Called after work is done
+    static void After(uv_work_t* work_req);
+};
+
+#endif
\ No newline at end of file
diff --git a/lib/win32/wrappers/security_credentials.js b/lib/win32/wrappers/security_credentials.js
new file mode 100644
index 0000000..4215c92
--- /dev/null
+++ b/lib/win32/wrappers/security_credentials.js
@@ -0,0 +1,22 @@
+var SecurityCredentialsNative = require('../../../build/Release/kerberos').SecurityCredentials;
+
+// Add simple kebros helper
+SecurityCredentialsNative.aquire_kerberos = function(username, password, domain, callback) {
+  if(typeof password == 'function') {
+    callback = password;
+    password = null;
+  } else if(typeof domain == 'function') {
+    callback = domain;
+    domain = null;
+  }
+
+  // We are going to use the async version
+  if(typeof callback == 'function') {
+    return SecurityCredentialsNative.aquire('Kerberos', username, password, domain, callback);
+  } else {
+    return SecurityCredentialsNative.aquireSync('Kerberos', username, password, domain);
+  }
+}
+
+// Export the modified class
+exports.SecurityCredentials = SecurityCredentialsNative;
\ No newline at end of file
diff --git a/lib/worker.cc b/lib/worker.cc
new file mode 100644
index 0000000..e7a472f
--- /dev/null
+++ b/lib/worker.cc
@@ -0,0 +1,7 @@
+#include "worker.h"
+
+Worker::Worker() {
+}
+
+Worker::~Worker() {  
+}
\ No newline at end of file
diff --git a/lib/worker.h b/lib/worker.h
new file mode 100644
index 0000000..1b0dced
--- /dev/null
+++ b/lib/worker.h
@@ -0,0 +1,38 @@
+#ifndef WORKER_H_
+#define WORKER_H_
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <nan.h>
+#include <v8.h>
+
+using namespace node;
+using namespace v8;
+
+class Worker {
+  public:
+    Worker();
+    virtual ~Worker();
+
+    // libuv's request struct.
+    uv_work_t request;
+    // Callback
+    Nan::Callback *callback;
+    // Parameters
+    void *parameters;
+    // Results
+    void *return_value;
+    // Did we raise an error
+    bool error;
+    // The error message
+    char *error_message;
+    // Error code if not message
+    int error_code;
+    // Any return code
+    int return_code;
+    // Method we are going to fire
+    void (*execute)(Worker *worker);
+    Local<Value> (*mapper)(Worker *worker);
+};
+
+#endif  // WORKER_H_
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..b3d74b5
--- /dev/null
+++ b/package.json
@@ -0,0 +1,29 @@
+{
+  "name": "kerberos",
+  "version": "0.0.14",
+  "description": "Kerberos library for Node.js",
+  "main": "index.js",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/christkv/kerberos.git"
+  },
+  "keywords": [
+    "kerberos",
+    "security",
+    "authentication"
+  ],
+  "dependencies": {
+    "nan": "~2.0"
+  },
+  "devDependencies": {
+    "nodeunit": "latest"
+  },
+  "scripts": {
+    "install" : "(node-gyp rebuild) || (exit 0)",
+    "test" : "nodeunit ./test"
+  },
+  "author": "Christian Amor Kvalheim",
+  "license": "Apache 2.0",
+  "readmeFilename": "README.md",
+  "gitHead": "bb01d4fe322e022999aca19da564e7d9db59a8ed"
+}
diff --git a/test/kerberos_tests.js b/test/kerberos_tests.js
new file mode 100644
index 0000000..a06c5fd
--- /dev/null
+++ b/test/kerberos_tests.js
@@ -0,0 +1,34 @@
+exports.setUp = function(callback) {
+  callback();
+}
+
+exports.tearDown = function(callback) {
+  callback();
+}
+
+exports['Simple initialize of Kerberos object'] = function(test) {
+  var Kerberos = require('../lib/kerberos.js').Kerberos;
+  var kerberos = new Kerberos();
+  // console.dir(kerberos)
+
+  // Initiate kerberos client
+  kerberos.authGSSClientInit('mongodb at kdc.10gen.me', Kerberos.GSS_C_MUTUAL_FLAG, function(err, context) {
+    console.log("===================================== authGSSClientInit")
+    test.equal(null, err);
+    test.ok(context != null && typeof context == 'object');
+    // console.log("===================================== authGSSClientInit")
+    console.dir(err)
+    console.dir(context)
+    // console.dir(typeof result)
+
+    // Perform the first step
+    kerberos.authGSSClientStep(context, function(err, result) {
+      console.log("===================================== authGSSClientStep")
+      console.dir(err)
+      console.dir(result)
+      console.dir(context)
+
+      test.done();
+    });
+  });
+}
\ No newline at end of file
diff --git a/test/kerberos_win32_test.js b/test/kerberos_win32_test.js
new file mode 100644
index 0000000..c8509db
--- /dev/null
+++ b/test/kerberos_win32_test.js
@@ -0,0 +1,15 @@
+if (/^win/.test(process.platform)) {
+
+exports['Simple initialize of Kerberos win32 object'] = function(test) {
+  var KerberosNative = require('../build/Release/kerberos').Kerberos;
+  // console.dir(KerberosNative)
+  var kerberos = new KerberosNative();
+  console.log("=========================================== 0")
+  console.dir(kerberos.acquireAlternateCredentials("dev1 at 10GEN.ME", "a"));
+  console.log("=========================================== 1")
+  console.dir(kerberos.prepareOutboundPackage("mongodb/kdc.10gen.com"));
+  console.log("=========================================== 2")
+  test.done();
+}
+
+}
\ No newline at end of file
diff --git a/test/win32/security_buffer_descriptor_tests.js b/test/win32/security_buffer_descriptor_tests.js
new file mode 100644
index 0000000..3531b6b
--- /dev/null
+++ b/test/win32/security_buffer_descriptor_tests.js
@@ -0,0 +1,41 @@
+exports.setUp = function(callback) {
+  callback();
+}
+
+exports.tearDown = function(callback) {
+  callback();
+}
+
+exports['Initialize a security Buffer Descriptor'] = function(test) {
+  var SecurityBufferDescriptor = require('../../lib/sspi.js').SecurityBufferDescriptor
+    SecurityBuffer = require('../../lib/sspi.js').SecurityBuffer;
+
+  // Create descriptor with single Buffer
+  var securityDescriptor = new SecurityBufferDescriptor(100);
+  try {
+    // Fail to work due to no valid Security Buffer
+    securityDescriptor = new SecurityBufferDescriptor(["hello"]);
+    test.ok(false);
+  } catch(err){}
+
+  // Should Correctly construct SecurityBuffer
+  var buffer = new SecurityBuffer(SecurityBuffer.DATA, 100);
+  securityDescriptor = new SecurityBufferDescriptor([buffer]);
+  // Should correctly return a buffer
+  var result = securityDescriptor.toBuffer();
+  test.equal(100, result.length);
+
+  // Should Correctly construct SecurityBuffer
+  var buffer = new SecurityBuffer(SecurityBuffer.DATA, new Buffer("hello world"));
+  securityDescriptor = new SecurityBufferDescriptor([buffer]);
+  var result = securityDescriptor.toBuffer();
+  test.equal("hello world", result.toString());
+
+  // Test passing in more than one Buffer
+  var buffer = new SecurityBuffer(SecurityBuffer.DATA, new Buffer("hello world"));
+  var buffer2 = new SecurityBuffer(SecurityBuffer.STREAM, new Buffer("adam and eve"));
+  securityDescriptor = new SecurityBufferDescriptor([buffer, buffer2]);
+  var result = securityDescriptor.toBuffer();
+  test.equal("hello worldadam and eve", result.toString());
+  test.done();
+}
\ No newline at end of file
diff --git a/test/win32/security_buffer_tests.js b/test/win32/security_buffer_tests.js
new file mode 100644
index 0000000..b52b959
--- /dev/null
+++ b/test/win32/security_buffer_tests.js
@@ -0,0 +1,22 @@
+exports.setUp = function(callback) {
+  callback();
+}
+
+exports.tearDown = function(callback) {
+  callback();
+}
+
+exports['Initialize a security Buffer'] = function(test) {
+  var SecurityBuffer = require('../../lib/sspi.js').SecurityBuffer;
+  // Create empty buffer
+  var securityBuffer = new SecurityBuffer(SecurityBuffer.DATA, 100);
+  var buffer = securityBuffer.toBuffer();
+  test.equal(100, buffer.length);
+
+  // Access data passed in
+  var allocated_buffer = new Buffer(256);
+  securityBuffer = new SecurityBuffer(SecurityBuffer.DATA, allocated_buffer);
+  buffer = securityBuffer.toBuffer();
+  test.deepEqual(allocated_buffer, buffer);
+  test.done();
+}
\ No newline at end of file
diff --git a/test/win32/security_credentials_tests.js b/test/win32/security_credentials_tests.js
new file mode 100644
index 0000000..7758180
--- /dev/null
+++ b/test/win32/security_credentials_tests.js
@@ -0,0 +1,55 @@
+exports.setUp = function(callback) {
+  callback();
+}
+
+exports.tearDown = function(callback) {
+  callback();
+}
+
+exports['Initialize a set of security credentials'] = function(test) {
+  var SecurityCredentials = require('../../lib/sspi.js').SecurityCredentials;
+
+  // Aquire some credentials
+  try {
+    var credentials = SecurityCredentials.aquire('Kerberos', 'dev1 at 10GEN.ME', 'a');    
+  } catch(err) {    
+    console.dir(err)
+    test.ok(false);
+  }
+
+
+
+  // console.dir(SecurityCredentials);
+
+  // var SecurityBufferDescriptor = require('../../lib/sspi.js').SecurityBufferDescriptor
+  //   SecurityBuffer = require('../../lib/sspi.js').SecurityBuffer;
+
+  // // Create descriptor with single Buffer
+  // var securityDescriptor = new SecurityBufferDescriptor(100);
+  // try {
+  //   // Fail to work due to no valid Security Buffer
+  //   securityDescriptor = new SecurityBufferDescriptor(["hello"]);
+  //   test.ok(false);
+  // } catch(err){}
+
+  // // Should Correctly construct SecurityBuffer
+  // var buffer = new SecurityBuffer(SecurityBuffer.DATA, 100);
+  // securityDescriptor = new SecurityBufferDescriptor([buffer]);
+  // // Should correctly return a buffer
+  // var result = securityDescriptor.toBuffer();
+  // test.equal(100, result.length);
+
+  // // Should Correctly construct SecurityBuffer
+  // var buffer = new SecurityBuffer(SecurityBuffer.DATA, new Buffer("hello world"));
+  // securityDescriptor = new SecurityBufferDescriptor([buffer]);
+  // var result = securityDescriptor.toBuffer();
+  // test.equal("hello world", result.toString());
+
+  // // Test passing in more than one Buffer
+  // var buffer = new SecurityBuffer(SecurityBuffer.DATA, new Buffer("hello world"));
+  // var buffer2 = new SecurityBuffer(SecurityBuffer.STREAM, new Buffer("adam and eve"));
+  // securityDescriptor = new SecurityBufferDescriptor([buffer, buffer2]);
+  // var result = securityDescriptor.toBuffer();
+  // test.equal("hello worldadam and eve", result.toString());
+  test.done();
+}
\ No newline at end of file

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-kerberos.git



More information about the Pkg-javascript-commits mailing list