使用带有 Cognito 身份验证用户的 Web 套接字连接到 AWS IoT
Posted
技术标签:
【中文标题】使用带有 Cognito 身份验证用户的 Web 套接字连接到 AWS IoT【英文标题】:connect to AWS IoT using web socket with Cognito authenticated users 【发布时间】:2017-03-11 03:01:07 【问题描述】:我正在尝试使用浏览器中的 Web 套接字连接到 AWS IoT。
我试过这个例子: https://github.com/awslabs/aws-iot-examples/tree/master/mqttSample
另外一个稍作修改,以便与 Cognito 身份池登录用户一起使用。 https://github.com/dwyl/learn-aws-iot/blob/master/src/js/utils/request.js#L27
如果我使用具有有效 IoT 策略的 IAM 用户,我可以成功连接,但如果我使用用户凭证,我会收到“101 Switching Protocols”响应,但随后会关闭。
与经过身份验证的用户关联的 IAM 角色是正确的,我可以签署请求并执行其他私有操作,例如调用 APIG 端点。套接字连接也没有响应 403。所以这可能不是权限问题。
还能是什么?
【问题讨论】:
【参考方案1】:对于未经身份验证的认知身份,“身份池已身份验证”角色足以允许连接到 IoT MQTT 代理。但是,对于经过身份验证的 cognito 身份,需要两件事:
“身份池已验证”角色必须允许访问您需要的 IoT 操作(例如连接、发布等)。
您必须使用 AttachPrincipalPolicy API 将 IoT 策略(与附加到您的设备的策略完全相同)附加到 cognito 身份
第 2 步是我今天早些时候卡住的地方,因为在任何地方都不是特别清楚需要这样做。
AFAIK 无法从任何 AWS 网站将 IoT 策略附加到认知用户。但是,如果您在机器上设置了 AWS 命令行界面,则可以从那里进行设置。命令如下:
aws iot attach-principal-policy --policy-name <iot-policy-name> --principal <cognito-identity-id>
可以使用Federated Identities > Your Pool > Identity browser
找到认知身份ID,或者您也可以在对CognitoIdentityCredentials.get
呼叫的响应中找到它。看起来像这样us-east-1:ba7cef62-f3eb-5be2-87e5-fffbdeed2824
对于生产系统,您显然希望自动附加此策略,可能在用户注册时使用 lambda 函数。
有关需要附加 IoT 策略的文档部分可以在 this page 上找到:
要使经过身份验证的 Amazon Cognito 身份通过 HTTP 在您的 AWS 账户中的 topic1 上发布 MQTT 消息,您必须指定两个策略,如此处所述。第一个策略必须附加到 Amazon Cognito 身份池角色,并允许该池中的身份进行发布调用。第二个策略使用 AWS IoT AttachPrincipalPolicy API 附加到 Amazon Cognito 用户,并允许指定的 Amazon Cognito 用户访问 topic1 主题。
【讨论】:
我错过了 2º。我在注册时为每个用户附加了一个带有Action: ["iot:*"], Resource: "*"
的新策略(创建的 con aws iot 控制台),并为经过身份验证的 Cognito 池角色附加了相同的权限(仅用于测试目的)。但是现在我得到了 403。我在 Paho.MQTT.Client 和 region、AccessKeyId 和 SecretKey 中使用用户 identityId (us-east-1:...) 作为 clientId 来签署请求。
@nachoab 您可以发布指向您的项目源的链接吗?还是要重现问题?在我的项目中,我使用 AWS IoT Device SDK 来连接到消息代理并处理设备影子。因此,我需要查看更多您的来源,以了解您的问题可能是什么。
问题出在 iot 策略上,它的“资源”不正确,现在一切正常
我正在使用github.com/aws/aws-iot-device-sdk-js 来获取使用套接字的设备的状态。我已经附加了策略,并传递了 cognito credential accesskey & secretkey 来创建设备连接,client.device = awsIot.device( clientId: clientID, host: host, accessKeyId: cognitoCredentials.AccessKeyId, secretKey: cognitoCredentials.SessionToken,协议:'wss',sessionToken:cognitoCredentials.SessionToken );但它得到403错误。我在这里错过了什么吗?
@Indra,我今天才注意到你的评论。 accessKeyId
、secretKey
和 sessionToken
在初始化 AWSIoTData.thingShadow
时应该是一个空字符串 ""
。然后你需要在thinkShadow
返回的对象上调用updateWebSocketCredentials
,传入以下参数`updateWebSocketCredentials(accessKeyId, secretKey, sessionToken)。【参考方案2】:
为了在前端实现 Caleb 的回答,我必须做几件事:
-
通过转到 IoT 控制台 > 安全 > 策略并将
AWSIoTDataAccess
策略内容复制并粘贴到其中来创建一个 IoT 策略(命名为“默认”)
将以下内联策略添加到我的 Cognito 身份池的已验证角色:"Effect": "Allow", "Action": ["iot:AttachPrincipalPolicy"], "Resource": ["*"]
然后我更新了我的前端代码,如下所示:
AWS.config.region = process.env.AWS_REGION;
AWS.config.credentials = new AWS.CognitoIdentityCredentials(
IdentityPoolId: process.env.AWS_IDENTITY_POOL,
Logins:
'graph.facebook.com': FACEBOOK_ACCESS_TOKEN
);
AWS.config.credentials.get(() =>
const IoT = new AWS.Iot();
IoT.attachPrincipalPolicy(
policyName: 'default',
principal: AWS.config.credentials.identityId
, (err, res) =>
if (err) console.error(err);
// Connect to AWS IoT MQTT
);
);
【讨论】:
你是个救星。 我可能弄错了,但这并不安全。您允许任何经过身份验证的用户在附加到身份池的策略定义的范围内决定哪些身份可以在 IOT 上执行哪些操作。因此,如果我理解正确,任何经过身份验证的用户都可以为自己或其他人分配不同于“默认”的策略。看起来这应该由后端的 lambda 来完成。 我没有机会对此进行测试,但我认为您是对的。出于生产目的,请仔细考虑您的策略和前端的策略访问权限。 绝对不应该从前端将策略附加到用户。您的后端需要组织附加策略。【参考方案3】:这是一个代码示例,用于将 IoT 策略从 Lambda (NodeJS) 函数附加到 Cognito 用户 ID。
function attachPrincipalPolicy(device_id, cognito_user_id)
const iotMgmt = new AWS.Iot();
return new Promise(function(resolve, reject)
let params =
policyName: device_id + '_policy',
principal: cognito_user_id
;
console.log("Attaching IoT policy to Cognito principal")
iotMgmt.attachPrincipalPolicy(params, (err, res) =>
if (err)
console.error(err);
reject(err);
else
resolve();
);
);
【讨论】:
是否应该将此方法创建为单独的 Lambda 函数?我想以某种方式在我的 Dart 代码中使用此方法,但我无法弄清楚如何或从何处获得此功能。 这是一个 AWS API 调用,可以在任何地方进行。 谢谢,是的,自从我发表评论以来,我学到了很多关于这个话题的新有趣的东西。【参考方案4】:我参考了 Caleb 和 senornestor 的答案,以下实现对我有用:
AWS.config.credentials = new AWS.CognitoIdentityCredentials(
IdentityPoolId: AWSConfiguration.poolId,
Logins:
'accounts.google.com': user.Zi.id_token
);
var cognitoIdentity = new AWS.CognitoIdentity();
AWS.config.credentials.get(function(err, data)
if (!err)
console.log('retrieved identity: ' + AWS.config.credentials.identityId);
var params =
IdentityId: AWS.config.credentials.identityId,
Logins:
"accounts.google.com": user.Zi.id_token
;
cognitoIdentity.getCredentialsForIdentity(params, function(err, data)
if (!err)
console.log('retrieved credentials');
const IoT = new AWS.Iot();
IoT.attachPrincipalPolicy(
policyName: 'exampleIoTPolicy',
principal: AWS.config.credentials.identityId
, (err, res) =>
if (err) console.error(err);
); // Change the "policyName" to match your IoT Policy
else
console.log('error retrieving credentials: ' + err);
alert('error retrieving credentials: ' + err);
);
else
console.log('error retrieving identity:' + err);
alert('error retrieving identity: ' + err);
);
【讨论】:
【参考方案5】:这是一个示例应用程序,它应该有助于演示如何使用 Cognito 对 IoT 进行身份验证:
https://github.com/awslabs/aws-iot-chat-example
有关明确说明,您可以阅读:
https://github.com/awslabs/aws-iot-chat-example/blob/master/docs/authentication.md
【讨论】:
【参考方案6】:事实证明,即使在 2021 年,也有必要创建一个专用的 Lambda 函数来执行 AttachPolicy
(不是 ,因为它已过时)。如the official Docs中所述:AttachPrincipalPolicy
要将 AWS IoT Core 策略附加到 Amazon Cognito 身份,您必须定义一个调用 AttachPolicy 的 Lambda 函数。
其他答案显示了如何实现该 Lambda。
【讨论】:
以上是关于使用带有 Cognito 身份验证用户的 Web 套接字连接到 AWS IoT的主要内容,如果未能解决你的问题,请参考以下文章
是否可以在 Amplify 的 schema.graphql 中指定 2 个带有身份验证注释的 Cognito 用户池?
带有 facebook 身份验证的 Amazon cognito 身份管理
使用 AWS Cognito 和 AD FS 作为 Web 应用程序的身份验证