【问题标题】:How can you use Auth0 with existing ASP.NET Core Identity database?如何将 Auth0 与现有的 ASP.NET Core Identity 数据库一起使用?
【发布时间】:2018-10-29 21:30:35
【问题描述】:

我正在为应用程序进行Auth0 集成。我有一个现有的应用程序,它使用 ASP.NET Core Identity 和一个 SQL 数据库,目前带有常用表(AspNetUsers、AspNetRoles 等)。

问题在于 Auth0 当前具有用于与 ASP.NET Membership Provider(MVC3 Universal Providers 和 MVC4 Simple Membership)数据库一起使用的自定义数据库模板,但没有 ASP.NET Core Identity。由于我不确切知道如何编写与我的 ASP.NET Core 身份数据库兼容所需的密码哈希,这是一个大问题。

有没有人提供使用现有 ASP.NET Core Identity 数据库的 Auth0 自定义数据库脚本示例?或者,至少是我自己编码的散列算法?

有关详细信息,ASP.NET Core Identity 数据库有一个 AspNetUsers 表,其中包含此集成感兴趣的以下列:

  • Id(PK,nvarchar,不为空)
  • Email (nvarchar, null)
  • PasswordHash (nvarchar, null)
  • SecurityStamp (nvarchar, null)
  • UserName(nvarchar,不为空)

为了清楚起见,我问的是如何在用 JavaScript 编写的 Auth0 配置中设置自定义数据库脚本;不是 ASP.NET Web 应用程序代码。根据文档进行设置,Web 应用程序非常简单。只是 Auth0 内置自定义数据库模板不包含用于 ASP.NET Core Identity 数据库架构和密码哈希的示例。

任何帮助将不胜感激!

【问题讨论】:

    标签: asp.net azure authentication asp.net-identity auth0


    【解决方案1】:

    您需要弄清楚使用的哈希算法并修改模板中的脚本,同时使用 Auth0 文档,以使其正确。您可以在aspnet-identity-pw 项目中找到该算法的代码。

    下面是 Auth0 的登录数据库操作脚本示例,它与存储在 Azure SQL 数据库中的 ASP.NET Core Identity 2.0 数据库一起使用:

    function login (username, password, callback) {
    
      var Connection = require('tedious@1.11.0').Connection;
      var Request = require('tedious@1.11.0').Request;
      var TYPES = require('tedious@1.11.0').TYPES;
    
      var connection = new Connection({
        userName:  configuration.db_username + '@' + configuration.db_server,
        password:  configuration.db_password,
        server:    configuration.db_server, //'dbserver.database.windows.net',
        options: {
          database:  configuration.db_database,
          encrypt: true
        }
      });
    
      connection.on('debug', function(text) {
        // if you have connection issues, uncomment this to get more detailed info
        //console.log(text);
      }).on('errorMessage', function(text) {
        // this will show any errors when connecting to the SQL database or with the SQL statements
        //console.log(JSON.stringify(text));
      });
    
      connection.on('connect', function (err) {
        if (err) {
          console.log('error: ' + JSON.stringify(err));
          return callback(err);
        }
    
        getMembershipUser(username, function(err, user) {
          if (err) {
            return callback(err); // this will return a 500
          }
          if (!user.profile) {
            return callback(); // this will return a 401
          }
    
          validatePassword(password, user.password.hash, function(err, isValid) {
            if (!isValid) {
              return callback(); // unauthorized
            }
    
            callback(null, user.profile);
          });
    
        });
      });
    
    
      // Membership Provider implementation used with ASP.NET Core Identity database
    
      /**
       * getMembershipUser
       *
       * This function gets a username or email and returns a user info, password hashes and salt
       *
       * @usernameOrEamil   {[string]}    the username or email, the method will do a query
       *                                  on both with an OR
       * @callback          {[Function]}  first argument will be the Error if any, and second
       *                                  argument will be a user object
       */
      function getMembershipUser(usernameOrEmail, callback) {
        var user = {};
        var query =
          'SELECT Id, UserName, Email, PasswordHash, SecurityStamp from AspNetUsers ' +
          'WHERE UserName = @UserName';
    
        var getMembershipQuery = new Request(query);
    
        getMembershipQuery.addParameter('UserName', TYPES.VarChar, usernameOrEmail);
    
        getMembershipQuery.on('row', function (fields) {
          user.profile = {};
          user.password = {};
          for(var f in fields) {
            var item = fields[f];
            if (item.metadata.colName === 'Id') {
              user.profile.user_id = item.value;
            } else if (item.metadata.colName === 'UserName') {
              user.profile.nickname = item.value;
            } else if (item.metadata.colName ==='Email') {
              user.profile.email = item.value;
            } else if (item.metadata.colName ==='PasswordHash') {
              user.password.hash = item.value;
            }
          }
    
          //console.log('User: ' + JSON.stringify(user));
          callback(null, user);
        });
    
        connection.execSql(getMembershipQuery);
      }
    
      /**
       * validatePassword
       *
       * This function gets the password entered by the user, and the original password
       * hash and salt from database and performs an HMAC SHA256 hash.
       *
       * @password      {[string]}      the password entered by the user
       * @originalHash  {[string]}      the original password hashed from the database
       *                                (including the salt).
       * @return        {[bool]}        true if password validates
       */
      function validatePassword(password, originalHash, callback) {
        aspnet_identity_pw.validatePassword(password, originalHash, function(result, isValid) {
          console.log('Is Password Valid: ' + isValid);
    
          callback(null, isValid);
        });
      }
    
      var aspnet_identity_pw = {
        validatePassword: function(password, hashedPassword, callback) {
          // Original Source:
          //   https://github.com/Syncbak-Git/aspnet-identity-pw/blob/master/lib/aspnet-identity-pw.js
          //   https://www.npmjs.com/package/aspnet-identity-pw
          //   There were some slight modifications to make it run well in Auth0
    
          var done = false;
          var error = null;
          var result = null;
    
          if(!hashedPassword) {
    
              if(callback) {
                  callback(null, false);
              }
    
              return false;
          }
    
          if(!password) {
    
              error = new Error("Password is required.");
    
              if(callback) {
                  callback(error);
                  return;
              }
    
              throw error;
          }
    
          var src = new Buffer(hashedPassword, 'base64');
    
          if(src.length !== 49 || src[0] !== 0) {
              return false;
          }
    
          var salt = new Buffer(16);
          src.copy(salt, 0, 1, 17);
    
          var bytes = new Buffer(32);
          src.copy(bytes, 0, 17, 49);
    
          var hashed = crypto.pbkdf2Sync(password, salt, 1000, 32, 'sha1');
          result = true;
    
              for(var i = 0; i < 32; i++) {
                  if(bytes[i] !== hashed[i]) {
                      result = false;
                      break;
                  }
              }
    
              done = true;
    
              if(callback) {
                  callback(null, result);
              }
    
          if(!callback) {
            throw 'callback required!';
          }
    
          }
      };
    
      }
    

    这似乎需要很长时间才能完全弄清楚。尤其是要对密码哈希算法进行编码,直到偶然发现列出的 js 项目及其代码。

    希望这对其他人有所帮助!

    【讨论】:

    • 太棒了!顺便说一句,你有没有遇到过Request to Webtask got ESOCKETTIMEDOUT?似乎无法建立连接。
    猜你喜欢
    • 2019-11-02
    • 2019-08-30
    • 2015-08-01
    • 2021-07-25
    • 1970-01-01
    • 1970-01-01
    • 2022-01-05
    • 2013-06-27
    • 2017-07-12
    相关资源
    最近更新 更多