Skip to content

Commit

Permalink
feat(mobile): ios oauth & magic-link login
Browse files Browse the repository at this point in the history
  • Loading branch information
CatsJuice committed Oct 23, 2024
1 parent 3f0219a commit 62f92b0
Show file tree
Hide file tree
Showing 15 changed files with 211 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "504EC3031FED79650016851F"
BuildableName = "App.app"
BlueprintName = "App"
ReferencedContainer = "container:App.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "504EC3031FED79650016851F"
BuildableName = "App.app"
BlueprintName = "App"
ReferencedContainer = "container:App.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "504EC3031FED79650016851F"
BuildableName = "App.app"
BlueprintName = "App"
ReferencedContainer = "container:App.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
17 changes: 15 additions & 2 deletions packages/frontend/apps/ios/App/App/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
Expand All @@ -20,8 +18,23 @@
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>None</string>
<key>CFBundleURLName</key>
<string>affine</string>
<key>CFBundleURLSchemes</key>
<array>
<string>affine</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>10</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchScreen</key>
Expand Down
3 changes: 2 additions & 1 deletion packages/frontend/apps/ios/App/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ install! 'cocoapods', :disable_input_output_paths => true
def capacitor_pods
pod 'Capacitor', :path => '../../../../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../../../../node_modules/@capacitor/ios'

pod 'CapacitorApp', :path => '../../../../../node_modules/@capacitor/app'
pod 'CapacitorBrowser', :path => '../../../../../node_modules/@capacitor/browser'
end

target 'App' do
Expand Down
14 changes: 13 additions & 1 deletion packages/frontend/apps/ios/App/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,22 +1,34 @@
PODS:
- Capacitor (6.1.2):
- CapacitorCordova
- CapacitorApp (6.0.1):
- Capacitor
- CapacitorBrowser (6.0.3):
- Capacitor
- CapacitorCordova (6.1.2)

DEPENDENCIES:
- "Capacitor (from `../../../../../node_modules/@capacitor/ios`)"
- "CapacitorApp (from `../../../../../node_modules/@capacitor/app`)"
- "CapacitorBrowser (from `../../../../../node_modules/@capacitor/browser`)"
- "CapacitorCordova (from `../../../../../node_modules/@capacitor/ios`)"

EXTERNAL SOURCES:
Capacitor:
:path: "../../../../../node_modules/@capacitor/ios"
CapacitorApp:
:path: "../../../../../node_modules/@capacitor/app"
CapacitorBrowser:
:path: "../../../../../node_modules/@capacitor/browser"
CapacitorCordova:
:path: "../../../../../node_modules/@capacitor/ios"

SPEC CHECKSUMS:
Capacitor: 679f9673fdf30597493a6362a5d5bf233d46abc2
CapacitorApp: 0bc633b4eae40a1f32cd2834788fad3bc42da6a1
CapacitorBrowser: aab1ed943b01c0365c4810538a8b3477e2d9f72e
CapacitorCordova: f48c89f96c319101cd2f0ce8a2b7449b5fb8b3dd

PODFILE CHECKSUM: 54b94ef731578bd3a2af3619f2a5a0589e32dea5
PODFILE CHECKSUM: 0f32d90fb8184cf478f85b78b1c00db1059ac3aa

COCOAPODS: 1.15.2
11 changes: 11 additions & 0 deletions packages/frontend/apps/ios/capacitor.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ const config: CapacitorConfig = {
ios: {
path: '.',
},
server: {
// url: 'http://localhost:8080',
},
plugins: {
CapacitorCookies: {
enabled: true,
},
CapacitorHttp: {
enabled: true,
},
},
};

