LoginSignup
20
21

More than 5 years have passed since last update.

Amazon API Gateway,lambda,cognitoで認証して、Datasets参照・保存

Last updated at Posted at 2015-12-06

あくまでもサンプルです。このままは使えないかと
組み合わせれば、なんとかなりそうなパターンを整理

サーバー上でcoginitoを使ってますが、
セッションエラーになるので基本はクライアントサイドで実行します。

やりたいこと

  • 未認証のRequestは、coginto.identityIdを返却
  • 外部サービス認証のIDでcoginto.identityIdを参照
  • Authロールにcredentialを更新
  • 外部サービス認証時に認証情報をDatasetに保存
  • coginto.identityIdからDatasetを参照

今回は後に共通処理としてまとめれる用に、ひとつのexports.handlerでまとめてある(手抜き)
appIdは本来、認証をするためのパラメータ(id,pass)とか

実行環境

  • Amazon API Gateway
  • Lambda (Node.js runtime)
  • Cognito

npm

var _ = require('lodash');
var Q = require("q");
var AWS = require('aws-sdk');

AWSはlambdaで実行するのに必要ではないけども、ローカル開発用
Qはlambdaのnodejsが0.10.36なので
lambda current-supported-versions

未認証のRequestは、coginto.identityIdを返却

とりあえずGETでidentityIdを送る前提

var identityId = null;

var createUser = function(data){
  var d = Q.defer();
  AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityId: identityId,
    IdentityPoolId: IDENTITY_POOLID
  });
  AWS.config.credentials.get(function(err) {
    if (err) {
      d.resolve({success: false});
      return;
    }
    identityId = AWS.config.credentials.identityId;
    d.resolve({
      identityId: identityId
    });
  });
  return d.promise;
}

外部サービス認証のIDでcoginto.identityIdを参照

my.appはcognitoで設定したもの
本来はここで独自認証の処理を行い、userIDやtokenをLoginに登録
setTimeoutは認証の代わり

TokenはAuthenticated RoleでのAWSの接続に利用(今回はつかっていない)

var authUser = function(data){
  var d = Q.defer();

  //ここで外部認証処理...
  setTimeout(function(){

    //結果を保持する
    var params = {
      IdentityPoolId: IDENTITY_POOLID,
      IdentityId: identityId,
      Logins: {
        'my.app': data.user
      }
    };
    cognitoidentity.getOpenIdTokenForDeveloperIdentity(params, function(err, res){
      if(err){
        d.resolve({success: false});
        return;
      }
      identityId = res.IdentityId;
      d.resolve(res);
    });

  }, 500);
  return d.promise;
};

Authロールにcredentialを更新

getOpenIdTokenForDeveloperIdentityで取得したTokenでcredentialを更新することで、ロールを切り変える

cognitoidentity.getOpenIdTokenForDeveloperIdentity(params, function(err, res){
  if(err){
    return;
  }

  //credentialを更新
  AWS.config.credentials = new AWS.WebIdentityCredentials({
    RoleArn: "arn:aws:iam:xxxxxxx:role/Cognito_myAppAuth_Role",
    WebIdentityToken: res.Token
  });

  var dynamodb = new AWS.DynamoDB();
  dynamodb.scan({
    TableName: 'xxxxxx'
  },function(err,data){
    //tableを取得
  });
});

coginto.identityIdからDatasetを参照

ここはそのまま

var cognitosync = new AWS.CognitoSync();

var listData = function(data){
  var d = Q.defer();
  if(!identityId){
    d.resolve({
      success: false
    });
    return d.promise;
  }

  cognitosync.listRecords({
    DatasetName: COGNITO_DATASET_NAME,
    IdentityId: identityId,
    IdentityPoolId: IDENTITY_POOLID
  }, function(err, res){
    if (err) {
      d.resolve({success: false});
      return;
    }
    console.log(parseData(res.Records));
    d.resolve(res); 
  });
  return d.promise;
};

外部サービス認証時に認証情報をDatasetに保存

Recordに外部接続情報等を保持

var addData = function(data) {
  var d = Q.defer();

  if(!identityId){
    d.resolve({
      success: false
    });
    return d.promise;
  }

  var params = {
    DatasetName: COGNITO_DATASET_NAME,
    IdentityId: identityId,
    IdentityPoolId: IDENTITY_POOLID,
    SyncSessionToken: data.SyncSessionToken,
    RecordPatches: [{
        Key: 'USER_ID',
        Op: 'replace',
        SyncCount: data.DatasetSyncCount,
        Value: 'aaaaaaaaaaaaa'
    }]
  };
  cognitosync.updateRecords(params, function(err, data) {
    if(err){
      d.resolve({success: false});
      return;
    }
    d.resolve(parseData(data.Records)); 
  });
  return d.promise;
}

