Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,5 @@ frontend/*.local

# Build caching apparently?
*.tsbuildinfo

**/.claude/settings.local.json
10 changes: 8 additions & 2 deletions frontend/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"mdast-util-gfm-autolink-literal": "2.0.0"
},
"dependencies": {
"@opensecret/react": "1.2.0",
"@opensecret/react": "1.3.1",
"@radix-ui/react-alert-dialog": "^1.1.1",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-dialog": "^1.1.1",
Expand Down Expand Up @@ -46,7 +46,8 @@
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7"
"tailwindcss-animate": "^1.0.7",
"uuid": "^11.1.0"
},
"devDependencies": {
"@eslint/js": "^9.14.0",
Expand All @@ -59,6 +60,7 @@
"@types/node": "^22.3.0",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/uuid": "^10.0.0",
"@vitejs/plugin-react": "^4.3.1",
"autoprefixer": "^10.4.20",
"eslint": "^9.8.0",
Expand Down
13 changes: 13 additions & 0 deletions frontend/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ tauri-plugin = "2.1.1"
tauri-plugin-deep-link = "2"
tauri-plugin-opener = "2"
tauri-plugin-os = "2"
tauri-plugin-sign-in-with-apple = "1.0.2"
tokio = { version = "1.0", features = ["time"] }
once_cell = "1.18.0"
102 changes: 102 additions & 0 deletions frontend/src-tauri/apple-sign-in-info.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Sign in with Apple Integration

## Overview
This document provides a comprehensive guide to the Sign in with Apple integration for Maple, supporting both native iOS authentication and web-based OAuth.

## Integration Types

### 1. Native iOS Authentication
- Uses the iOS native Sign In with Apple dialog
- Implemented via `tauri-plugin-sign-in-with-apple` (version 1.0.0)
- Returns user credentials directly to the app
- Provides access to user identifiers, email, and name (only on first sign-in)

### 2. Web-based OAuth Flow
- Similar to GitHub and Google OAuth flow
- Redirects users to Apple's authentication page
- Supports both web and desktop (non-iOS) platforms
- Handles callback with auth code and state verification

## Configuration

### iOS Native Auth
1. Required entitlements have been added in `maple_iOS.entitlements`
2. The capability is registered in `capabilities/default.json` and `capabilities/mobile-ios.json`
3. Plugin is configured in Cargo.toml and registered in the app

### OAuth Configuration
1. Set up these parameters in your OpenSecret project settings:
- **Client ID**: Your Apple Services ID (e.g., com.example.web)
- **Client Secret**: The base64-encoded contents of your Apple private key (p8 file)
- **Redirect URI**: Configure as `https://api.opensecret.cloud/auth/apple/callback`

## Implementation Details

### Frontend Integration

#### iOS Native Flow
```typescript
// iOS native authentication
const result = await invoke("plugin:sign-in-with-apple|get_apple_id_credential", {
payload: {
scope: ["email", "fullName"],
state: "apple-auth-state",
options: { debug: true }
}
});

// Format and send to backend
const appleUser = {
user_identifier: result.user,
identity_token: result.identityToken,
email: result.email,
given_name: result.fullName?.givenName,
family_name: result.fullName?.familyName
};

// Call OpenSecret SDK
await os.handleAppleNativeSignIn(appleUser, inviteCode);
```

#### Web OAuth Flow
```typescript
// Web OAuth authentication
const { auth_url } = await os.initiateAppleAuth(inviteCode);
window.location.href = auth_url;

// Callback handling (in separate component)
await handleAppleCallback(code, state, inviteCode);
```

### Platform Detection
The app automatically determines the appropriate flow:
1. Checks if the app is running on iOS and uses native flow
2. Checks if running in a Tauri environment (desktop) and uses the desktop auth flow
3. Uses the web OAuth flow for all other cases

## Callback Handling
For the web OAuth flow, callbacks are handled in `auth.$provider.callback.tsx`:
1. Extracts code and state from URL parameters
2. Verifies auth state to prevent CSRF attacks
3. Processes the authentication with the backend
4. Redirects to appropriate page after successful authentication

## Debugging
If you experience issues with Sign in with Apple:
1. Check debug logs (enabled by default in both flows)
2. Verify that iOS entitlements are properly configured (for native flow)
3. For OAuth flow, check if Apple Developer account is properly set up
4. Verify that the OpenSecret project settings are correctly configured

## Apple Developer Setup
To use Sign in with Apple, you need:
1. An Apple Developer account
2. An App ID with "Sign In with Apple" capability
3. A Services ID for web authentication
4. A Bundle ID for iOS apps
5. A private key for JWT token signing

## Resources
- [Apple Developer Documentation](https://developer.apple.com/documentation/sign_in_with_apple)
- [OpenSecret Apple Auth API](https://docs.opensecret.cloud/docs/guides/authentication)
- [tauri-plugin-sign-in-with-apple](https://crates.io/crates/tauri-plugin-sign-in-with-apple)
3 changes: 2 additions & 1 deletion frontend/src-tauri/capabilities/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"url": "https://trymaple.ai/*"
}
]
}
},
"sign-in-with-apple:default"
]
}
25 changes: 25 additions & 0 deletions frontend/src-tauri/capabilities/mobile-ios.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "../gen/schemas/mobile-schema.json",
"identifier": "mobile-ios",
"description": "Capabilities for iOS",
"windows": [
"main"
],
"platforms": ["iOS"],
"permissions": [
"core:default",
"updater:default",
{
"identifier": "opener:allow-open-url",
"allow": [
{
"url": "http://localhost:5173/*"
},
{
"url": "https://trymaple.ai/*"
}
]
},
"sign-in-with-apple:default"
]
}
4 changes: 2 additions & 2 deletions frontend/src-tauri/gen/apple/maple_iOS/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.4</string>
<string>1.0.5</string>
<key>CFBundleVersion</key>
<string>1.0.4</string>
<string>1.0.5</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src-tauri/gen/apple/maple_iOS/maple_iOS.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:trymaple.ai</string>
Expand Down
14 changes: 12 additions & 2 deletions frontend/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use tauri::Emitter;
use tauri_plugin_deep_link::DeepLinkExt;
use tauri_plugin_opener;
use tauri_plugin_sign_in_with_apple;

