使用Hasura Actions的Auth0处理GraphQL API认证(详细指南)

477 阅读13分钟

使用Hasura Actions的Auth0处理GraphQL API认证

简介

认证和授权是任何应用程序开发过程中两个最基本但又最复杂的安全功能。最常见的做法是将它们互换,但它们实际上是不同的概念。认证是识别用户的过程,而授权决定了被认证的用户可以根据其角色访问的路线。GraphQL是一种用于编写和查询API的出色语言。然而,处理认证和授权可能并不容易,尤其是对于新的GraphQL开发者来说。在基本层面上,你将不得不处理以下问题:

  • 为GraphQL项目设置HTTP服务器
  • 设置数据库(模型、ORM、连接、播种、迁移等)。
  • 编写GraphQL模式和
  • 解析器
    • 验证模式类型
    • 哈希密码
    • 生成JWT
  • 将模式和解析器整合到GraphQL服务器上
  • 检查用户角色/权限的授权层
  • 以及更多...

在所有这些方面的工作很容易让人不知所措(特别是对于小项目)。在这篇文章中,我们将演示如何用Hasura和Auth0设置认证/授权。首先,我们将设置Auth0应用程序、API和规则。接下来,我们将从Auth0应用程序中生成一个JWT令牌,作为客户端(本例中为GraphQL)的授权头。最后,我们将演示Hasura Actions是如何工作的;通过解决一个自定义查询,使用HTTP处理器检索你的Auth0用户资料。

Hasura

Hasura是一个很棒的GraphQL引擎,用于构建强大的GraphQL APIs。Hasura对我来说是个不错的选择,基于以下几点:

  • 它是开源的。
  • 有很好的文档和资源。
  • 提供快速和即时的GraphQL APIs。
  • 使得数据库配置和部署无缝化。
  • 它与其他平台/工具(如Auth0、Firebase)的可扩展性很强。
  • 云服务有很好的UI/UX,第一次使用就很直接。
  • 模式是可扩展的,有远程模式。
  • 你可以用动作进行自定义查询和变异

Auth0

Auth0是一个云身份管理平台,将认证和授权作为一项服务提供。该平台是在开发者和组织很难保证其服务/应用安全的时候出现的。Auth0与语言/框架无关,可扩展性很强。你只需要连接你的应用程序(就像我们很快会和Hasura一起做的那样),并定义你想如何识别你的用户。Auth0从开发者身上解除的一些认证问题是:

  • 基于会话和Cookie的认证
  • 无密码认证
  • 基于令牌的认证
  • 多因素认证
  • 社会认证

另外,Auth0的文档是有史以来最广泛和最广泛的文档!它就像MDN的auth文档一样。它就像MDN的auth docs一样😅:

  • Auth0平台的核心功能是
  • 授权代码 OAuth 2.0
  • 应用程序
  • APIs
  • 基于角色的访问控制(RBAC)
  • 用户角色和权限
  • 规则

在建立我们的演示时,我们将使用这些功能中的大部分。

让我们开始吧。

前提条件

虽然我们不会在这里写很多代码,但你需要以下条件:

  • JavaScript知识
  • Hasura账户
  • Heroku账户
  • Auth0账户
  • 在Glitch上注册

用Auth0设置认证

在本节结束时,我们将能够创建以下内容:

  • JWT令牌作为Graphiql客户端的授权标头
  • 制定认证规则
  • Auth0 API管理令牌
  • 为Hasura生成JWT配置

让我们开始创建一个Auth0应用程序。遵循这些步骤:

  1. 点击你的Auth0仪表板上的应用程序标签

Auth0 dashboard - Create Application

Auth0仪表板 - 创建应用程序

2.选择一个名称,并选择 "单页网页应用程序"

Choose an application type

选择一个应用程序类型

真棒!现在我们已经创建了一个应用程序。现在我们已经创建了一个应用程序,让我们继续创建认证API。

3.点击左侧边栏的 "API "和 "创建API"

 Create Authentication API

创建认证API

