PKCE Flow errors: 'cannot parse response' or '#ZgotmplZ' in magic link emails
在使用魔法链接和移动深度链接设置身份验证时,你可能会在邮件模板中遇到像 #ZgotmplZ 的特定错误,或者在登录流程中遇到“无法解析响应”的错误。本指南将解释这些问题的根本原因,并提供一个可靠的解决方案。
🌐 When setting up authentication with magic links and mobile deep linking, you might encounter specific errors like #ZgotmplZ in your email templates or a 'cannot parse response' error during the login flow. This guide explains the underlying causes and provides a robust solution.
PKCE 流程是什么? #
🌐 What is PKCE flow?
PKCE(用于代码交换的证明密钥)是 OAuth 2.0 授权码流程的一个安全扩展,专门为像移动应用这样的公共客户端设计。它通过要求客户端生成一个密钥(“代码验证器”)来防止拦截攻击,这个密钥会被哈希处理并在初始授权请求时发送给授权服务器。之后,当客户端用授权码换取访问令牌时,这个哈希(“代码挑战”)会与实际的代码验证器进行对比。它就像是你的移动应用和认证服务之间的一个安全“握手”。
🌐 PKCE (Proof Key for Code Exchange) is a security extension to the OAuth 2.0 Authorization Code Flow, specifically designed for public clients like mobile apps. It prevents interception attacks by requiring the client to generate a secret (a "code verifier") which is hashed and sent to the authorization server during the initial authorization request. This hash (the "code challenge") is later compared to the actual code verifier when the client exchanges the authorization code for an access token. It acts as a secure "handshake" between your mobile application and the authentication service.
什么是魔法链接? #
🌐 What are magic links?
魔术链接是一种无密码认证方式,用户会通过邮箱收到一个独特的、时效性的链接。点击这个链接就可以直接登录应用,无需输入密码。
🌐 Magic links are a passwordless authentication method where users receive a unique, time-sensitive link via email. Clicking this link directly logs them into the application without needing a password.
Go 模板安全性和 {{ .SiteURL }}#
🌐 What is go template security and {{ .SiteURL }}?
电子邮件模板系统通常使用像 Go 这样的语言,它自带安全功能,可以防止跨站脚本攻击(XSS)和其他漏洞。当像 {{ .SiteURL }} 这样的变量(用于表示你应用的主 URL)在邮件模板中使用时,Go 的安全模型会自动清理输出。如果 {{ .SiteURL }} 提供的 URL 没有以像 http:// 或 https://(例如 your-app-scheme://)这样的安全方案开头,Go 会认为它可能是恶意的。为了保护用户,它会用一个占位符替换不安全的链接,通常是 #ZgotmplZ。
🌐 The email templating system often uses a language like Go, which has built-in security features to prevent cross-site scripting (XSS) and other vulnerabilities. When a variable like {{ .SiteURL }} (intended to represent your application's primary URL) is used in an email template, Go's security model automatically sanitizes the output. If the URL provided in {{ .SiteURL }} does not start with a recognized safe scheme like http:// or https:// (e.g., your-app-scheme://), Go considers it potentially malicious. To protect users, it replaces the unsafe link with a placeholder, typically #ZgotmplZ.
理解问题 #
🌐 Understanding the problem
你可能会注意到两个不同的问题:
🌐 You may observe two distinct issues:
#ZgotmplZ错误:- 原因:当你试图在邮件模板中直接使用非标准 URL 方案(比如针对移动应用深度链接的
your-app-scheme://)作为模板变量(比如{{ .SiteURL }})时,就会出现这个错误。Go 的安全功能会对这个被认为“不安全”的 URL 进行清理,用#ZgotmplZ占位符替换它。这会导致链接无法正确显示,从而无法使用。
- 原因:当你试图在邮件模板中直接使用非标准 URL 方案(比如针对移动应用深度链接的
- “无法解析响应”错误:
- 原因1:PKCE 握手失败:这个错误通常发生在身份验证流程,尤其是 PKCE 被中断的时候。如果邮件客户端试图在它自己的内部浏览器中打开魔法链接,就可能会打断你的移动应用和身份验证服务之间的“握手”过程。移动应用期望收到特定参数来完成 PKCE 流程,如果因为中间的邮件浏览器导致这些参数没有正确传递,应用就会报出“无法解析响应”的错误。
- 原因 2:电子邮件链接扫描器:许多电子邮件提供商和安全工具会自动扫描或“预先点击”邮件中的链接,以检查是否存在恶意内容。对于一次性魔法链接来说,这种自动扫描可能会在合法用户点击链接之前就消耗掉认证令牌。当用户随后点击(此时已被消耗的)链接时,认证尝试就会失败,从而出现“无法解析响应”的错误。这可能导致偶尔的失败,即使总体流程大多数时候看起来是正常的。
解决问题:推荐流程(邮件 → 网站 → 移动应用) #
🌐 Resolving the problem: The recommended flow (email → website → mobile app)
为了实现既可靠又安全的身份验证体验,同时兼顾 Go 的安全模型以及移动深度链接和 PKCE 的复杂性,推荐的做法是多步骤重定向:邮箱 → 网站 → 移动应用。
🌐 To achieve a reliable and secure authentication experience that accommodates both Go's security model and the complexities of mobile deep linking and PKCE, the recommended approach is a multi-step redirection: Email → Website → Mobile App.
下面是如何实现这个解决方案的:
🌐 Here’s how to implement this solution:
第1步:配置你的认证项目 #
🌐 Step 1: Configure your authentication project
确保你的认证服务(例如 Supabase)已正确配置,将你的网站作为主要回调目标。
🌐 Ensure your authentication service (e.g., Supabase) is correctly configured to use your website as the primary callback destination.
- 设置你项目的
SITE_URL:将此设置更新为你网站的主要域名。这应该是一个标准的网页 URL(例如,https://example.com)。这样可以确保{{ .SiteURL }}和{{ .ConfirmationURL }}生成安全的网页标准链接。 - 将你的移动深度链接方案添加到
Additional Redirect URLs:在授权重定向 URL 列表中包含你的移动应用深度链接方案及通配符(例如your-app-scheme://*)。这会告诉身份验证服务,在初始网页重定向之后,你的应用方案是有效的跳转目标。
第2步:更新你的邮件模板 #
🌐 Step 2: Update your email template
现在你的 SITE_URL 已经正确设置为一个网站域名,你可以安全地使用内置变量了。
🌐 Now that your SITE_URL is correctly set to a web domain, you can safely use the built-in variables.
- 使用
{{ .ConfirmationURL }}创建魔法链接:在你的邮件模板中,使用{{ .ConfirmationURL }}。这个变量会生成一个指向你配置的SITE_URL的安全网页链接,通常是https://example.com/auth/callback或类似的路径。由于它使用标准的https://协议,这个链接是不受 Go 的清理机制影响的。
步骤 3:实现一个带用户操作的中介网页 #
🌐 Step 3: Implement an intermediary web page with user action
这是解决断裂的 PKCE 握手和邮箱链接扫描器问题的最关键一步。
🌐 This is the most crucial step to resolve both the broken PKCE handshake and the email link scanner issues.
- 创建网页回调页面:在你的
SITE_URL(例如,https://example.com/auth/callback)上创建一个网页。当用户点击邮件中的魔法链接时,这个页面就是他们的初始目的地。 - 添加用户触发按钮:在此回调页面上,加入一个用户 必须 点击才能继续的按钮或明确操作。例如,一个标注为“验证并打开应用”的按钮。
- 为什么这很重要:
- 它可以防止电子邮件链接扫描器自动触发最终深度链接,确保一次性令牌不会被过早使用。
- 它确保用户使用的是标准的网络浏览器(比如 Chrome 或 Safari),这样才能正确处理深度链接重定向,而不是使用可能会破坏 PKCE 流程的邮件客户端内置浏览器。
- 为什么这很重要:
- 实现深度链接重定向:当用户点击“验证并打开应用”按钮时,执行一个脚本,将他们重定向到你的移动应用,使用它的深度链接。例如,
window.location.href = "your-app-scheme://login-callback?code=...",其中code会包含网页回调页面收到的任何必要的认证参数。
通过遵循这个电子邮件 → 网站 → 移动应用的流程,并在网页上进行用户主动操作,你可以建立一个稳健且安全的认证过程,绕开与邮件客户端和链接扫描器相关的常见问题,同时确保用户体验的一致性。
🌐 By following this Email → Website → Mobile App flow with a user-initiated action on the web page, you establish a robust and secure authentication process that sidesteps common pitfalls associated with email clients and link scanners, ensuring a consistent user experience.