使用用户池和身份池时的 Lambda 授权方与 IAM 授权方

时间:2021-01-06 02:01:12

标签: amazon-web-services amazon-cognito aws-userpools lambda-authorizer

在 AWS re-Invent 视频中,该解决方案使用了 Cognito 池 + 身份池

它还在 API 网关使用 lambda 授权器来验证令牌并生成策略。我正在阅读How to authenticate API Gateway calls with Facebook?

它说: 要使用联合身份,请将 API 网关方法设置为使用“AWS_IAM”授权。您使用 Cognito 创建角色并将其与您的 Cognito 身份池相关联。然后,您可以使用身份和访问管理 (IAM) 服务授予此角色调用 API 网关方法的权限。

-> 如果是这种情况,我们如何在使用身份池的同时使用 lambda 授权程序而不是 IAM 授权程序 -> 使用 IAM 授权器和在自定义授权器中生成 IAM 策略有什么区别,就像我在这里看到的那样:

https://github.com/aws-quickstart/saas-identity-cognito/blob/96531568b5bd30106d115ad7437b2b1886379e57/functions/source/custom-authorizer/index.js

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');
        });
};

0 个答案:

没有答案