确保你注意到标识符。我们将在以后的OAuth2/OIDC工作流程中使用它作为一个受众。棒极了!在我们测试我们的应用程序是否启动和运行之前,我们应该为用户认证时提供规则。Auth0的另一个核心功能是规则。规则是在用户被认证到应用程序时运行的JavaScript函数。它们只在认证完成后运行。规则是JavaScript函数?是的,它们是无服务器函数。换句话说,当你在Auth0中创建一个规则时,你实际上是在利用一个函数即服务的云服务模型。该函数只有在被触发时才会启动服务器。Auth0规则允许我们做以下事情:

  • 使用JavaScript定义授权规则
    • 你可以允许拒绝任何调用API的人访问
    • 为令牌添加用户规则
    • 允许在特定日期访问一个API
    • 以及更多
  • 在Auth0提供的范围之外,从认证提供者那里扩展属性
  • 认证一个用户并获得用户的详细信息
  • 请求自定义API访问

在这里,我们只想认证用户并获得用户的详细信息。让我们继续写一个设置规则来实现。

4.点击左侧边栏的 "规则"。给它一个名字,并粘贴以下几行代码:

function (user, context, callback) {
  const namespace = "https://hasura.io/jwt/claims";
  context.accessToken[namespace] =
    {
      'x-hasura-default-role': 'user',
      // do some custom logic to decide allowed roles
      'x-hasura-allowed-roles': ['user'],
      'x-hasura-user-id': user.user_id
    };
  callback(null, user, context);
}

Auth0 Rules

Auth0 规则

让我们来看看这段代码。user 一个参数是一个存储认证用户信息的对象。它来自身份提供者(谷歌、Github等)。context 是一个存储应用程序当前认证交易信息的对象。诸如用户IP地址、用户连接介质、使用的认证协议等信息。callback 函数要把修改过的令牌送回给Auth0,或者抛出一个错误。调用回调函数很重要,这样函数就不会超时了。在第2行,我们初始化了一个命名空间变量。命名空间只是你给你的索赔的一个标识符(索赔是一个信息,证明了认证的用户信息。它用键/值对表示。这是关键部分)。

注意:命名空间可以是任何东西。它是推荐的。

我们需要命名空间,这样我们的主张就不会与Auth0的保留名称冲突。这就是为什么它被称为自定义索赔😆 。然后,利用context.accessToken[namespace] 对象,将自定义索赔添加到ID令牌中。在我们的数据库中,我们要有一个用户表,并为'user' 创建权限。最后,我们调用callback 函数,这样脚本就不会超时了。很好!现在让我们创建另一条规则,将使用auth0登录的用户与其他使用其他认证策略登录但在数据库中的用户同步。

5.创建另一条规则,并将其称为sync-rules。添加以下几行代码:

function (user, context, callback) {
  const userId = user.user_id;
  const nickname = user.nickname;
  
  const admin_secret = <add hasura admin secret here>;
  const url = <add hasura graphql api here> ;
  const query = `mutation($userId: String!, $nickname: String) {
    insert_users(objects: [{
      id: $userId, name: $nickname
    }], on_conflict: {constraint: users_pkey, update_columns: [last_seen, name]}
    ) {
      affected_rows
    }
  }`;
  const variables = { "userId": userId, "nickname": nickname };
  request.post({
      url: url,
      headers: {'content-type' : 'application/json', 'x-hasura-admin-secret': admin_secret},
      body: JSON.stringify({
        query: query,
        variables: variables
      })
  }, function(error, response, body){
       console.log(body);
       callback(null, user, context);
  });
}

在这段代码中,我们写了一个GraphQL突变,通过id和name添加一个用户,然后检查他们是否是新用户,如果有一个新用户,它就添加用户并返回用户对象。我们刚刚执行了一个upsert操作(一种操作,如果行不存在就插入数据库表,如果存在就更新)。太棒了!我们已经完成了规则。现在让我们得到我们的访问令牌和Auth0管理API令牌。我们将通过测试我们刚刚创建的认证API来获得这些值。

