implement deep link handling for OAuth authentication

https://github.com/donetick/donetick/issues/504
This commit is contained in:
Mo Tarbin
2026-02-09 23:23:12 -05:00
parent 34c382daea
commit b784f94a64
4 changed files with 62 additions and 6 deletions

View File

@@ -19,6 +19,14 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Deep link intent filter for OAuth callback -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="donetick" />
</intent-filter>
</activity>
<provider

View File

@@ -42,10 +42,16 @@
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string></string>
<string>com.donetick.app</string>
<key>CFBundleURLSchemes</key>
<array>
<string>donetick</string>
</array>
</dict>
</array>
<key>NSCameraUsageDescription</key>

View File

@@ -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()

View File

@@ -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) {