実行結果

identityIdなし

{
    identityId: "ap-northeast-1:2b967234-8a98-4dbf-945f-ba9581981a06"
}

identityIdあり

{
    identityId: "ap-northeast-1:2b967234-8a98-4dbf-945f-ba9581981a06",
    userdata: {
        USER_ID: "aaaaaaaaaaaaa"
    }
}

外部の認証情報(appId)を渡す

{
    identityId: "ap-northeast-1:dc94f109-993a-457e-b40e-86042a1fbe67",
    userdata: {
        USER_ID: "aaaaaaaaaaaaa"
    }
}

identityId, appId の優先度

appIdが優先される。
Identity poolに既にある場合はLinked logins:DISABLEDとなる

全文

アカウントはダミー

index.js
/**
 * cognito test
 *
 */
"use strict";

var _ = require('lodash');
var Q = require("q");
var AWS = require('aws-sdk');
AWS.config.region = 'ap-northeast-1';
var IDENTITY_POOLID = 'ap-northeast-1:XXXXXXXXX';
var COGNITO_DATASET_NAME = 'userData';

var cognitoidentity = new AWS.CognitoIdentity();
var cognitosync = new AWS.CognitoSync();
var identityId = null; 


/**
 * 認証情報がない場合はUnauthenticated Userを作成
 */
var createUser = function(data){
  var d = Q.defer();
  AWS.config.credentials = new AWS.CognitoIdentityCredentials({
    IdentityId: identityId,
    IdentityPoolId: IDENTITY_POOLID
  });
  AWS.config.credentials.get(function(err) {
    if (err) {
      d.resolve({success: false});
      return;
    }
    identityId = AWS.config.credentials.identityId;
    d.resolve({
      identityId: identityId
    });
  });
  return d.promise;
}


/**
 * Developer Authenticated 認証
 */
var authUser = function(data){
  var d = Q.defer();

  //ここで外部認証処理...
  setTimeout(function(){

    //結果を保持する
    var params = {
      IdentityPoolId: IDENTITY_POOLID,
      IdentityId: identityId,
      Logins: {
        'my.app': data.user
      }
    };
    cognitoidentity.getOpenIdTokenForDeveloperIdentity(params, function(err, res){
      if(err){
        d.resolve({success: false});
        return;
      }
      identityId = res.IdentityId;
      d.resolve(res);
    });

  }, 500);
  return d.promise;
};


/**
 * List store Data
 */
var listData = function(data){

  var d = Q.defer();
  if(!identityId){
    d.resolve({
      success: false
    });
    return d.promise;
  }

  cognitosync.listRecords({
    DatasetName: COGNITO_DATASET_NAME,
    IdentityId: identityId,
    IdentityPoolId: IDENTITY_POOLID
  }, function(err, res){
    if (err) {
      d.resolve({success: false});
      return;
    }
    console.log(parseData(res.Records));
    d.resolve(res); 
  });
  return d.promise;
};


/**
 * add store Data
 */
var addData = function(data) {
  var d = Q.defer();

  if(!identityId){
    d.resolve({
      success: false
    });
    return d.promise;
  }

  var params = {
    DatasetName: COGNITO_DATASET_NAME,
    IdentityId: identityId,
    IdentityPoolId: IDENTITY_POOLID,
    SyncSessionToken: data.SyncSessionToken,
    RecordPatches: [{
        Key: 'USER_ID',
        Op: 'replace',
        SyncCount: data.DatasetSyncCount,
        Value: 'aaaaaaaaaaaaa'
    }]
  };
  cognitosync.updateRecords(params, function(err, data) {
    if(err){
      d.resolve({success: false});
      return;
    }
    d.resolve(parseData(data.Records)); 
  });
  return d.promise;
}

/**
 * Records を Object に変換
 */
var parseData= function(rec){
  var obj = {};
  _.each(rec, function(r){
    obj[r.Key] = r.Value;
  });
  return obj;
};

exports.handler = function (e, context) {
  var query = e.queryParameters || {};

  //認証情報ない
  if(!query.identityId && !query.appId){
    createUser().done(function(res){
      context.succeed(res);
    });
    return;
  }

  //appIdがない
  if(!query.appId){
    identityId = query.identityId;
    Q.when()
    .then(createUser)
    .then(listData)
    .then(addData)
    .done(function(res){
      context.succeed({
        identityId:identityId,
        userdata:res
      });
    });
    return;
  }

  if(query.identityId) {
    identityId = query.identityId;
  }

  Q.when({
    user: query.appId
  })
  .then(authUser)
  .then(listData)
  .then(addData)
  .done(function(res){
    context.succeed({
      identityId:identityId,
      userdata:res
    });
  });
};

参考リンク

20
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
21