6.回到你的Auth0仪表板,点击左侧边栏的 "扩展",搜索 "Auth0认证API调试器"。继续授权它,我们将使用API调试器来测试我们的API。你应该有这样的东西:

Auth0 API debugger extension

Auth0 API调试器扩展

确保你注意到配置标签上的域名和回调URL。我们将在下一个步骤中使用它。

7.点击0Auth2/0IDC

OAuth是一个身份协议,它在两个应用程序之间进行资源访问和共享,而不会泄露你的密码。相反,它使用授权令牌来确认身份。OAuth2框架是该协议的最新和推荐版本。OIDC(OpenID Connect)也是一个身份协议,使第三方应用程序能够验证用户的身份并检索用户信息。它使用JSON网络令牌(JWT)。

OAUTH2/OIDC

OAUTH2/OIDC

在设置下,添加Audience ,这样我们就可以获得访问令牌。这里的受众通常应该是你的前端应用程序URI。然而,由于我们没有这个,让我们使用我们的API标识符。另外,检查 "使用受众 "开关。配置应该看起来像这样。

Auth settings

认证设置

真棒!我的天啊还有一件事,让我们把API调试器配置中的回调URL添加到我们的应用程序。回到左侧边栏的 "应用程序",点击应用程序名称,在这里添加回调URL。

Application Configuration

应用程序配置

确保你保存更改。

8.回到API调试器,点击OAUTH2/OIDC登录,你应该得到这样的结果:

OAUTH login

OAUTH登录

你可以选择登录或创建一个新账户。提供登录信息后,你应该被重定向到有你的访问令牌的页面:

Access token

访问令牌

请注意access_token ,我们将在Graphiql客户端使用它。你还应该看一下访问令牌的JSON对象。

差不多了!!我们几乎已经完成了Auth0的设置,只是我们还没有访问Auth0管理API。该API使后端服务器能够访问你的Auth0仪表板。

9.转到API,点击Auth0管理API,然后点击API Explorer标签:

Auth0 management API

Auth0管理API

另外,注意JWT令牌

恭喜你,你已经成功地为我们的Hasura应用程序设置了Auth0。
在下一节中,你需要以下内容:

  • 应用程序域名
  • Auth0管理API令牌
  • API访问令牌

设置Hasura应用程序

Hasura为你提供两种建立API的选择。Hasura云和Hasura CLI。推荐使用Hasura Cloud,因为它的设置是无缝的,不过在更大的生产应用中,你的团队可能想在本地使用Hasura的CLI。让我们开始吧。在本节中,我们将完成以下工作:

  • 用Hasura连接Auth0
  • 为Auth0创建自定义类型和类型定义
  • 编写和部署一个调用我们的认证API的Rest API
  • 获取用户资料信息
  1. 转到cloud.hasura.io,并创建一个项目:

Create Hasura App

创建Hasura应用程序

2.设置环境变量

Hasura Env Vars

Hasura Env Vars

已经提供了一个管理秘密。Hasura管理秘密将确保我们的GraphQl端点不公开。让我们再添加一个名为HASURA_GRAPHQL_JWT_SECRET的变量。这就是我们如何将我们的认证服务器连接到Hasura。Auth0将返回JWT令牌,Hasura将解码并验证该令牌,然后授权该请求。要生成这个令牌,请到hasura.io/jwt-config/…选择Auth0作为提供者,然后将你的Auth0应用程序的域名粘贴为域名,并点击 "生成配置 "按钮。

Generated JWT token

生成的JWT令牌

现在复制该令牌并将其作为值添加到HASURA_GRAPHQL_JWT配置中。厉害!启动控制台

 Hasura Console

哈苏拉控制台

3.为Auth0创建自定义类型和类型定义

在这一点上,你可能想知道我们如何扩展Schema(我们无法控制)并编写自定义业务逻辑来整合Auth0 API端点。通常在GraphQL中,我们会写一个Resolver(查询或突变)来执行这个操作。然而,Hasura现在通过使用Actions使之成为可能。Actions帮助你编写自定义查询和突变。Hasura Actions有4个组件。类型、类型定义、处理程序和种类。在我们的案例中,我们将为Auth0创建一个动作。在仪表板的顶部导航栏,点击 "行动"。

