Angular应用中Apple Login在Safari浏览器无法正常工作
Apple登录在Safari中无法传递redirect URI的解决方案
问题根源
Safari对Apple登录有特殊处理机制:会拦截window.open触发的跳转,替换为原生的「Sign in with Apple」触控弹窗。这种情况下,原逻辑依赖的window.opener关联会失效,导致redirect页面无法通过postMessage向主页面传递code等数据。
解决方案1:修复现有逻辑并适配Safari场景
第一步:修正Angular代码的语法错误
原代码存在字符串拼接语法错误,且未对redirect_uri做URL编码,先修复这两点:
// 原错误代码 // const openNewWindow = window.open( 'https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&scope=name%20email&state=W4cL2JgRJq&client_id=CLIENT_ID&redirect_uri='+ this.AppleURL',"_blank" ); // 修正后代码 try { const authUrl = `https://appleid.apple.com/auth/authorize?response_type=code&response_mode=form_post&scope=name%20email&state=W4cL2JgRJq&client_id=CLIENT_ID&redirect_uri=${encodeURIComponent(this.AppleURL)}`; const openNewWindow = window.open(authUrl, "_blank"); // 移除重复的message监听,避免多次触发 const signInWithAppleHandler = (event) => { try { this.signInWithApple(JSON.parse(event.data)); window.removeEventListener('message', signInWithAppleHandler); window.removeEventListener('storage', storageHandler); } catch (e) { console.error('解析登录数据失败', e); } }; window.addEventListener('message', signInWithAppleHandler); // 新增storage监听,适配Safari原生弹窗场景 const storageHandler = (event) => { if (event.key === 'appleLoginData') { const data = JSON.parse(event.newValue); this.signInWithApple(data); localStorage.removeItem('appleLoginData'); window.removeEventListener('storage', storageHandler); window.removeEventListener('message', signInWithAppleHandler); } }; window.addEventListener('storage', storageHandler); if (openNewWindow) { openNewWindow.opener = window; } else { // 若window.open被拦截(Safari原生弹窗触发),直接在当前页面跳转 window.location.href = authUrl; } } catch (error) { console.error("打开Apple登录页面失败", error); }
第二步:修改重定向页面的传递逻辑
在redirect页面中增加localStorage fallback逻辑,应对Safari无法获取window.opener的情况:
<script> function proceedPostToken() { var data = { "code": '{{apple_code}}', "firstName": '{{first_name}}', "lastName": '{{last_name}}'}; if (window.ReactNativeWebView) { window.ReactNativeWebView.postMessage(JSON.stringify(data)); window.close(); } else if (window.opener) { window.opener.postMessage(JSON.stringify(data), '*'); window.close(); } else { // Safari原生弹窗场景,用localStorage传递数据 localStorage.setItem('appleLoginData', JSON.stringify(data)); // 尝试关闭弹窗,若无效则跳转回主页面(需保证同域) window.close() || (window.location.href = '你的主页面地址'); } } proceedPostToken(); </script>
关键注意点
- 主页面和redirect URI必须同域,否则
localStorage和window.opener都会被浏览器的同源策略限制。 - 确保Apple开发者后台中配置的redirect URI和代码中完全一致(包括编码后的格式)。
解决方案2:使用Apple官方JS SDK(推荐)
官方SDK会自动适配所有浏览器的差异,包括Safari的原生弹窗,无需手动处理跳转和数据传递逻辑:
- 在页面引入SDK:
<script type="text/javascript" src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
- Angular中调用登录:
// 初始化授权配置 const appleAuth = new window.AppleID.auth({ clientId: 'CLIENT_ID', scope: 'name email', redirectURI: this.AppleURL, state: 'W4cL2JgRJq' }); // 发起登录请求 appleAuth.signIn().then((response) => { // 直接获取code和用户信息 this.signInWithApple({ code: response.code, firstName: response.user?.name?.firstName, lastName: response.user?.name?.lastName }); }).catch((error) => { console.error('Apple登录失败', error); });
这种方式无需依赖redirect页面的postMessage或localStorage,SDK会自动处理所有兼容性问题。
内容的提问来源于stack exchange,提问作者aman singh
相关产品推荐
相关产品推荐

