为无服务器应用程序使用Auth0创建ReactJS客户端(使用IAM)

在上一篇文章中,我们学习了如何在React应用程序中使用Auth0来认证用户并允许他们访问我们的无服务器应用程序。 该方法使用了Auth0和AWS自定义授权功能生成的JWT令牌来验证它们。

对于今天的帖子,我们将使用不同的方法。 我们将利用Auth0与AWS的集成来通过委托获取AWS Access Key ID和AWS Secret Access Key属性 端点以访问使用IAM保护的API网关端点。

通过这种方法,我们不仅可以使用Web应用程序上的AWS开发工具包来访问API Gateway终端节点,还可以访问其他AWS服务。 不利的一面是,它使我们的Web应用程序与AWS结合使用,并且需要大量额外的配置。

我们将从上一篇文章的开头开始。 您可以在我的Github存储库中找到该Web应用程序和无服务器应用程序的代码。

概括地说,我们使用了Auth0生成的React + Webpack Web应用程序来创建Web客户端的基础。 我们在Auth0中创建了一个客户端,该客户端用于消耗我们的无服务器应用程序端点。 我们为hello函数创建了两个Serverless端点,它们仅返回请求中发送的上下文数据。 一个公共helloPublic和一个受保护的helloAuth0 。 在本文中,我们将创建第三个端点helloIAM,以使用IAM进行保护。

让我们开始在无服务器应用程序中更新我们的serverless.yml文件:

 功能: 
authorizerFunc:
处理程序:handler.auth
helloPublic:
处理程序:handler.hello
事件:
-http:
路径:helloPublic
方法:获取
cors:是的
helloIAM
处理程序:handler.hello
事件:
-http:
路径:helloIAM
方法:获取
cors:是的
helloAuth0:
处理程序:handler.hello
事件:
-http:
路径:helloAuth0
方法:获取
cors:是的
授权者:authorizerFunc

如您所见, helloIAMhelloPublic的端点具有相同的配置。 这是因为,无服务器框架仍未提供在API网关端点中启用IAM授权的方法。 这也意味着无服务器脱机无法在本地启用这种授权。

为了启用IAM授权,您需要将无服务器应用程序部署到AWS:

 无服务器部署 

现在,打开AWS Web控制台并导航到API Gateway服务,您可以找到无服务器生成的终端节点。 在“授权设置”中,将“授权”字段更改为AWS_IAM

尝试访问helloIAM端点:

现在,端点已受到保护。

为了允许我们的Web应用使用API​​端点,我们必须通过插件启用Auth0委派 我使用了Auth0教程来创建无服务器应用程序,并将其与AWS集成,并使用API​​将AWS设置为委托身份验证,以此作为本文的基础。 尝试不要忽略有关Web应用程序配置的部分,因为它使用的是较旧版本的Auth0,该版本与生成的React代码给出的版本不同。 以下步骤将向我们展示如何绕过该限制。

对于此示例,对您的auth0-api-role角色使用以下策略:

  { 
“ Version”:“ 2012-10-17”,
“声明”:[
{
“效果”:“允许”,
“动作”:[
“ execute-api:*”
],
“资源”:[
“ *”
]
}
]
}

如在Auth0教程中的使用API​​为委托的身份验证设置AWS一节中所述,在Auth0中创建规则。

使用此图像可获得更好的上下文:

这些教程中描述的设置很长且充满了细节。 我不想在这里详细复制它,因此请在应用它时尝试使其有意义。 如果您对如何使用它的具体细节有疑问,请发表评论或向我发送推文。 如果需要更多信息,我将更新此帖子。

配置Auth0和AWS后,我们需要在Auth0生成的Web应用程序中更新AuthService.js,以检索Auth0提供的AWS密钥。

