From 23b1a851a9902a459bd9c81c97abe8bc5996670b Mon Sep 17 00:00:00 2001 From: moonrailgun Date: Fri, 25 Aug 2023 01:00:58 +0800 Subject: [PATCH] docs: add document about oauth login --- .../test/demo/openapi-client-simple/index.ts | 5 +- website/docs/advanced-usage/openapp/about.md | 2 + website/docs/advanced-usage/openapp/oauth.md | 94 ++++++++++++++++++ .../current/advanced-usage/openapp/about.md | 2 + .../current/advanced-usage/openapp/oauth.md | 94 ++++++++++++++++++ .../static/img/advanced-usage/openapp/3.png | Bin 0 -> 19788 bytes .../static/img/advanced-usage/openapp/4.png | Bin 0 -> 15115 bytes 7 files changed, 194 insertions(+), 3 deletions(-) create mode 100644 website/docs/advanced-usage/openapp/oauth.md create mode 100644 website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-usage/openapp/oauth.md create mode 100644 website/static/img/advanced-usage/openapp/3.png create mode 100644 website/static/img/advanced-usage/openapp/4.png diff --git a/server/test/demo/openapi-client-simple/index.ts b/server/test/demo/openapi-client-simple/index.ts index f05228f4..866881f3 100644 --- a/server/test/demo/openapi-client-simple/index.ts +++ b/server/test/demo/openapi-client-simple/index.ts @@ -3,9 +3,9 @@ import path from 'path'; import axios from 'axios'; import fs from 'fs-extra'; const app = express(); -const port = 8080; +const port = process.env.PORT || 8080; -const API = process.env.API || 'http://localhost:11001'; +const API = process.env.API || 'https://tailchat-nightly.moonrailgun.com'; // dev environment is 'http://localhost:11001' const clientUrl = `http://localhost:${port}`; const clientId = process.env.ID || 'tc_649aa2179e97b8b3b2d1004f'; const clientSecret = process.env.SECRET || '4Pt4lccOaztJROs-VhmQf8XBU89-z8rr'; @@ -54,7 +54,6 @@ app.get('/cb', async (req, res, next) => { // 根据获取到的code获取授权码 const { data: tokenInfo } = await request.post('/open/token', { - // client_id: 'foo', client_id: clientId, client_secret: clientSecret, redirect_uri: `${clientUrl}/cb`, diff --git a/website/docs/advanced-usage/openapp/about.md b/website/docs/advanced-usage/openapp/about.md index d2e15650..71a45617 100644 --- a/website/docs/advanced-usage/openapp/about.md +++ b/website/docs/advanced-usage/openapp/about.md @@ -17,6 +17,8 @@ In Tailchat. At present, it mainly provides two forms of open platform applicati The difference from the `com.msgbyte.iam` plugin: `iam` plugin provides a way to log in to `Tailchat` with an external account, such as using a `Github` account to log in to `Tailchat`, while the OAuth capability of the open platform is based on `Tailchat` account to log in to other platforms. ::: +[Learn more](./oauth) + ### Bot `Bot` endows chatbots with interactive application capabilities, which means that Tailchat can not only passively receive external messages, but also actively forward internal chat requests to external applications for processing. diff --git a/website/docs/advanced-usage/openapp/oauth.md b/website/docs/advanced-usage/openapp/oauth.md new file mode 100644 index 00000000..c496039f --- /dev/null +++ b/website/docs/advanced-usage/openapp/oauth.md @@ -0,0 +1,94 @@ +--- +sidebar_position: 5 +title: OAuth +--- + +The `Tailchat` open platform supports the `OAuth` login protocol, and you can easily integrate the `Tailchat` account system into your system. Just like our common `Github Login`, `Google Login`, `Apple Login` + +Now, you can use `Tailchat` to implement a unified account management system for your multiple platforms. + +## Create a new open platform application in Tailchat + +You need to create an open platform application and enable **OAuth** service. + +Fill in the address that is allowed to be redirected in **callback address**. + +![](/img/advanced-usage/openapp/3.png) + +## Create a stand-alone application that initiates and accepts callbacks + +First of all, we need to have a general understanding of the basic process of **OAuth** before we officially start + +![](/img/advanced-usage/openapp/4.png) + +Simply put, it is divided into three steps: + +- The first step: access authorization, you need to pass client_id: client id, redirect_uri: redirect uri, response_type is code, scope is the scope of authorization, fill in `openid profile` by default, and state is other custom parameters +- Step 2: After the authorization is passed, it will be redirected to redirect_uri, and the code will be used as its parameter +- Step 3: After getting the code, you can exchange it for an access token, and then you can directly access resources through the token + +You can refer to [https://github.com/msgbyte/tailchat/blob/master/server/test/demo/openapi-client-simple/index.ts](https://github.com/msgbyte/tailchat/blob /master/server/test/demo/openapi-client-simple/index.ts) to implement your own OAuth application + +### Main process + +Here is a brief overview of the process: + +First construct a request address, like: +``` +/open/auth?client_id=&redirect_uri=&scope=openid profile&response_type=code&state=123456789 +``` + +in: +- `API` is your tailchat backend address, if you use the default deployment scheme, it is your access address. +- `clientId` is the address of the open platform you applied for in the first step. +- `redirect_uri` is your callback address, you need to make sure it has been added to the whitelist of allowed callback addresses +- `scope` is the scope of application authorization, currently fill in `openid profile` fixedly +- `response_type` is the response type, just fill in `code` +- `state` and other custom parameters will be called with redirection and `code` parameters. + +After the user visits this address, it will jump to the Tailchat platform for login authorization. If the authorization is passed, it will be redirected to the address specified by `redirect_uri`. At this time, the receiving address can get `code` and `state` in the query string. + +In the next step, we need to exchange `code` for `token` by sending a POST request. Next, we need to use `token` to obtain user information + +``` +POST /open/token +{ + "client_id": clientId, + "client_secret": clientSecret, + "redirect_uri": redirect_uri, + "code": code, + "grant_type": 'authorization_code', +} +``` + +return value: +``` +{ + access_token, + expires_in, + id_token, + scope, + token_type +} +``` + +At this point we got the `access_token`, which we can use to request user information: + +``` +POST /open/me +{ + "access_token": access_token, +} +``` + +return value: +``` +{ + sub, + nickname, + discriminator, + avatar, +} +``` + +Among them, `sub` can be understood as the user's id, which is the unique identifier of the user diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-usage/openapp/about.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-usage/openapp/about.md index dd292741..673437fe 100644 --- a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-usage/openapp/about.md +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-usage/openapp/about.md @@ -17,6 +17,8 @@ title: 关于开放平台 与 `com.msgbyte.iam` 插件的区别: `iam`插件是提供外部账号登录`Tailchat`的方式,如使用`Github`账号登录`Tailchat`, 而开放平台的OAuth能力则是以`Tailchat`账号登录其他平台。 ::: +[了解更多](./oauth) + ### Bot `Bot` 赋予聊天机器人可交互的应用能力,这意味着 Tailchat 不仅仅可以被动接收来自外部的消息,也可以主动将内部聊天的请求转发到外部应用代为处理。 diff --git a/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-usage/openapp/oauth.md b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-usage/openapp/oauth.md new file mode 100644 index 00000000..6fdb3f28 --- /dev/null +++ b/website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/advanced-usage/openapp/oauth.md @@ -0,0 +1,94 @@ +--- +sidebar_position: 5 +title: OAuth 第三方登录 +--- + +`Tailchat` 开放平台支持 `OAuth` 登录协议, 你可以很方便的将 `Tailchat` 账号体系接入到你的系统中。正如我们常见的 `Github 登录`、`Google 登录`、`Apple 登录` 一样 + +而现在,你可以通过 `Tailchat` 对你的多个平台做统一的账号管理体系。 + +## 在 Tailchat 中新建开放平台应用 + +你需要创建一个开放平台应用并开启 **OAuth** 服务。 + +在**回调地址**处填入允许被重定向的地址。 + +![](/img/advanced-usage/openapp/3.png) + +## 创建独立 应用发起并接受回调 + +首先我们在正式开始之前要大概了解一下 **OAuth** 的基本流程 + +![](/img/advanced-usage/openapp/4.png) + +简单的来说,就是分为三步: + +- 第一步:访问授权,要传client_id:客户端id,redirect_uri:重定向uri,response_type为code,scope是授权范围,默认填`openid profile`即可,state是其它自定义参数 +- 第二步:授权通过,会重定向到redirect_uri,code码会作为它的参数 +- 第三步:拿到code后可以换取 access token, 之后就可以通过token直接访问资源 + +你可以参考 [https://github.com/msgbyte/tailchat/blob/master/server/test/demo/openapi-client-simple/index.ts](https://github.com/msgbyte/tailchat/blob/master/server/test/demo/openapi-client-simple/index.ts) 来实现你自己的 OAuth 应用 + +### 主要流程 + +这里简单简述一下过程: + +首先构建一个请求地址,形如: +``` +/open/auth?client_id=&redirect_uri=&scope=openid profile&response_type=code&state=123456789 +``` + +其中: +- `API` 是你的tailchat后端地址,如果是使用默认部署方案的话就是你的访问地址。 +- `clientId` 是你第一步申请到的开放平台的地址。 +- `redirect_uri` 为你的回调地址,你需要确保其已经被添加到允许回调地址的白名单中 +- `scope` 是申请授权范围,目前固定填入 `openid profile` 即可 +- `response_type` 是响应类型,固定填入 `code` 即可 +- `state` 其他的自定义参数,会随着重定向和`code`参数一起调用. + +用户访问该地址后,会跳转到 Tailchat 平台进行登录授权,如果授权通过的话会重定向到 `redirect_uri` 规定的地址. 此时接收地址可以在query string 中获取到 `code` 和 `state`. + +下一步我们需要通过发送 POST 请求使用 `code` 换取 `token`. 后续我们需要通过 `token` 来获取用户信息 + +``` +POST /open/token +{ + "client_id": clientId, + "client_secret": clientSecret, + "redirect_uri": redirect_uri, + "code": code, + "grant_type": 'authorization_code', +} +``` + +返回值: +``` +{ + access_token, + expires_in, + id_token, + scope, + token_type +} +``` + +此时我们拿到了 `access_token`, 我们可以用来请求用户信息: + +``` +POST /open/me +{ + "access_token": access_token, +} +``` + +返回值: +``` +{ + sub, + nickname, + discriminator, + avatar, +} +``` + +其中`sub`可以理解为用户的id,是用户的唯一标识 diff --git a/website/static/img/advanced-usage/openapp/3.png b/website/static/img/advanced-usage/openapp/3.png new file mode 100644 index 0000000000000000000000000000000000000000..a8afa3b34bb8be9c95409db28845bb1b50232e36 GIT binary patch literal 19788 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