exchange credential for profiles
This commit is contained in:
		
							parent
							
								
									81b213ec4b
								
							
						
					
					
						commit
						abaa59dab0
					
				
							
								
								
									
										75
									
								
								accounts.js
									
									
									
									
									
								
							
							
						
						
									
										75
									
								
								accounts.js
									
									
									
									
									
								
							@ -61,19 +61,24 @@ function validateOtp(codeStore, codeId, token) {
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getOrCreate(store, username) {
 | 
					function getOrCreate(store, iss, username) {
 | 
				
			||||||
  // account => profile
 | 
					  // account => profile
 | 
				
			||||||
  return store.IssuerOauth3OrgAccounts.get(username).then(function (profile) {
 | 
					  return store.IssuerOauth3OrgProfiles.find({ username: username }).then(function (profile) {
 | 
				
			||||||
 | 
					    profile = profile && profile[0];
 | 
				
			||||||
    if (profile) {
 | 
					    if (profile) {
 | 
				
			||||||
      return profile;
 | 
					      return profile;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // TODO profile should be ecdsa256 pub/privkeypair
 | 
					    // TODO profile should be ecdsa256 pub/privkeypair
 | 
				
			||||||
 | 
					    var sub = makeB64UrlSafe(crypto.randomBytes(32).toString('base64'));
 | 
				
			||||||
 | 
					    var iss = req.experienceId;
 | 
				
			||||||
    profile = {
 | 
					    profile = {
 | 
				
			||||||
      username:  username,
 | 
					      id: sub + (iss && ('@' + iss) || '')
 | 
				
			||||||
      accountId: makeB64UrlSafe(crypto.randomBytes(32).toString('base64')),
 | 
					    , username:  username
 | 
				
			||||||
 | 
					    , sub: sub
 | 
				
			||||||
 | 
					    , iss: iss
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    return store.IssuerOauth3OrgAccounts.create(username, profile).then(function () {
 | 
					    return store.IssuerOauth3OrgProfiles.create(profile.id, profile).then(function () {
 | 
				
			||||||
      // TODO: put sort sort of email notification to the server managers?
 | 
					      // TODO: put sort sort of email notification to the server managers?
 | 
				
			||||||
      return profile;
 | 
					      return profile;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@ -176,7 +181,7 @@ function create(deps, app) {
 | 
				
			|||||||
  restful.sendOtp = function (req, res) {
 | 
					  restful.sendOtp = function (req, res) {
 | 
				
			||||||
    var params = req.body;
 | 
					    var params = req.body;
 | 
				
			||||||
    var promise = req.getSiteStore().then(function (store) {
 | 
					    var promise = req.getSiteStore().then(function (store) {
 | 
				
			||||||
      store.IssuerOauth3OrgProfiles = store.IssuerOauth3OrgProfiles || store.IssuerOauth3OrgAccounts;
 | 
					      //store.IssuerOauth3OrgProfiles = store.IssuerOauth3OrgProfiles || store.IssuerOauth3OrgAccounts;
 | 
				
			||||||
      return createOtp(store, params).then(function (code) {
 | 
					      return createOtp(store, params).then(function (code) {
 | 
				
			||||||
        var emailParams = {
 | 
					        var emailParams = {
 | 
				
			||||||
          to:      params.username,
 | 
					          to:      params.username,
 | 
				
			||||||
@ -241,7 +246,7 @@ function create(deps, app) {
 | 
				
			|||||||
          return restful.createToken._helper(req, res, tokenInfo);
 | 
					          return restful.createToken._helper(req, res, tokenInfo);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function createProfile(credential, profile) {
 | 
					        function createProfile(credential, meta) {
 | 
				
			||||||
          var bs58 = require('bs58');
 | 
					          var bs58 = require('bs58');
 | 
				
			||||||
          var EC = require('elliptic').ec;
 | 
					          var EC = require('elliptic').ec;
 | 
				
			||||||
          var ec = new EC('secp256k1');
 | 
					          var ec = new EC('secp256k1');
 | 
				
			||||||
@ -251,28 +256,25 @@ function create(deps, app) {
 | 
				
			|||||||
          //var ec = new EC('curve25519');
 | 
					          //var ec = new EC('curve25519');
 | 
				
			||||||
          var pub = bs58.encode(Buffer.from(key.derive(key.getPublic()).toString('hex'), 'hex'));
 | 
					          var pub = bs58.encode(Buffer.from(key.derive(key.getPublic()).toString('hex'), 'hex'));
 | 
				
			||||||
          var priv = bs58.encode(Buffer.from(key.priv.toString('hex'), 'hex'));
 | 
					          var priv = bs58.encode(Buffer.from(key.priv.toString('hex'), 'hex'));
 | 
				
			||||||
          var parts = [];
 | 
					          var iss = req.experienceId;
 | 
				
			||||||
 | 
					          var id = pub + '@' + iss;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (!credential.sub) {
 | 
					          if (!credential.sub) {
 | 
				
			||||||
            return deps.Promise.reject(new Error("missing 'sub' from credential"));
 | 
					            return deps.Promise.reject(new Error("missing 'sub' from credential"));
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if (credential.sub !== profile.sub || credential.iss !== profile.iss) {
 | 
					          if (credential.sub !== meta.sub || credential.iss !== meta.iss) {
 | 
				
			||||||
            return deps.Promise.reject(new Error("credential 'sub' and 'iss' do not match information in profile"));
 | 
					            return deps.Promise.reject(new Error("credential 'sub' and 'iss' do not match information in request body"));
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          parts.push(pub);
 | 
					 | 
				
			||||||
          if (req.experienceId) {
 | 
					 | 
				
			||||||
            parts.push(req.experienceId);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          var username = parts.join('@');
 | 
					 | 
				
			||||||
          var profile = {
 | 
					          var profile = {
 | 
				
			||||||
            accountId: pub // profile.sub
 | 
					            id: id
 | 
				
			||||||
 | 
					          //  accountId: pub // profile.sub
 | 
				
			||||||
          , sub: pub // profile.sub
 | 
					          , sub: pub // profile.sub
 | 
				
			||||||
          , iss: req.experienceId
 | 
					          , iss: req.experienceId
 | 
				
			||||||
          , prv: priv
 | 
					          , prv: priv
 | 
				
			||||||
          , typ: 'oauth3'
 | 
					          , typ: 'profile'
 | 
				
			||||||
 | 
					          , username: id
 | 
				
			||||||
          };
 | 
					          };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          function getId(token) {
 | 
					          function getId(token) {
 | 
				
			||||||
@ -280,17 +282,15 @@ function create(deps, app) {
 | 
				
			|||||||
            if (token.iss) {
 | 
					            if (token.iss) {
 | 
				
			||||||
              id += '@' + token.iss;
 | 
					              id += '@' + token.iss;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return id || token.accountId;
 | 
					            return id || token.id || token.accountId;
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          console.log('[debug] username, credential, profile:');
 | 
					          console.log('[debug] id, credential, profile:');
 | 
				
			||||||
          console.log(username);
 | 
					          console.log(id);
 | 
				
			||||||
          console.log(credential);
 | 
					          console.log(credential);
 | 
				
			||||||
          console.log(profile);
 | 
					          console.log(profile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          return deps.Promise.reject(new Error("blah blah grrr grrr"));
 | 
					          return store.IssuerOauth3OrgProfiles.create(profile.id/*username*/, profile).then(function () {
 | 
				
			||||||
 | 
					 | 
				
			||||||
          return store.IssuerOauth3OrgAccounts.create(username, profile).then(function () {
 | 
					 | 
				
			||||||
            var id = crypto.randomBytes(16).toString('hex');
 | 
					            var id = crypto.randomBytes(16).toString('hex');
 | 
				
			||||||
            return store.IssuerOauth3OrgCredentialsProfiles.create(id, {
 | 
					            return store.IssuerOauth3OrgCredentialsProfiles.create(id, {
 | 
				
			||||||
              credentialId: getId(credential)
 | 
					              credentialId: getId(credential)
 | 
				
			||||||
@ -308,12 +308,13 @@ function create(deps, app) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function getProfile() {
 | 
					        function getProfile() {
 | 
				
			||||||
          var query = { username: 'IN ' + joins.map(function (el) { return el.profileId }).join(',') };
 | 
					          var query = { id: 'IN ' + joins.map(function (el) { return el.profileId }).join(',') };
 | 
				
			||||||
 | 
					          //var query = { username: 'IN ' + joins.map(function (el) { return el.profileId }).join(',') };
 | 
				
			||||||
          //var query = { accountId: 'IN ' + joins.map(function (el) { return el.profileId }).join(',') };
 | 
					          //var query = { accountId: 'IN ' + joins.map(function (el) { return el.profileId }).join(',') };
 | 
				
			||||||
          //var query = { accountId: joins.map(function (el) { return el.profileId })[0] };
 | 
					          //var query = { accountId: joins.map(function (el) { return el.profileId })[0] };
 | 
				
			||||||
          console.log('[DEBUG] query profiles:');
 | 
					          console.log('[DEBUG] query profiles:');
 | 
				
			||||||
          console.log(query);
 | 
					          console.log(query);
 | 
				
			||||||
          return req.Models.IssuerOauth3OrgAccounts.find(query).then(function (profiles) {
 | 
					          return req.Models.IssuerOauth3OrgProfiles.find(query).then(function (profiles) {
 | 
				
			||||||
            console.log('[DEBUG] Profiles:');
 | 
					            console.log('[DEBUG] Profiles:');
 | 
				
			||||||
            console.log(profiles);
 | 
					            console.log(profiles);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -363,7 +364,7 @@ function create(deps, app) {
 | 
				
			|||||||
    var store;
 | 
					    var store;
 | 
				
			||||||
    var promise = req.getSiteStore().then(function (_store) {
 | 
					    var promise = req.getSiteStore().then(function (_store) {
 | 
				
			||||||
      store = _store;
 | 
					      store = _store;
 | 
				
			||||||
      store.IssuerOauth3OrgProfiles = store.IssuerOauth3OrgProfiles || store.IssuerOauth3OrgAccounts;
 | 
					      //store.IssuerOauth3OrgProfiles = store.IssuerOauth3OrgProfiles || store.IssuerOauth3OrgAccounts;
 | 
				
			||||||
      if (!req.body || !req.body.grant_type) {
 | 
					      if (!req.body || !req.body.grant_type) {
 | 
				
			||||||
        throw new OpErr("missing 'grant_type' from the body");
 | 
					        throw new OpErr("missing 'grant_type' from the body");
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@ -486,19 +487,20 @@ function create(deps, app) {
 | 
				
			|||||||
    var codeId = crypto.createHash('sha256').update(params.username_type+':'+params.username).digest('base64');
 | 
					    var codeId = crypto.createHash('sha256').update(params.username_type+':'+params.username).digest('base64');
 | 
				
			||||||
    codeId = makeB64UrlSafe(codeId);
 | 
					    codeId = makeB64UrlSafe(codeId);
 | 
				
			||||||
    return req.getSiteStore().then(function (store) {
 | 
					    return req.getSiteStore().then(function (store) {
 | 
				
			||||||
      store.IssuerOauth3OrgProfiles = store.IssuerOauth3OrgProfiles || store.IssuerOauth3OrgAccounts;
 | 
					      //store.IssuerOauth3OrgProfiles = store.IssuerOauth3OrgProfiles || store.IssuerOauth3OrgAccounts;
 | 
				
			||||||
      return validateOtp(store.IssuerOauth3OrgCodes, codeId, params.password)
 | 
					      return validateOtp(store.IssuerOauth3OrgCodes, codeId, params.password)
 | 
				
			||||||
      .then(function () {
 | 
					      .then(function () {
 | 
				
			||||||
        return getOrCreate(store, params.username);
 | 
					        return getOrCreate(store, req.experienceId, params.username);
 | 
				
			||||||
      }).then(function (account) {
 | 
					      }).then(function (account) {
 | 
				
			||||||
        var contactClaimId = crypto.createHash('sha256').update(account.accountId+':'+params.username_type+':'+params.username).digest('base64');
 | 
					        var contactClaimId = crypto.createHash('sha256').update(account.sub+':'+params.username_type+':'+params.username).digest('base64');
 | 
				
			||||||
 | 
					        //var contactClaimId = crypto.createHash('sha256').update(account.accountId+':'+params.username_type+':'+params.username).digest('base64');
 | 
				
			||||||
        return req.Models.IssuerOauth3OrgContactNodes.get(contactClaimId).then(function (contactClaim) {
 | 
					        return req.Models.IssuerOauth3OrgContactNodes.get(contactClaimId).then(function (contactClaim) {
 | 
				
			||||||
          var now = Date.now();
 | 
					          var now = Date.now();
 | 
				
			||||||
          if (!contactClaim) { contactClaim = { id: contactClaimId }; }
 | 
					          if (!contactClaim) { contactClaim = { id: contactClaimId }; }
 | 
				
			||||||
          if (!contactClaim.verifiedAt) { contactClaim.verifiedAt = now; }
 | 
					          if (!contactClaim.verifiedAt) { contactClaim.verifiedAt = now; }
 | 
				
			||||||
          contactClaim.lastVerifiedAt = now;
 | 
					          contactClaim.lastVerifiedAt = now;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          console.log('contactClaim');
 | 
					          console.log('[DEBUG] contactClaim');
 | 
				
			||||||
          console.log(contactClaim);
 | 
					          console.log(contactClaim);
 | 
				
			||||||
          return req.Models.IssuerOauth3OrgContactNodes.upsert(contactClaim).then(function () {
 | 
					          return req.Models.IssuerOauth3OrgContactNodes.upsert(contactClaim).then(function () {
 | 
				
			||||||
            return {
 | 
					            return {
 | 
				
			||||||
@ -540,7 +542,7 @@ function create(deps, app) {
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  restful.getProfile = function (req, res) {
 | 
					  restful.getProfile = function (req, res) {
 | 
				
			||||||
    var promise = req.Models.IssuerOauth3OrgAccounts.get(req.oauth3.accountIdx).then(function (result) {
 | 
					    var promise = req.Models.IssuerOauth3OrgProfiles.get(req.oauth3._IDX_ || req.oauth3.accountIdx).then(function (result) {
 | 
				
			||||||
      if (!result) { result = { id: undefined }; }
 | 
					      if (!result) { result = { id: undefined }; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      result.id = undefined;
 | 
					      result.id = undefined;
 | 
				
			||||||
@ -551,11 +553,11 @@ function create(deps, app) {
 | 
				
			|||||||
    app.handlePromise(req, res, promise, '[issuer@oauth3.org] get profile');
 | 
					    app.handlePromise(req, res, promise, '[issuer@oauth3.org] get profile');
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
  restful.setProfile = function (req, res) {
 | 
					  restful.setProfile = function (req, res) {
 | 
				
			||||||
    console.log('req.oauth3');
 | 
					    console.log('[setProfile] req.oauth3:');
 | 
				
			||||||
    console.log(req.oauth3);
 | 
					    console.log(req.oauth3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var body = req.body;
 | 
					    var body = req.body;
 | 
				
			||||||
    var promise = req.Models.IssuerOauth3OrgAccounts.find({ accountId: req.oauth3.ppid }).then(function (results) {
 | 
					    var promise = req.Models.IssuerOauth3OrgProfiles.find({ sub: req.oauth3.ppid, iss: req.experienceId }).then(function (results) {
 | 
				
			||||||
      var result = results[0];
 | 
					      var result = results[0];
 | 
				
			||||||
      var changed = false;
 | 
					      var changed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -575,7 +577,7 @@ function create(deps, app) {
 | 
				
			|||||||
      });
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (changed) {
 | 
					      if (changed) {
 | 
				
			||||||
        return req.Models.IssuerOauth3OrgAccounts.upsert(result).then(function () { console.log('update updated'); return result; });
 | 
					        return req.Models.IssuerOauth3OrgProfiles.upsert(result).then(function () { console.log('update updated'); return result; });
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return result;
 | 
					      return result;
 | 
				
			||||||
@ -626,7 +628,8 @@ function create(deps, app) {
 | 
				
			|||||||
        throw new OpErr("code didn't have contact node and type information");
 | 
					        throw new OpErr("code didn't have contact node and type information");
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      var contactClaimId = crypto.createHash('sha256').update(req.oauth3.accountIdx+':'+code.node.type+':'+code.node.node).digest('base64');
 | 
					      // TODO this token may represent a 3rd-party credential or 1st-party profile. What should the ID be?
 | 
				
			||||||
 | 
					      var contactClaimId = crypto.createHash('sha256').update((req.oauth3._IDX_ || req.oauth3.accountIdx)+':'+code.node.type+':'+code.node.node).digest('base64');
 | 
				
			||||||
      return req.Models.IssuerOauth3OrgContactNodes.get(contactClaimId).then(function (contactClaim) {
 | 
					      return req.Models.IssuerOauth3OrgContactNodes.get(contactClaimId).then(function (contactClaim) {
 | 
				
			||||||
        var now = Date.now();
 | 
					        var now = Date.now();
 | 
				
			||||||
        if (!contactClaim) { contactClaim = { id: contactClaimId }; }
 | 
					        if (!contactClaim) { contactClaim = { id: contactClaimId }; }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										11
									
								
								models.js
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								models.js
									
									
									
									
									
								
							@ -18,7 +18,7 @@ module.exports = [
 | 
				
			|||||||
    tablename: apiname + '_credentials',
 | 
					    tablename: apiname + '_credentials',
 | 
				
			||||||
    idname: 'id',
 | 
					    idname: 'id',
 | 
				
			||||||
    // credentialId = ppid@iss
 | 
					    // credentialId = ppid@iss
 | 
				
			||||||
    indices: baseFields.concat([ 'credentialId', 'sub', 'iss', 'typ' ]), // comment, recoveryCredential
 | 
					    indices: baseFields.concat([ 'username', 'sub', 'iss', 'typ', 'salt', 'shadow' ]), // comment, recoveryCredential
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    tablename: apiname + '_credentials_profiles',
 | 
					    tablename: apiname + '_credentials_profiles',
 | 
				
			||||||
@ -26,15 +26,16 @@ module.exports = [
 | 
				
			|||||||
    // credentialId = ppid@iss
 | 
					    // credentialId = ppid@iss
 | 
				
			||||||
    indices: baseFields.concat([ 'credentialId', 'profileId' ]),
 | 
					    indices: baseFields.concat([ 'credentialId', 'profileId' ]),
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  { // TODO rename to profiles
 | 
					  {
 | 
				
			||||||
    tablename: apiname + '_accounts',
 | 
					    tablename: apiname + '_profiles',
 | 
				
			||||||
    idname: 'username',
 | 
					    idname: 'id',
 | 
				
			||||||
    // make sub an ecdsa256 key
 | 
					    // make sub an ecdsa256 key
 | 
				
			||||||
    indices: baseFields.concat([ 'accountId', 'sub', 'iss', 'typ', 'privateKey' ]), // comment, recoveryNode
 | 
					    indices: baseFields.concat([ 'username', 'sub', 'iss', 'typ', 'privateKey' ]), // comment, recoveryNodes
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    tablename: apiname + '_contact_nodes',
 | 
					    tablename: apiname + '_contact_nodes',
 | 
				
			||||||
    idname: 'id',
 | 
					    idname: 'id',
 | 
				
			||||||
 | 
					    // contact nodes could apply to either credential or profile?
 | 
				
			||||||
    indices: baseFields.concat([ 'accountId', 'verifiedAt', 'lastVerifiedAt' ]),
 | 
					    indices: baseFields.concat([ 'accountId', 'verifiedAt', 'lastVerifiedAt' ]),
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user