export default config;
2 changes: 2 additions & 0 deletions packages/frontend/apps/ios/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"@affine/i18n": "workspace:*",
"@blocksuite/affine": "0.17.19",
"@blocksuite/icons": "^2.1.67",
"@capacitor/app": "^6.0.1",
"@capacitor/browser": "^6.0.3",
"@capacitor/core": "^6.1.2",
"@capacitor/ios": "^6.1.2",
"@sentry/react": "^8.0.0",
Expand Down
35 changes: 35 additions & 0 deletions packages/frontend/apps/ios/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Telemetry } from '@affine/core/components/telemetry';
import { configureMobileModules } from '@affine/core/mobile/modules';
import { router } from '@affine/core/mobile/router';
import { configureCommonModules } from '@affine/core/modules';
import { AuthService } from '@affine/core/modules/cloud';
import { I18nProvider } from '@affine/core/modules/i18n';
import { configureLocalStorageStateStorageImpls } from '@affine/core/modules/storage';
import { configureIndexedDBUserspaceStorageProvider } from '@affine/core/modules/userspace';
Expand All @@ -12,6 +13,8 @@ import {
configureBrowserWorkspaceFlavours,
configureIndexedDBWorkspaceEngineStorageProvider,
} from '@affine/core/modules/workspace-engine';
import { App as CapacitorApp } from '@capacitor/app';
import { Browser } from '@capacitor/browser';
import {
Framework,
FrameworkRoot,
Expand Down Expand Up @@ -41,6 +44,38 @@ window.addEventListener('focus', () => {
});
frameworkProvider.get(LifecycleService).applicationStart();

CapacitorApp.addListener('appUrlOpen', ({ url }) => {
// try to close browser if it's open
Browser.close().catch(e => console.error('Failed to close browser', e));

const urlObj = new URL(url);

if (urlObj.hostname === 'authentication') {
const method = urlObj.searchParams.get('method');
const payload = JSON.parse(urlObj.searchParams.get('payload') ?? 'false');

if (
!method ||
(method !== 'magic-link' && method !== 'oauth') ||
!payload
) {
console.error('Invalid authentication url', url);
return;
}

const authService = frameworkProvider.get(AuthService);
if (method === 'oauth') {
authService
.signInOauth(payload.code, payload.state, payload.provider)
.catch(console.error);
} else if (method === 'magic-link') {
authService
.signInMagicLink(payload.email, payload.token)
.catch(console.error);
}
}
});

export function App() {
return (
<Suspense>
Expand Down
2 changes: 2 additions & 0 deletions packages/frontend/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
"@affine/track": "workspace:*",
"@blocksuite/affine": "0.17.19",
"@blocksuite/icons": "2.1.69",
"@capacitor/app": "^6.0.1",
"@capacitor/browser": "^6.0.3",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/modifiers": "^7.0.0",
"@dnd-kit/sortable": "^8.0.0",
Expand Down
7 changes: 7 additions & 0 deletions packages/frontend/core/src/components/affine/auth/oauth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ function OAuthProvider({ provider }: { provider: OAuthProviderType }) {
oauthUrl += `&client=${appInfo?.schema}`;
}

if (BUILD_CONFIG.isIOS) {
// app scheme: "affine"
oauthUrl += `&client=affine`;
}
// TODO: Android app scheme not implemented
// if (BUILD_CONFIG.isAndroid) {}

popupWindow(oauthUrl);
}, [provider]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,11 @@ const useSendEmail = (emailType: AuthPanelProps<'sendEmail'>['emailType']) => {
return trigger({
email,
callbackUrl: `/auth/${callbackUrl}?isClient=${
BUILD_CONFIG.isElectron ? 'true' : 'false'
BUILD_CONFIG.isElectron ||
BUILD_CONFIG.isIOS ||
BUILD_CONFIG.isAndroid
? 'true'
: 'false'
}`,
});
},
Expand Down
4 changes: 4 additions & 0 deletions packages/frontend/core/src/mobile/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ export const topLevelRoutes = [
path: '/redirect-proxy',
lazy: () => import('@affine/core/desktop/pages/redirect'),
},
{
path: '/open-app/:action',
lazy: () => import('@affine/core/desktop/pages/open-app'),
},
{
path: '*',
lazy: () => import('./pages/404'),
Expand Down
7 changes: 6 additions & 1 deletion packages/frontend/core/src/modules/cloud/services/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,18 @@ export class AuthService extends Service {
) {
track.$.$.auth.signIn({ method: 'magic-link' });
try {
const scheme = BUILD_CONFIG.isElectron
? appInfo?.schema
: BUILD_CONFIG.isIOS || BUILD_CONFIG.isAndroid
? 'affine'
: 'web';
await this.fetchService.fetch('/api/auth/sign-in', {
method: 'POST',
body: JSON.stringify({
email,
// we call it [callbackUrl] instead of [redirect_uri]
// to make it clear the url is used to finish the sign-in process instead of redirect after signed-in
callbackUrl: `/magic-link?client=${BUILD_CONFIG.isElectron ? appInfo?.schema : 'web'}`,
callbackUrl: `/magic-link?client=${scheme}`,
}),
headers: {
'content-type': 'application/json',
Expand Down
6 changes: 6 additions & 0 deletions packages/frontend/core/src/utils/popup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DebugLogger } from '@affine/debug';
import { apis } from '@affine/electron-api';
import { Browser } from '@capacitor/browser';

const logger = new DebugLogger('popup');

Expand Down Expand Up @@ -35,6 +36,11 @@ export function popupWindow(target: string) {
apis?.ui.openExternal(url).catch(e => {
logger.error('Failed to open external URL', e);
});
} else if (BUILD_CONFIG.isIOS || BUILD_CONFIG.isAndroid) {
Browser.open({
url,
presentationStyle: 'popover',
}).catch(console.error);
} else {
window.open(url, '_blank', `noreferrer noopener`);
}
Expand Down
3 changes: 3 additions & 0 deletions tools/cli/src/bin/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ const buildFlags = process.argv.includes('--static')
{
value: 'mobile',
},
{
value: 'ios',
},
],
initialValue: 'web',
}),
Expand Down
22 changes: 22 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,8 @@ __metadata:
"@affine/track": "workspace:*"
"@blocksuite/affine": "npm:0.17.19"
"@blocksuite/icons": "npm:2.1.69"
"@capacitor/app": "npm:^6.0.1"
"@capacitor/browser": "npm:^6.0.3"
"@dnd-kit/core": "npm:^6.1.0"
"@dnd-kit/modifiers": "npm:^7.0.0"
"@dnd-kit/sortable": "npm:^8.0.0"
Expand Down Expand Up @@ -596,6 +598,8 @@ __metadata:
"@affine/i18n": "workspace:*"
"@blocksuite/affine": "npm:0.17.19"
"@blocksuite/icons": "npm:^2.1.67"
"@capacitor/app": "npm:^6.0.1"
"@capacitor/browser": "npm:^6.0.3"
"@capacitor/cli": "npm:^6.1.2"
"@capacitor/core": "npm:^6.1.2"
"@capacitor/ios": "npm:^6.1.2"
Expand Down Expand Up @@ -2908,6 +2912,24 @@ __metadata:
languageName: node
linkType: hard

"@capacitor/app@npm:^6.0.1":
version: 6.0.1
resolution: "@capacitor/app@npm:6.0.1"
peerDependencies:
"@capacitor/core": ^6.0.0
checksum: 10/3e8bc85c3a43728c003c80511dde913872b1263dbc27931b1168f52442b8d95f35e5a6a85423d6ac61253a5e4b2198bbb7b899cae9f5e343edfed24010399ade
languageName: node
linkType: hard

"@capacitor/browser@npm:^6.0.3":
version: 6.0.3
resolution: "@capacitor/browser@npm:6.0.3"
peerDependencies:
"@capacitor/core": ^6.0.0
checksum: 10/e19e66c12b5c3a05e0c4a93715c0bec760781ddeb215a93da0a9ee3e9e4ede2f2893d399ffb8d7b5ded6ac70651ab482689ff57e5e0d067cd4398cc3247c4f81
languageName: node
linkType: hard

"@capacitor/cli@npm:^6.1.2":
version: 6.1.2
resolution: "@capacitor/cli@npm:6.1.2"
Expand Down

0 comments on commit 62f92b0

Please sign in to comment.