关于 link-based-auth
link-based-auth 是一种免登录个性化访问的参考模式,适用于作品集和受邀访问等场景。它通过链接令牌、边缘验证和会话 Cookie 建立身份,并说明了安全边界。
主要功能
- 01生成个性化令牌链接
- 02在边缘验证令牌
- 03通过会话 Cookie 认证
- 04映射访客身份
- 05本地拦截并提供模拟数据
适用场景
- 01提供招聘者专属作品集
- 02管理仅限受邀者的演示应用
- 03展示客户专属文档内容
- 04在本地测试个性化视图
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ loxosceles-dev/ dev-skills --skill link-based-auth安装后在 Claude Code 中输入/link-based-auth确认已加载。
详细介绍
基于链接的身份验证
这是一个参考模式。 从这种方法中学习,并根据你的场景进行调整——不要原样照搬。
问题:某些应用需要提供个性化体验,但又不想让用户经历登录流程,例如作品集网站、演示应用和仅限受邀者访问的应用。
解决方案:在个性化 URL 中嵌入身份验证令牌。边缘函数会验证令牌并设置会话 Cookie,让访客无缝地获得已验证身份的体验。
模式
流程:
1. Admin generates personalized link with embedded token
2. Visitor clicks link
3. CDN edge function intercepts request
4. Edge function validates token, sets auth cookie
5. Frontend loads with authentication already established
6. API requests use cookie/token for authorization
架构:
Personalized Link (contains token)
↓
CDN Edge Function
├── Valid token → Set cookie, redirect to app
└── No/invalid token → Serve public view
↓
Static Frontend (reads cookie for auth state)
↓
API (validates token from Authorization header)
关键组件:
- 链接生成器 — 创建嵌入令牌的 URL,并为每个 URL 关联访客元数据
- 身份后端 — 将令牌映射到虚拟用户身份(例如,创建不带密码的 Cognito 用户)
- 边缘身份验证函数 — 在 CDN 边缘验证令牌,并设置会话 Cookie
- 身份验证上下文 — 从 Cookie 中提取令牌,并向组件提供身份验证状态的前端上下文
为什么采用这种模式?
优点:
- 零摩擦:无需登录页面、密码或注册
- 个性化:每个链接都映射到特定的访客身份
- 安全:令牌在服务器端验证,Cookie 使用 HttpOnly/Secure 属性
- 防篡改:修改 URL 无效,因为身份验证与后端身份绑定
适用场景:
- 面向招聘人员提供专属视图的作品集网站
- 仅限受邀者访问的演示应用
- 提供受限个性化内容的营销网站
- 包含客户专属内容的文档
实现
边缘身份验证
// Edge function (Lambda@Edge, CloudFlare Worker, etc.)
export async function handler(event) {
const request = event.Records[0].cf.request;
const params = new URLSearchParams(request.querystring);
const token = params.get('token');
if (token) {
const isValid = await validateToken(token);
if (isValid) {
return {
status: '302',
headers: {
'location': [{ value: '/' }],
'set-cookie': [{ value: `auth=${token}; Secure; HttpOnly; SameSite=Strict` }],
},
};
}
}
// Check existing session cookie
const cookies = request.headers.cookie?.[0]?.value || '';
if (cookies.includes('auth=')) {
return request; // Authenticated, proceed
}
return request; // Unauthenticated, serve public view
}
前端身份验证上下文
// lib/auth/auth-context.tsx
export function useAuth() {
const tokens = extractTokensFromCookies();
const environment = detectEnvironment();
const getAuthHeaders = (routeType: 'public' | 'protected') => {
if (environment === 'local') {
return { 'x-api-key': process.env.NEXT_PUBLIC_API_KEY };
}
if (routeType === 'public') {
return { 'x-api-key': process.env.NEXT_PUBLIC_API_KEY };
}
if (tokens.accessToken) {
return { Authorization: `Bearer ${tokens.accessToken}` };
}
return {};
};
return {
isAuthenticated: !!tokens.accessToken,
environment,
getAuthHeaders,
};
}
基于 Hook 的数据获取
// lib/profile/use-profile.ts
export function useProfile() {
const { isAuthenticated, getAuthHeaders } = useAuth();
const { data, loading } = useQuery(GET_PROFILE, {
skip: !isAuthenticated,
context: { headers: getAuthHeaders('protected') },
});
return { profile: data?.profile, isLoading: loading };
}
安全注意事项
- Cookie 标志:
Secure、HttpOnly、SameSite=Strict— 防止 XSS 和 CSRF - 令牌过期时间:较短的令牌有效期可缩短暴露窗口
- 内容隔离:每个令牌都映射到特定身份,访客只能看到自己的内容
- 无客户端机密信息:令牌在服务器端验证;前端只读取验证结果
- 直接访问 URL:没有有效令牌或 Cookie 时,只能查看公开内容(这是设计如此)
本地开发
在本地开发时,如果没有真实令牌,可以使用一个可根据环境切换的拦截器:
export function useLocalInterceptor() {
const { environment } = useAuth();
const visitorId = useSearchParams()?.get('visitor');
const shouldIntercept = environment === 'local' && !!visitorId;
return {
shouldIntercept,
getMockData: () => ({
name: `Test Visitor ${visitorId}`,
message: 'Mock data for local development',
}),
};
}
组件会先检查拦截器,若无结果则回退到真实数据:
function PersonalizedContent() {
const interceptor = useLocalInterceptor();
const { data } = useRealData();
const content = interceptor.shouldIntercept
? interceptor.getMockData()
: data;
return <div>{content.message}</div>;
}
不适用场景
- 敏感数据:此模式用于个性化,而非保护高度敏感的信息
- 长期会话:基于令牌的链接适合短期交互,不适合持久账户
- 复杂身份验证流程:如果需要 MFA、密码重置或角色管理,请使用完整的身份验证提供商
相关模式
- 静态前端托管 — 此身份验证模式运行所依赖的托管基础设施
- 环境部署策略 — 环境如何影响身份验证配置
渐进式改进
如果开发者纠正了此技能本应避免的行为,请建议对技能进行具体修改,以防止今后再次出现同样的问题。
评论
支持 Markdown