// This handles incoming deep links
fn handle_deep_link_event(url: &str, app: &tauri::AppHandle) {
Expand Down Expand Up @@ -184,15 +185,24 @@ pub fn run() {
.plugin(tauri_plugin_updater::Builder::new().build());

#[cfg(not(desktop))]
let app = tauri::Builder::default()
let mut builder = tauri::Builder::default()
.plugin(
tauri_plugin_log::Builder::default()
.level(log::LevelFilter::Info)
.build(),
)
.plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_os::init());

// Only add the Apple Sign In plugin on iOS
#[cfg(all(not(desktop), target_os = "ios"))]
{
builder = builder.plugin(tauri_plugin_sign_in_with_apple::init());
}

#[cfg(not(desktop))]
let app = builder
.setup(|app| {
// Set up the deep link handler for mobile
let app_handle = app.handle().clone();
Expand Down
14 changes: 12 additions & 2 deletions frontend/src/components/Marketing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,18 @@ export function Marketing() {
useEffect(() => {
const checkPlatform = async () => {
try {
const platform = await type();
setIsIOS(platform === "ios");
// First check if we're in a Tauri environment
const isTauriEnv = await import("@tauri-apps/api/core")
.then((m) => m.isTauri())
.catch(() => false);

if (isTauriEnv) {
// Only check platform type if we're in a Tauri environment
const platform = await type();
setIsIOS(platform === "ios");
} else {
setIsIOS(false);
}
} catch (error) {
console.error("Error checking platform:", error);
setIsIOS(false);
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/components/icons/Apple.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";

export function Apple(props: React.SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 384 512"
fill="currentColor"
{...props}
>
<path d="M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 49.9-11.4 69.5-34.3z" />
</svg>
);
}
Loading
Loading