IOSAuth.ts 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. import { IOS } from "cc/env";
  2. import { native } from 'cc';
  3. export interface IOSAuthResult {
  4. status: 'connected' | 'cancelled' | 'error';
  5. isCancel: boolean;
  6. identityToken?: string;
  7. authorizationCode?: string;
  8. userId?: string;
  9. name?: string;
  10. email?: string;
  11. nonce?: string;
  12. reason?: string;
  13. }
  14. class IOSAuth {
  15. private listening = false;
  16. private seed = 0;
  17. private pending: { id: string; resolve: (result: IOSAuthResult) => void; timer: ReturnType<typeof setTimeout> } | null = null;
  18. init(): void {
  19. if (this.listening) return;
  20. native.jsbBridgeWrapper.addNativeEventListener('appleauth.response', payload => {
  21. let response: { requestId: string; data: IOSAuthResult };
  22. try { response = JSON.parse(payload); } catch { return; }
  23. const pending = this.pending;
  24. if (!pending || response.requestId !== pending.id) return;
  25. clearTimeout(pending.timer);
  26. this.pending = null;
  27. pending.resolve(response.data);
  28. });
  29. this.listening = true;
  30. }
  31. /** nonce 原样传给 Apple;生产环境建议使用后端预先生成并绑定本次登录的随机挑战。 */
  32. async login(nonce?: string): Promise<IOSAuthResult> {
  33. const failure = (reason: string): IOSAuthResult => ({ status: 'error', isCancel: false, reason });
  34. if (this.pending) return failure('Apple 登录正在进行中。');
  35. if (nonce !== undefined && (!nonce.trim() || nonce.length > 512)) return failure('nonce 无效。');
  36. try { this.init(); } catch (error) { return failure(String(error)); }
  37. const id = `${Date.now()}-${++this.seed}`;
  38. return new Promise(resolve => {
  39. const timer = setTimeout(() => {
  40. this.pending = null;
  41. resolve(failure('Apple 授权等待超时,请关闭系统授权窗口后重试。'));
  42. }, 180000);
  43. this.pending = { id, resolve, timer };
  44. try {
  45. native.jsbBridgeWrapper.dispatchEventToNative('appleauth.request', JSON.stringify({ requestId: id, nonce }));
  46. } catch (error) {
  47. clearTimeout(timer); this.pending = null; resolve(failure(String(error)));
  48. }
  49. });
  50. }
  51. }
  52. const iosAuth = IOS ? new IOSAuth : null;
  53. export default iosAuth;