飞书网页应用免登(SSO)

一、需求描述

最近有个需求实现网页应用在飞书客户端内的免登操作并获取用户信息,相关飞书文档:https://open.feishu.cn/document/quickly-create-a-login-free-web-app/introduction

最初看到示例代码(https://open.feishu.cn/document/quickly-create-a-login-free-web-app/introduction-to-sample-code),看起来是挺简单的过程。但这里有个坑,就是示例代码跟简介里面的流程是不一样的:

实际示例代码的免登流程应该是这样的:https://open.feishu.cn/document/client-docs/h5/development-guide/step-3

示例代码是通过在客户端调用tt.requestAccess来获取临时授权code(官方推荐),而简介里是通过跳转授权链接再重定向URL携带授权码的方式。

飞书上的文档看似详细,但隐藏的问题也是需要注意的(后面会提到相关问题),这方面现在的资料比较少,因此记录起来分析给大家。

下面,也是使用示例代码的方式来实现飞书网页应用免登(SSO)。

二、代码实现

复制代码
<template>
  <div class="app">
      <!-- 加载状态 -->
      <div v-if="loading" class="loading-container">
        加载中
      </div>
  </div>
</template>

<script setup>
import { ref, onMounted, watch } from 'vue';
import { showToast } from 'vant';

// 状态管理
const loading = ref(false);

// 飞书免登录流程
const initFeishuLodinAuth = async () => {
  try {
    // 检查是否在飞书客户端内
    if (!window.h5sdk && !window.tt) {
      console.log('invalid h5sdk');
      showToast('please open in feishu');
      return;
    }
    
    // 通过服务端的get_appid获取app_id
    // 为了安全,app_id不应对外泄露,尤其不应在前端明文书写,因此此处从服务端获取
    const appIdResponse = await fetch('/feishu/shenpi/getAppId');
    const appIdRes = await appIdResponse.json();
    const appId = appIdRes.data.appId;
    
    // 通过error接口处理API验证失败后的回调
    window.h5sdk.error(err => {
        throw('h5sdk error:', JSON.stringify(err));
    });

    // 初始化JSSDK
    await new Promise((resolve, reject) => {
      window.h5sdk.ready(() => {
        resolve();
      });
    });
    
    // 获取免登临时授权码
    // 注意:根据文档,应该使用tt.requestAcces
    const code = await new Promise((resolve, reject) => {
      tt.requestAccess({
        appID: appId,
        scopeList: [],
        success: (res) => {
          console.log('requestAccess success:', res);
          resolve(res.code);
        },
        fail: (err) => {
          reject(err);
        }
      });
    });
    
    // 向服务端发送code获取用户信息
    const userResponse = await fetch(`/feishu/shenpi/ssoLogin?code=${code}`);
    
    const userResData = await userResponse.json();
    console.log('userResData:', userResData);
    if (userResData.code == 200) {
      isLoggedIn.value = true;
      console.log('登录成功:', userResData.data);
    } else {
      showToast('登录失败,请重试');
    }
  } catch (error) {
    console.error('免登流程失败:', error);
    showToast('免登失败,请重试');
  }
};

// 页面加载时初始化
onMounted(async () => {
  // 开始加载,显示加载动画
  loading.value = true;
  
  try {
    // 执行免登录流程
    await initFeishuLodinAuth();

  } finally {
    // 结束加载,隐藏加载动画
    loading.value = false;
  }
  
});
</script>

<style scoped>
.app {
  height: 100vh;
  display: flex;
  flex-direction: column;
  background-color: #f5f5f5;
}
</style>

三、问题记录

  1. Error code: 20029; error message: invalid redirect uri in h5 case 请求不合法

在飞书客户端打开测试网页应用,可能会遇到以下报错:

复制代码
errCode: 999
errMsg: "requestAccess:fail please check errno"
errno: 2700002
errString: "Authorization terminated unexpectedly. Error code: 20029; error message: invalid redirect uri in h5 case 请求不合法"

解决方案:

网页应用配置http://xxx.xxx.xxx:3000

需要在飞书开发者后台安全设置中正确设置重定向URL,由于是测试,所以这边设置了本地IP地址:http://xxx.xxx.xxx:3000/,注意这个地址是你调用tt.requestAccess的网页地址(建议在当前页面调用 window.location.href.split('?')[0].split('#')[0] 来获取重定向 URL),具体规则可以查看https://open.feishu.cn/document/develop-web-apps/configure-redirect-urls

IP 白名单、H5 可信域名也相应配置一下。

特别注意:如果是http://xxx.xxx.xxx:3000/,不要遗漏路径最后面的那个/,否则还会报那个错!!!

相关推荐
小村儿5 分钟前
Harness Engineering:为什么你用 AI 越用越累?
前端·后端·ai编程
enoughisenough27 分钟前
浏览器判断控制台是否开启
前端
Moment36 分钟前
当前端开始做 Agent 后,我才知道 LangGraph 有多重要❗❗❗
前端·后端·面试
竹林81839 分钟前
RainbowKit 快速集成多链钱包连接:从“连不上”到丝滑切换的踩坑实录
前端·javascript
小蜜蜂dry1 小时前
nestjs实战-登录、鉴权(一)
前端·后端·nestjs
农夫山泉不太甜1 小时前
WebSocket与SSE技术方案选型对比分析
前端
重阳微噪1 小时前
受够了空格翻页?我写了一个 Chrome 自动滚动插件,让你真正沉浸式阅读
前端
Awu12271 小时前
🍎用 pretext 搞定输入框动态宽度:一个困扰了我三天的 CSS 问题
前端
萑澈1 小时前
我用 Cloudflare 搭了一个 FlashInbox 临时邮箱
前端·next.js
慕斯fuafua2 小时前
CSS——盒模型
前端·css