Hasura Auth0 action

Hasura Auth0行动

然后创建一个动作,在动作定义中,添加这个代码片段:

type Query{
  auth0: auth0_profile
}

由于我们要从Auth0检索信息,我们要使用查询类型。在类型定义中,添加这段代码:

type auth0_profile{
   id : String
   email : String
   picture : String
}

接下来,我们必须写一个处理程序。处理程序具有当我们向Auth0进行查询时将被执行的逻辑。我们很快就会讲到这一点。保存该动作。你应该有这样的东西。

3.编写动作处理程序

动作处理程序的主要特点是:

  • 它是一个HTTP webhook
  • 它是一个REST API端点
  • Hasura通过行动接收有效载荷session_variables

现在让我们开始编写我们的第一个动作处理程序。

点击Codegen,在选择框中选择nodejs-express

Codegen

Codegen

Hasura已经为我们生成了一些模板代码。他们还让我们选择用glitch托管我们的HTTP服务器。单击 "Try with glitch"。转到src/server ,粘贴这个代码片段

// src/server

const express = require("express");
const bodyParser = require("body-parser");
const fetch = require('node-fetch');
const app = express();
const PORT = process.env.PORT || 3000;
app.use(bodyParser.json());
const getProfileInfo = (user_id) => {
    const headers = {'Authorization': 'Bearer '+process.env.AUTH0_MANAGEMENT_API_TOKEN};
    console.log(headers);
    return fetch('https://' + process.env.AUTH0_DOMAIN + '/api/v2/users/'+user_id,{ headers: headers})
        .then(response => response.json())
}
app.post('/auth0', async (req, res) => {
  // get request input
  const { session_variables } = req.body;
  
  const user_id = session_variables['x-hasura-user-id'];
  // make a rest api call to auth0
  return getProfileInfo(user_id).then( function(resp) {
    console.log(resp);
    if (!resp) {
      return res.status(400).json({
        message: "error happened"
      })
    }
    return res.json({
      id: resp.user_id,
      email: resp.email,
      picture: resp.picture
    })
  });
});
app.listen(PORT);

最后,添加环境变量。Glitch已经在项目的根部创建了一个.env 文件。添加以下的值。

// .env
AUTH0_MANAGEMENT_API_TOKEN 
AUTH0_DOMAIN

记住,你记下了这些变量的值。当你完成这些后,点击分享按钮并复制Live站点的链接

 Share Glitch Project

分享Glitch项目现在我们的处理程序动作URI将是hausraa.glitch.me/auth0。回到Has…

Hasura Action Header

哈苏拉行动标题

4.测试GraphQL API

在这一点上,我们要用Hasura GraphiQL测试我们的GraphQL API。让我们首先把我们从Auth0 API生成的JWT令牌作为授权头添加进去。在你的Hasura仪表板上,点击GraphiQL标签,添加Authorization作为一个键,然后像这样粘贴值Bearer eyJh… (你之前注意到的JWT令牌)。你应该有像这样的东西。

Add Authorization header

添加授权头

现在是测试的时候了。取消选择x-hasura-admin-secret 注意:一定要打开glitch服务器,确保它没有在睡觉。真棒。运行这个查询:

query MyQuery {
  auth0 {
    email
    id
    picture
  }
}

你应该得到这个:

 Auth0 user profile

Auth0用户资料

恭喜你!!

结论

有4种方法可以处理Hasura的认证。它们是

  • 基于JWT的认证
  • 基于管理员密码的认证
  • 基于Webhook的认证和
  • 未经认证的访问

在这篇文章中,我们通过使用Auth0作为认证提供者,探索了基于JWT的认证方法。我们还利用了两个平台的大部分核心功能,特别是Hasura Actions。Hasura Actions使你能够扩展Hasura GraphQL Schema,并为你的GraphQL APIs编写你的自定义业务逻辑。