despia-base44-oauth 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 despia-native
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,240 @@
1
+ # despia-base44-oauth
2
+
3
+ Native-safe OAuth for React + Base44 apps running inside the **Despia native runtime**. Uses Despia's secure browser transport (iOS: ASWebAuthenticationSession, Android: Chrome Custom Tabs) and the required `oauth/` deeplink prefix to close the browser and return tokens to your WebView.
4
+
5
+ ## Prerequisites
6
+
7
+ Your app must have these installed:
8
+
9
+ | Package | Required | Notes |
10
+ |---------|----------|-------|
11
+ | `react` | Yes | 17+ |
12
+ | `react-router-dom` | Yes | 6+ |
13
+ | `despia-native` | Yes | Despia SDK – required for Despia native apps. You should already have this. |
14
+ | `@base44/sdk` | Yes | Base44 JavaScript SDK – for `base44.auth.loginWithProvider()` and `base44.auth.setToken()` |
15
+
16
+ **Important:** `despia-native` is **required** when your app runs in the Despia wrapper. Import it as the default export: `import despia from 'despia-native'`. Do not create mock implementations. This package uses `despia-native` internally for the `oauth://` protocol.
17
+
18
+ This package expects a Base44 client with `auth.loginWithProvider(provider, fromUrl?)` and `auth.setToken(token, saveToStorage?)`, matching the [@base44/sdk](https://www.npmjs.com/package/@base44/sdk) auth module. Supported providers: `google`, `microsoft`, `facebook`.
19
+
20
+ **Custom login required:** You must build a custom login page using Base44's framework. The prebuilt Base44 login UI does not support custom deeplink-based native OAuth integrations (Despia secure browser + `oauth/` deeplink handoff). Use `<Base44OAuthButton>` or `useBase44OAuth` in your own login page.
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ npm install despia-base44-oauth despia-native @base44/sdk
26
+ ```
27
+
28
+ ```bash
29
+ # Or with pnpm
30
+ pnpm add despia-base44-oauth despia-native @base44/sdk
31
+
32
+ # Or with yarn
33
+ yarn add despia-base44-oauth despia-native @base44/sdk
34
+ ```
35
+
36
+ ## Despia OAuth URL Protocols
37
+
38
+ This package uses Despia's two OAuth protocols:
39
+
40
+ | Protocol | Purpose |
41
+ |----------|---------|
42
+ | `oauth://?url={url}` | **Opens** secure browser (ASWebAuth / Chrome Custom Tabs) |
43
+ | `{scheme}://oauth/{path}?params` | **Closes** browser, returns to WebView. The `oauth/` segment is **required** – without it the browser stays open. |
44
+
45
+ Deeplink examples:
46
+ - `myapp://oauth/auth?access_token=xxx` ✓ Browser closes, app receives `/auth?access_token=xxx`
47
+ - `myapp://auth?access_token=xxx` ✗ Browser stays open (user stuck)
48
+
49
+ ## Setup
50
+
51
+ ### 1. Base44OAuthProvider
52
+
53
+ Wrap your app with `Base44OAuthProvider` and pass your Base44 client. The client must expose `auth.loginWithProvider(provider, fromUrl?)` and `auth.setToken(token, saveToStorage?)`.
54
+
55
+ **Inside Base44-generated apps** (client is pre-configured):
56
+
57
+ ```tsx
58
+ import { Base44OAuthProvider, OAuthRoutes } from 'despia-base44-oauth';
59
+ import { BrowserRouter, Routes, Route } from 'react-router-dom';
60
+ import { base44 } from '@/api/base44Client';
61
+
62
+ function App() {
63
+ return (
64
+ <Base44OAuthProvider base44={base44}>
65
+ <BrowserRouter>
66
+ <Routes>
67
+ <Route path="/" element={<Home />} />
68
+ <Route path="/login" element={<Login />} />
69
+ <Route path="/*" element={<OAuthRoutes deeplink="myapp" />} />
70
+ </Routes>
71
+ </BrowserRouter>
72
+ </Base44OAuthProvider>
73
+ );
74
+ }
75
+ ```
76
+
77
+ **External apps** (create client yourself):
78
+
79
+ ```tsx
80
+ import { Base44OAuthProvider, OAuthRoutes } from 'despia-base44-oauth';
81
+ import { createClient } from '@base44/sdk';
82
+ import { BrowserRouter, Routes, Route } from 'react-router-dom';
83
+
84
+ const base44 = createClient({ appId: 'your-app-id' });
85
+
86
+ function App() {
87
+ return (
88
+ <Base44OAuthProvider base44={base44}>
89
+ <BrowserRouter>
90
+ <Routes>
91
+ <Route path="/" element={<Home />} />
92
+ <Route path="/login" element={<Login />} />
93
+ <Route path="/*" element={<OAuthRoutes deeplink="myapp" />} />
94
+ </Routes>
95
+ </BrowserRouter>
96
+ </Base44OAuthProvider>
97
+ );
98
+ }
99
+ ```
100
+
101
+ ### 2. Custom login page
102
+
103
+ Create a custom login page and use `<Base44OAuthButton>` or `useBase44OAuth` for OAuth. The prebuilt Base44 login does not support the Despia native OAuth flow.
104
+
105
+ ### 3. OAuthRoutes
106
+
107
+ `<OAuthRoutes deeplink="myapp" />` registers these internal routes:
108
+
109
+ | Route | Where it runs | Purpose |
110
+ |-------|---------------|---------|
111
+ | `/__despia_oauth/start` | Secure browser (native) or web redirect | Initiates OAuth, calls Base44 `loginWithProvider` |
112
+ | `/__despia_oauth/finish` | Secure browser only | Parses tokens, redirects to `{scheme}://oauth/auth?access_token=...` |
113
+ | `/auth` (default) | WebView | Applies token via `base44.auth.setToken`, navigates to `next` |
114
+
115
+ Optional props:
116
+
117
+ ```tsx
118
+ <OAuthRoutes
119
+ deeplink="myapp"
120
+ applyPath="/auth"
121
+ loginPath="/login"
122
+ />
123
+ ```
124
+
125
+ ### 4. Configure Deeplink Scheme in Despia
126
+
127
+ Configure your Despia project so deeplinks with the `oauth/` prefix are intercepted and the WebView is navigated to the matching path with query params. Example:
128
+
129
+ - Deeplink: `myapp://oauth/auth?access_token=xxx&next=/&state=yyy`
130
+ - Despia intercepts → closes secure browser → navigates WebView to: `https://your-app.com/auth?access_token=xxx&next=/&state=yyy`
131
+
132
+ ## Usage
133
+
134
+ ### Base44OAuthButton
135
+
136
+ ```tsx
137
+ import { Base44OAuthButton } from 'despia-base44-oauth';
138
+
139
+ <Base44OAuthButton
140
+ deeplink="myapp"
141
+ provider="google"
142
+ next="/"
143
+ />
144
+ ```
145
+
146
+ **Props:**
147
+
148
+ | Prop | Type | Required | Description |
149
+ |------|------|----------|-------------|
150
+ | `deeplink` | string | yes | Deeplink scheme **without** `://` (e.g. `"myapp"`) |
151
+ | `provider` | string | yes | OAuth provider: `"google"`, `"microsoft"`, or `"facebook"` (per @base44/sdk) |
152
+ | `next` | string | no | Relative path after success (default: `"/"`) |
153
+ | `children` | ReactNode | no | Button label (default: `"Sign in with {provider}"`) |
154
+
155
+ **Behavior:**
156
+
157
+ - **Despia native runtime:** Calls `despia('oauth://?url=...')` to open secure browser, then loads the start route which triggers Base44 OAuth.
158
+ - **Web browser:** Calls `base44.auth.loginWithProvider(provider, next)` directly.
159
+
160
+ ### useBase44OAuth
161
+
162
+ Programmatic OAuth start:
163
+
164
+ ```tsx
165
+ import { useBase44OAuth } from 'despia-base44-oauth';
166
+
167
+ function MyComponent() {
168
+ const { start, busy, error } = useBase44OAuth({ deeplink: 'myapp' });
169
+
170
+ return (
171
+ <button
172
+ disabled={busy}
173
+ onClick={() => start({ provider: 'google', next: '/dashboard' })}
174
+ >
175
+ {busy ? 'Starting…' : 'Sign in with Google'}
176
+ </button>
177
+ );
178
+ }
179
+ ```
180
+
181
+ ## Deeplink Format
182
+
183
+ - Pass the scheme **without** `://`: `"myapp"`, not `"myapp://"`.
184
+ - Valid scheme pattern: `/^[a-zA-Z][a-zA-Z0-9+.-]*$/`.
185
+ - Invalid values (e.g. containing `://`) throw a clear error in development.
186
+
187
+ ## Platform Detection
188
+
189
+ Detect Despia runtime and platform via user agent:
190
+
191
+ ```tsx
192
+ import { isDespiaNative, isDespiaIOS, isDespiaAndroid } from 'despia-base44-oauth';
193
+
194
+ if (isDespiaNative()) {
195
+ // Running in Despia native runtime
196
+ if (isDespiaIOS()) {
197
+ // iOS – e.g. Apple JS SDK for Sign in with Apple (no browser needed)
198
+ } else if (isDespiaAndroid()) {
199
+ // Android
200
+ }
201
+ }
202
+ ```
203
+
204
+ - `isDespiaNative()`: `navigator.userAgent` includes `"despia"` (case-insensitive).
205
+ - `isDespiaIOS()`: Despia + `"iphone"` or `"ipad"` in user agent.
206
+ - `isDespiaAndroid()`: Despia + `"android"` in user agent.
207
+
208
+ ## Security
209
+
210
+ - **State:** Random `state` for replay protection, stored in `sessionStorage`.
211
+ - **next:** Only relative paths starting with `/` allowed; absolute URLs and schemes rejected.
212
+ - **Token parsing:** Supports query (`?access_token=...`) and hash (`#access_token=...`).
213
+ - **Flow TTL:** One active flow at a time; expires after 2 minutes; cleared on success or error.
214
+
215
+ ## Integration with Despia SDK
216
+
217
+ This package relies on `despia-native` for the OAuth transport. When running in the Despia native runtime, it uses:
218
+
219
+ ```javascript
220
+ despia(`oauth://?url=${encodeURIComponent(oauthUrl)}`);
221
+ ```
222
+
223
+ Per Despia docs: always use the real SDK (`import despia from 'despia-native'`). Mock implementations will not work on actual devices. The SDK provides command queuing, variable watching, and type safety for TypeScript, React, and other frameworks.
224
+
225
+ ## Exports (Stable for AI)
226
+
227
+ | Export | Type | Description |
228
+ |--------|------|-------------|
229
+ | `OAuthRoutes` | Component | Registers `/__despia_oauth/start`, `/__despia_oauth/finish`, `/auth` |
230
+ | `Base44OAuthButton` | Component | OAuth button; native → secure browser, web → Base44 redirect |
231
+ | `useBase44OAuth` | Hook | `{ start, busy, error }` for programmatic OAuth |
232
+ | `Base44OAuthProvider` | Component | Provides Base44 client via context; expects `base44.auth` |
233
+ | `Base44Auth`, `Base44Client` | Type | Types for the auth interface used by this package |
234
+ | `isDespiaNative` | Function | `true` if userAgent includes `"despia"` |
235
+ | `isDespiaIOS` | Function | `true` if Despia + iPhone/iPad |
236
+ | `isDespiaAndroid` | Function | `true` if Despia + Android |
237
+
238
+ ## License
239
+
240
+ MIT
@@ -0,0 +1,89 @@
1
+ import React from 'react';
2
+
3
+ interface OAuthRoutesProps {
4
+ /** Deeplink scheme WITHOUT "://" (e.g. "myapp", "coolapp") */
5
+ deeplink: string;
6
+ /** Path for the apply route (default: "/auth") */
7
+ applyPath?: string;
8
+ /** Path for "Try again" fallback (default: "/login") */
9
+ loginPath?: string;
10
+ }
11
+ /**
12
+ * Provides internal OAuth routes:
13
+ * - /__despia_oauth/start
14
+ * - /__despia_oauth/finish
15
+ * - /auth (or applyPath)
16
+ */
17
+ declare function OAuthRoutes({ deeplink, applyPath, loginPath, }: OAuthRoutesProps): React.ReactElement;
18
+
19
+ interface Base44OAuthButtonProps {
20
+ /** Deeplink scheme WITHOUT "://" */
21
+ deeplink: string;
22
+ /** OAuth provider (e.g. "google", "github") */
23
+ provider: string;
24
+ /** Relative path to navigate after success (default: "/") */
25
+ next?: string;
26
+ /** Optional children; default: "Sign in with {provider}" */
27
+ children?: React.ReactNode;
28
+ /** Disabled state */
29
+ disabled?: boolean;
30
+ /** Additional props passed to the button */
31
+ [key: string]: unknown;
32
+ }
33
+ /**
34
+ * OAuth button: in Despia native, opens secure session; in web, uses Base44 redirect.
35
+ */
36
+ declare function Base44OAuthButton({ deeplink, provider, next, children, disabled, ...rest }: Base44OAuthButtonProps): React.ReactElement;
37
+
38
+ interface UseBase44OAuthOptions {
39
+ /** Deeplink scheme WITHOUT "://" */
40
+ deeplink: string;
41
+ /** Path for the apply route (default: "/auth") - informational, routes use OAuthRoutes config */
42
+ applyPath?: string;
43
+ }
44
+ interface UseBase44OAuthReturn {
45
+ start: (args: {
46
+ provider: string;
47
+ next?: string;
48
+ }) => Promise<void>;
49
+ busy: boolean;
50
+ error: Error | null;
51
+ }
52
+ /**
53
+ * Hook for programmatic OAuth start. Same behavior as Base44OAuthButton.
54
+ */
55
+ declare function useBase44OAuth({ deeplink }: UseBase44OAuthOptions): UseBase44OAuthReturn;
56
+
57
+ /**
58
+ * Compatible with @base44/sdk auth module:
59
+ * - loginWithProvider(provider, fromUrl?) - redirects to OAuth provider
60
+ * - setToken(token, saveToStorage?) - sets JWT for API requests
61
+ */
62
+ interface Base44Auth {
63
+ loginWithProvider: (provider: string, fromUrl?: string) => void | Promise<void>;
64
+ setToken: (token: string, saveToStorage?: boolean) => void | Promise<void>;
65
+ }
66
+ interface Base44Client {
67
+ auth: Base44Auth;
68
+ }
69
+ declare function Base44OAuthProvider({ base44, children, }: {
70
+ base44: Base44Client;
71
+ children: React.ReactNode;
72
+ }): React.ReactElement;
73
+ declare function useBase44Auth(): Base44Auth;
74
+
75
+ /**
76
+ * Returns true if the app is running inside the Despia native wrapper.
77
+ * Detection is based on userAgent including "despia" (case-insensitive).
78
+ */
79
+ declare function isDespiaNative(): boolean;
80
+ /**
81
+ * Returns true if running in Despia on iOS (iPhone or iPad).
82
+ */
83
+ declare function isDespiaIOS(): boolean;
84
+ /**
85
+ * Returns true if running in Despia on Android.
86
+ */
87
+ declare function isDespiaAndroid(): boolean;
88
+
89
+ export { type Base44Auth, type Base44Client, Base44OAuthButton, type Base44OAuthButtonProps, Base44OAuthProvider, OAuthRoutes, type OAuthRoutesProps, type UseBase44OAuthOptions, type UseBase44OAuthReturn, isDespiaAndroid, isDespiaIOS, isDespiaNative, useBase44Auth, useBase44OAuth };
@@ -0,0 +1,89 @@
1
+ import React from 'react';
2
+
3
+ interface OAuthRoutesProps {
4
+ /** Deeplink scheme WITHOUT "://" (e.g. "myapp", "coolapp") */
5
+ deeplink: string;
6
+ /** Path for the apply route (default: "/auth") */
7
+ applyPath?: string;
8
+ /** Path for "Try again" fallback (default: "/login") */
9
+ loginPath?: string;
10
+ }
11
+ /**
12
+ * Provides internal OAuth routes:
13
+ * - /__despia_oauth/start
14
+ * - /__despia_oauth/finish
15
+ * - /auth (or applyPath)
16
+ */
17
+ declare function OAuthRoutes({ deeplink, applyPath, loginPath, }: OAuthRoutesProps): React.ReactElement;
18
+
19
+ interface Base44OAuthButtonProps {
20
+ /** Deeplink scheme WITHOUT "://" */
21
+ deeplink: string;
22
+ /** OAuth provider (e.g. "google", "github") */
23
+ provider: string;
24
+ /** Relative path to navigate after success (default: "/") */
25
+ next?: string;
26
+ /** Optional children; default: "Sign in with {provider}" */
27
+ children?: React.ReactNode;
28
+ /** Disabled state */
29
+ disabled?: boolean;
30
+ /** Additional props passed to the button */
31
+ [key: string]: unknown;
32
+ }
33
+ /**
34
+ * OAuth button: in Despia native, opens secure session; in web, uses Base44 redirect.
35
+ */
36
+ declare function Base44OAuthButton({ deeplink, provider, next, children, disabled, ...rest }: Base44OAuthButtonProps): React.ReactElement;
37
+
38
+ interface UseBase44OAuthOptions {
39
+ /** Deeplink scheme WITHOUT "://" */
40
+ deeplink: string;
41
+ /** Path for the apply route (default: "/auth") - informational, routes use OAuthRoutes config */
42
+ applyPath?: string;
43
+ }
44
+ interface UseBase44OAuthReturn {
45
+ start: (args: {
46
+ provider: string;
47
+ next?: string;
48
+ }) => Promise<void>;
49
+ busy: boolean;
50
+ error: Error | null;
51
+ }
52
+ /**
53
+ * Hook for programmatic OAuth start. Same behavior as Base44OAuthButton.
54
+ */
55
+ declare function useBase44OAuth({ deeplink }: UseBase44OAuthOptions): UseBase44OAuthReturn;
56
+
57
+ /**
58
+ * Compatible with @base44/sdk auth module:
59
+ * - loginWithProvider(provider, fromUrl?) - redirects to OAuth provider
60
+ * - setToken(token, saveToStorage?) - sets JWT for API requests
61
+ */
62
+ interface Base44Auth {
63
+ loginWithProvider: (provider: string, fromUrl?: string) => void | Promise<void>;
64
+ setToken: (token: string, saveToStorage?: boolean) => void | Promise<void>;
65
+ }
66
+ interface Base44Client {
67
+ auth: Base44Auth;
68
+ }
69
+ declare function Base44OAuthProvider({ base44, children, }: {
70
+ base44: Base44Client;
71
+ children: React.ReactNode;
72
+ }): React.ReactElement;
73
+ declare function useBase44Auth(): Base44Auth;
74
+
75
+ /**
76
+ * Returns true if the app is running inside the Despia native wrapper.
77
+ * Detection is based on userAgent including "despia" (case-insensitive).
78
+ */
79
+ declare function isDespiaNative(): boolean;
80
+ /**
81
+ * Returns true if running in Despia on iOS (iPhone or iPad).
82
+ */
83
+ declare function isDespiaIOS(): boolean;
84
+ /**
85
+ * Returns true if running in Despia on Android.
86
+ */
87
+ declare function isDespiaAndroid(): boolean;
88
+
89
+ export { type Base44Auth, type Base44Client, Base44OAuthButton, type Base44OAuthButtonProps, Base44OAuthProvider, OAuthRoutes, type OAuthRoutesProps, type UseBase44OAuthOptions, type UseBase44OAuthReturn, isDespiaAndroid, isDespiaIOS, isDespiaNative, useBase44Auth, useBase44OAuth };