From b784f94a6467ce7e91bf7aede8a7aac046298878 Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Mon, 9 Feb 2026 23:23:12 -0500 Subject: [PATCH] implement deep link handling for OAuth authentication https://github.com/donetick/donetick/issues/504 --- android/app/src/main/AndroidManifest.xml | 8 ++++ ios/App/App/Info.plist | 8 +++- src/CapacitorListener.js | 45 ++++++++++++++++++++++ src/views/Authorization/Authenticating.jsx | 7 +--- 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 13e1d3e..837d403 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -19,6 +19,14 @@ + + + + + + + + UIInterfaceOrientationLandscapeRight UIViewControllerBasedStatusBarAppearance + + CFBundleURLTypes CFBundleURLName - + com.donetick.app + CFBundleURLSchemes + + donetick + NSCameraUsageDescription diff --git a/src/CapacitorListener.js b/src/CapacitorListener.js index cbb8071..c1facf2 100644 --- a/src/CapacitorListener.js +++ b/src/CapacitorListener.js @@ -1,10 +1,44 @@ import { App as mobileApp } from '@capacitor/app' +import { Browser } from '@capacitor/browser' import { Capacitor } from '@capacitor/core' import { Device } from '@capacitor/device' import { LocalNotifications } from '@capacitor/local-notifications' import { Preferences } from '@capacitor/preferences' import { PushNotifications } from '@capacitor/push-notifications' import { RegisterDeviceToken } from './utils/Fetcher' + +// OAuth callback handler for deep links +const handleOAuthDeepLink = async url => { + console.log('OAuth deep link received:', url) + + try { + // Parse the URL to extract code and state + const urlObj = new URL(url) + const code = urlObj.searchParams.get('code') + const state = urlObj.searchParams.get('state') + + if (code && state) { + // Store the OAuth params for the app to pick up + await Preferences.set({ + key: 'oauth_callback', + value: JSON.stringify({ code, state, timestamp: Date.now() }), + }) + + // Close the browser if it's still open + try { + await Browser.close() + } catch (e) { + // Browser might already be closed + } + + // Navigate to the OAuth handler page + window.location.href = `/auth/oauth2?code=${encodeURIComponent(code)}&state=${encodeURIComponent(state)}` + } + } catch (error) { + console.error('Error handling OAuth deep link:', error) + } +} + const localNotificationListenerRegistration = () => { LocalNotifications.addListener('localNotificationReceived', notification => { console.log('Notification received', notification) @@ -180,6 +214,17 @@ const registerCapacitorListeners = () => { return } localNotificationListenerRegistration() + + // Register deep link handler for OAuth and other deep links + mobileApp.addListener('appUrlOpen', event => { + console.log('App URL opened:', event.url) + + // Handle OAuth callback + if (event.url.startsWith('donetick://auth/')) { + handleOAuthDeepLink(event.url) + } + }) + mobileApp.addListener('backButton', ({ canGoBack }) => { if (canGoBack) { window.history.back() diff --git a/src/views/Authorization/Authenticating.jsx b/src/views/Authorization/Authenticating.jsx index 4a67a2c..e811b45 100644 --- a/src/views/Authorization/Authenticating.jsx +++ b/src/views/Authorization/Authenticating.jsx @@ -60,11 +60,7 @@ const AuthenticationLoading = () => { if (code) { const baseURL = apiClient.getApiURL() - console.log( - '{baseURL}/auth/oauth2/callback', - `${baseURL}/auth/oauth2/callback`, - ) - fetch(`${baseURL}/auth/${provider}/callback`, { + fetch(`${baseURL}/auth/oauth2/callback`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -72,6 +68,7 @@ const AuthenticationLoading = () => { body: JSON.stringify({ code, state: returnedState, + redirect_uri: `${window.location.origin}/auth/oauth2`, }), }).then(response => { if (response.status === 200) {