在 AWS re-Invent 视频中,该解决方案使用 Cognito 池 + 身份池
它还在 API 网关上使用 lambda 授权器来验证令牌并生成策略。我正在阅读 如何使用 Facebook 验证 API Gateway 调用?
它说:要使用联合身份,您将 API Gateway 方法设置为使用“AWS_IAM”授权。您使用 Cognito 创建角色并将其与您的 Cognito 身份池相关联。然后,您使用身份和访问管理 (IAM) 服务授予此角色调用您的 API 网关方法的权限。
-> 如果是这种情况,我们在使用身份池时如何使用 lambda 授权方而不是 IAM 授权方 -> 使用 IAM 授权方和在自定义授权方中生成 IAM 策略有什么区别,正如我在这里看到的那样:
const Promise = require('bluebird');
const jws = require('jws');
const jwkToPem = require('jwk-to-pem');
const request = require('request-promise');
const AWS = require('aws-sdk');
AWS.config.setPromisesDependency(Promise);
const s3 = new AWS.S3({ apiVersion: '2006-03-01' });
const { env: { s3bucket }} = process
// cache for certificates of issuers
const certificates = {};
// time tenant data was loaded
let tenantLoadTime = 0;
// promise containt tenant data
let tenantPromise;
// this function returns tenant data promise
// refreshes the data if older than a minute
function tenants() {
if (new Date().getTime() - tenantLoadTime > 1000 * 60) {
console.log('Tenant info outdated, reloading');
tenantPromise = s3.getObject({
Bucket: s3bucket,
Key: 'tenants.json'
}).promise().then((data) => {
const config = JSON.parse(data.Body.toString());
console.log('Tenant config: %j', config);
const tenantMap = {};
config.forEach((t) => { tenantMap[t.iss] = t.id; });
return tenantMap;
});
tenantLoadTime = new Date().getTime();
}
return tenantPromise;
}
// helper function to load certificate of issuer
function getCertificate(iss, kid) {
if (certificates[iss]) {
// resolve with cached certificate, if exists
return Promise.resolve(certificates[iss][kid]);
}
return request({
url: `${iss}/.well-known/jwks.json`,
method: 'GET'
}).then((rawBody) => {
const { keys } = JSON.parse(rawBody);
const pems = keys.map(k => ({ kid: k.kid, pem: jwkToPem(k) }));
const map = {};
pems.forEach((e) => { map[e.kid] = e.pem; });
certificates[iss] = map;
return map[kid];
});
}
// extract tenant from a payload
function getTenant(payload) {
return tenants().then(config => config[payload.iss]);
}
// Help function to generate an IAM policy
function generatePolicy(payload, effect, resource) {
return getTenant(payload).then((tenant) => {
if (!tenant) {
return Promise.reject(new Error('Unknown tenant'));
}
const authResponse = {};
authResponse.principalId = payload.sub;
if (effect && resource) {
authResponse.policyDocument = {
Version: '2012-10-17',
Statement: [{
Action: 'execute-api:Invoke',
Effect: effect,
Resource: resource
}]
};
}
// extract tenant id from iss
payload.tenant = tenant;
authResponse.context = { payload: JSON.stringify(payload) };
console.log('%j', authResponse);
return authResponse;
});
}
function verifyPayload(payload) {
if (payload.token_use !== 'id') {
console.log('Invalid token use');
return Promise.reject(new Error('Invalid token use'));
}
if (parseInt(payload.exp || 0, 10) * 1000 < new Date().getTime()) {
console.log('Token expired');
return Promise.reject(new Error('Token expired'));
}
// check if iss is a known tenant
return tenants().then((config) => {
if (config[payload.iss]) {
return Promise.resolve();
}
console.log('Invalid issuer');
return Promise.reject();
});
}
function verifyToken(token, alg, pem) {
if (!jws.verify(token, alg, pem)) {
console.log('Invalid Signature');
return Promise.reject(new Error('Token invalid'));
}
return Promise.resolve();
}
exports.handle = function handle(e, context, callback) {
console.log('processing event: %j', e);
const { authorizationToken: token } = e;
if (!token) {
console.log('No token found');
return callback('Unauthorized');
}
const { header: { alg, kid }, payload: rawToken } = jws.decode(token);
const payload = JSON.parse(rawToken);
return verifyPayload(payload)
.then(() => getCertificate(payload.iss, kid))
.then(pem => verifyToken(token, alg, pem))
.then(() => generatePolicy(payload, 'Allow', e.methodArn))
.then(policy => callback(null, policy))
.catch((err) => {
console.log(err);
return callback('Unauthorized');
});
};