不幸的是,Auth0.js v8 SDK不支持令牌委派。 V7曾经提供了一种便捷的方法来获取此AWS令牌,但尚未在v8中实现。 但是,仍然通过/ delegation端点提供该功能。 我们仍然可以通过使用Axios对它进行AJAX请求来检索信息。

 导出默认类AuthService扩展EventEmitter { 
构造函数(clientId,域){
超()

this.delegate = {
client_id:clientId,
grant_type:'urn:ietf:params:oauth:grant-type:jwt-bearer',
范围:“ open_id”,
api_type:'aws'
}

this.auth0Endpoint =域;
//配置Auth0
this.lock = new Auth0Lock(clientId,domain,{
验证:{
redirectUrl:`$ {window.location.origin} / login`,
responseType:“令牌”
}
})
//为锁`authenticated`事件添加回调
this.lock.on('authenticated',this._doAuthentication.bind(this))
  // ... 
}
  // .... 
  _doAuthentication(authResult){ 
var ctrl = this;
//保存用户令牌
this.setToken(authResult.idToken)
//导航到本国路线
browserHistory.replace('/ home')
//异步加载用户个人资料数据
this.lock.getProfile(authResult.idToken,(错误,配置文件)=> {
如果(错误){
console.log('加载配置文件时出错',错误)
}其他{
this.setProfile(个人资料)
}
})

序列化 = function(obj){
var str = [];
for(在obj中的var p)
如果(obj.hasOwnProperty(p)){
str.push(encodeURIComponent(p)+“ =” + encodeURIComponent(obj [p]));;
}
返回str.join(“&”);
}

axios({
方法:“获取”,
url :“ https://” + this.auth0Endpoint +“ / delegation?” + 序列化 (Object.assign({id_token:authResult.idToken},this.delegate))
})
.then((res)=> {
localStorage.setItem('awstoken', JSON .stringify (res.data.Credentials))
});
}

重点是:

  • 完全按照前面的代码中所示配置委托和auth0Endpoint属性。
  • 使用axios将请求发送到客户端的Auth0委托终结点。
  • 从响应中检索凭据属性,并将其放入本地存储中。

使用awstoken ,现在我们可以构建一个客户端来使用API​​端点。 它包含一个使用者密钥ID和一个秘密密钥,只要我们在允许之前创建的角色和策略,AWS SKD可以使用它来使用AWS中的任何服务

AWS提供了一个非常酷的功能。 他们将生成一个SDK,供您使用API​​网关资源。 您可以下载它,设置访问密钥ID和秘密密钥,并使用针对特定端点预先配置的SDK。

您可以在相应阶段(本例中为dev )的“阶段”部分的“ SDK生成”选项卡中获取它。 我们可以选择JavaScript作为要为其生成SDK的平台。

不利的一面是生成的JavaScript代码未针对Webpack构建进行结构化。 它依赖于您在HTML文档中导入正确的JS文件。 我为您修改了公共库,以便能够将其导入Webpack项目中,您可以在此处找到代码。 我希望在不久的将来将它们移动到自定义的NPM包中,以使您可以将其导入到项目中。

但是,SDK中有一个非通用文件。 这就是apigClient.js的内容。 该代码是专门为您的API网关生成的。 我将其重命名为TacoApiClient.js,并对其进行了修改以与Webpack和ES5一起使用。 您可以将我的文件用作参考,以了解需要进行哪些更改。

使用该代码,您可以将客户端导入到您的React应用程序中。

在Auth0生成的网络应用程序内的views / Main / Home / Home.js中,我们将添加以下功能:

  从'utils / TacoApiClient' 导入 TacoApiClient 
  // ... 
  testIAM (){ 
var awstoken = JSON .parse( localStorage .getItem( 'awstoken' ));
  客户端= 新的 TacoApiClient({ 
accessKey :awstoken.AccessKeyId,
secretKey:awstoken.SecretAccessKey,
sessionToken :awstoken.SessionToken,
region'us-east-2' //您要工作的区域。
})

  var params = { 
//这是任何标头,路径或查询字符串请求参数的位置。 密钥是API中定义的命名参数
};
var body = {
//在这里定义请求的主体
};
var AdditionalParams = {
//如果有任何未建模的查询参数或标头需要与请求一起发送,则可以在此处添加它们
标头 :{
},
queryParams :{
}
};
  client.helloGet(params,body,AdditionalParams) 
.then(result => {
控制台 .log(结果)
//在这里放置成功回调
这个 .setState({
你好 :result.data.message
})
})。catch(result => {
控制台。错误 (结果)
//这是放置错误回调的地方
});
}

对于最后一步,让我们在Home组件中添加一个按钮来调用testIAM函数:

  render(){ 
const { profile ,hello} = 这个
返回
< div className = {样式。 }>
< h2 >家</ h2 >
< p >欢迎{ 个人资料名称 }!</ p >

< Button onClick = { this .testAuth0.bind( this }}>测试Auth0 </ Button >
< Button onClick = { this .testPublic.bind( this }}>测试公共</ Button >
< Button onClick = { this .logout.bind( this }}>注销</ Button >
< pre > {hello} </ pre >
</ div >

}

加载您的Web应用程序:

点击“测试IAM”,您应该会看到一个成功的请求: