@agent-native/core 0.157.16 → 0.157.18
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/corpus/templates/calendar/server/handlers/google-auth.ts +19 -2
- package/corpus/templates/dispatch/server/plugins/identity-sso.ts +11 -1
- package/corpus/templates/dispatch/shared/feature-flags.ts +6 -2
- package/dist/observability/routes.d.ts +6 -6
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +13 -2
- package/dist/server/better-auth-instance.d.ts +2 -0
- package/dist/server/better-auth-instance.js +11 -0
- package/dist/server/google-oauth.d.ts +10 -0
- package/dist/server/google-oauth.js +10 -2
- package/dist/server/identity-sso.js +3 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/onboarding-html.js +15 -0
- package/package.json +1 -1
|
@@ -50,6 +50,7 @@ type CalendarOAuthStateOptions = {
|
|
|
50
50
|
owner?: string;
|
|
51
51
|
orgId?: string;
|
|
52
52
|
desktop?: boolean;
|
|
53
|
+
mobile?: boolean;
|
|
53
54
|
addAccount?: boolean;
|
|
54
55
|
app?: string;
|
|
55
56
|
returnUrl?: string;
|
|
@@ -245,6 +246,7 @@ export const getGoogleAuthUrl = defineEventHandler(async (event: H3Event) => {
|
|
|
245
246
|
const orgId = session?.orgId;
|
|
246
247
|
const desktop =
|
|
247
248
|
isElectron(event) || q.desktop === "1" || q.desktop === "true";
|
|
249
|
+
const mobile = q.mobile === "1" || q.mobile === "true";
|
|
248
250
|
const flowId = desktop ? (q.flow_id as string) || undefined : undefined;
|
|
249
251
|
const calendarConnect = isCalendarConnectRequest(q, owner);
|
|
250
252
|
const credentials = calendarConnect
|
|
@@ -279,6 +281,7 @@ export const getGoogleAuthUrl = defineEventHandler(async (event: H3Event) => {
|
|
|
279
281
|
owner,
|
|
280
282
|
orgId,
|
|
281
283
|
desktop,
|
|
284
|
+
mobile,
|
|
282
285
|
addAccount: calendarConnect,
|
|
283
286
|
app: OAUTH_STATE_APP_ID,
|
|
284
287
|
returnUrl,
|
|
@@ -309,6 +312,7 @@ export const getGoogleAuthUrl = defineEventHandler(async (event: H3Event) => {
|
|
|
309
312
|
export const handleGoogleCallback = defineEventHandler(
|
|
310
313
|
async (event: H3Event) => {
|
|
311
314
|
let desktop = false;
|
|
315
|
+
let mobile = false;
|
|
312
316
|
let flowId: string | undefined;
|
|
313
317
|
try {
|
|
314
318
|
const query = getQuery(event);
|
|
@@ -317,6 +321,7 @@ export const handleGoogleCallback = defineEventHandler(
|
|
|
317
321
|
getAppUrl(event, "/_agent-native/google/callback"),
|
|
318
322
|
);
|
|
319
323
|
desktop = state.desktop ?? false;
|
|
324
|
+
mobile = state.mobile ?? false;
|
|
320
325
|
flowId = state.flowId;
|
|
321
326
|
|
|
322
327
|
const googleError = query.error as string | undefined;
|
|
@@ -358,6 +363,7 @@ export const handleGoogleCallback = defineEventHandler(
|
|
|
358
363
|
{
|
|
359
364
|
hasProductionSession,
|
|
360
365
|
desktop,
|
|
366
|
+
...(mobile ? { mobile: true } : {}),
|
|
361
367
|
trackSignup: {
|
|
362
368
|
authProvider: "google",
|
|
363
369
|
authUserId: identity.id,
|
|
@@ -373,6 +379,7 @@ export const handleGoogleCallback = defineEventHandler(
|
|
|
373
379
|
return oauthCallbackResponse(event, identity.email, {
|
|
374
380
|
sessionToken,
|
|
375
381
|
desktop,
|
|
382
|
+
mobile,
|
|
376
383
|
returnUrl,
|
|
377
384
|
flowId,
|
|
378
385
|
appName: "Calendar",
|
|
@@ -398,11 +405,14 @@ export const handleGoogleCallback = defineEventHandler(
|
|
|
398
405
|
addAccount || (owner !== undefined && email !== owner);
|
|
399
406
|
const sessionOwner = isAddAccount ? (owner ?? email) : email;
|
|
400
407
|
const shouldCreateSession =
|
|
401
|
-
!isAddAccount ||
|
|
408
|
+
!isAddAccount ||
|
|
409
|
+
(desktop && flowId && sessionOwner) ||
|
|
410
|
+
(mobile && sessionOwner);
|
|
402
411
|
const { sessionToken } = shouldCreateSession
|
|
403
412
|
? await createOAuthSession(event, sessionOwner, {
|
|
404
413
|
hasProductionSession,
|
|
405
414
|
desktop,
|
|
415
|
+
...(mobile ? { mobile: true } : {}),
|
|
406
416
|
})
|
|
407
417
|
: { sessionToken: undefined };
|
|
408
418
|
|
|
@@ -414,6 +424,7 @@ export const handleGoogleCallback = defineEventHandler(
|
|
|
414
424
|
return oauthCallbackResponse(event, email, {
|
|
415
425
|
sessionToken,
|
|
416
426
|
desktop,
|
|
427
|
+
mobile,
|
|
417
428
|
addAccount: isAddAccount,
|
|
418
429
|
flowId,
|
|
419
430
|
appName: "Calendar",
|
|
@@ -434,6 +445,7 @@ export const getGoogleAddAccountUrl = defineEventHandler(
|
|
|
434
445
|
const q = getQuery(event);
|
|
435
446
|
const desktop =
|
|
436
447
|
isElectron(event) || q.desktop === "1" || q.desktop === "true";
|
|
448
|
+
const mobile = q.mobile === "1" || q.mobile === "true";
|
|
437
449
|
const flowId = desktop ? (q.flow_id as string) || undefined : undefined;
|
|
438
450
|
if (!(await resolveCalendarOAuthCredentials(event))) {
|
|
439
451
|
return missingCredentialsResponse(
|
|
@@ -456,6 +468,7 @@ export const getGoogleAddAccountUrl = defineEventHandler(
|
|
|
456
468
|
owner: session.email,
|
|
457
469
|
orgId: session.orgId,
|
|
458
470
|
desktop,
|
|
471
|
+
mobile,
|
|
459
472
|
addAccount: true,
|
|
460
473
|
app: OAUTH_STATE_APP_ID,
|
|
461
474
|
flowId,
|
|
@@ -481,6 +494,7 @@ export const getGoogleAddAccountUrl = defineEventHandler(
|
|
|
481
494
|
export const handleGoogleAddAccountCallback = defineEventHandler(
|
|
482
495
|
async (event: H3Event) => {
|
|
483
496
|
let desktop = false;
|
|
497
|
+
let mobile = false;
|
|
484
498
|
let flowId: string | undefined;
|
|
485
499
|
try {
|
|
486
500
|
const session = await getSession(event);
|
|
@@ -490,6 +504,7 @@ export const handleGoogleAddAccountCallback = defineEventHandler(
|
|
|
490
504
|
getAppUrl(event, "/_agent-native/google/add-account/callback"),
|
|
491
505
|
);
|
|
492
506
|
desktop = state.desktop ?? false;
|
|
507
|
+
mobile = state.mobile ?? false;
|
|
493
508
|
flowId = state.flowId;
|
|
494
509
|
|
|
495
510
|
const googleError = query.error as string | undefined;
|
|
@@ -530,10 +545,11 @@ export const handleGoogleAddAccountCallback = defineEventHandler(
|
|
|
530
545
|
session?.orgId ?? stateOrgId,
|
|
531
546
|
);
|
|
532
547
|
const { sessionToken } =
|
|
533
|
-
desktop && flowId
|
|
548
|
+
(desktop && flowId) || mobile
|
|
534
549
|
? await createOAuthSession(event, ownerEmail, {
|
|
535
550
|
hasProductionSession: !!session?.email,
|
|
536
551
|
desktop,
|
|
552
|
+
...(mobile ? { mobile: true } : {}),
|
|
537
553
|
})
|
|
538
554
|
: { sessionToken: undefined };
|
|
539
555
|
|
|
@@ -544,6 +560,7 @@ export const handleGoogleAddAccountCallback = defineEventHandler(
|
|
|
544
560
|
return oauthCallbackResponse(event, addedEmail, {
|
|
545
561
|
sessionToken,
|
|
546
562
|
desktop,
|
|
563
|
+
mobile,
|
|
547
564
|
addAccount: true,
|
|
548
565
|
flowId,
|
|
549
566
|
appName: "Calendar",
|
|
@@ -21,7 +21,11 @@ import {
|
|
|
21
21
|
isFeatureFlagEnabled,
|
|
22
22
|
} from "@agent-native/core/feature-flags";
|
|
23
23
|
import { getOrgDomain } from "@agent-native/core/org";
|
|
24
|
-
import {
|
|
24
|
+
import {
|
|
25
|
+
getH3App,
|
|
26
|
+
getSession,
|
|
27
|
+
hasGoogleAuthIdentity,
|
|
28
|
+
} from "@agent-native/core/server";
|
|
25
29
|
import { signInJourney } from "@agent-native/core/shared";
|
|
26
30
|
import { defineEventHandler, getHeader, getMethod, readBody } from "h3";
|
|
27
31
|
import type { H3Event } from "h3";
|
|
@@ -324,6 +328,9 @@ export const tokenHandler = defineEventHandler(
|
|
|
324
328
|
name: identity.name,
|
|
325
329
|
orgDomain: identity.orgDomain,
|
|
326
330
|
});
|
|
331
|
+
const identityAuthProvider = (await hasGoogleAuthIdentity(identity.email))
|
|
332
|
+
? "google"
|
|
333
|
+
: undefined;
|
|
327
334
|
let assertion: string;
|
|
328
335
|
try {
|
|
329
336
|
assertion = await signA2AToken(
|
|
@@ -337,6 +344,9 @@ export const tokenHandler = defineEventHandler(
|
|
|
337
344
|
extraClaims: {
|
|
338
345
|
email: claims.email,
|
|
339
346
|
...(claims.name ? { name: claims.name } : {}),
|
|
347
|
+
...(identityAuthProvider
|
|
348
|
+
? { identity_auth_provider: identityAuthProvider }
|
|
349
|
+
: {}),
|
|
340
350
|
scope: IDENTITY_SCOPE,
|
|
341
351
|
jti: identity.jti,
|
|
342
352
|
redirect_uri: redirectUri,
|
|
@@ -2,9 +2,12 @@ import {
|
|
|
2
2
|
defineFeatureFlag,
|
|
3
3
|
defineFeatureFlags,
|
|
4
4
|
} from "@agent-native/core/feature-flags";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
DISPATCH_WORKSPACE_APP_LIST_FLAG,
|
|
7
|
+
DISPATCH_WORKSPACE_SSO_FLAG,
|
|
8
|
+
} from "@agent-native/dispatch/shared/feature-flags";
|
|
6
9
|
|
|
7
|
-
export { DISPATCH_WORKSPACE_SSO_FLAG };
|
|
10
|
+
export { DISPATCH_WORKSPACE_APP_LIST_FLAG, DISPATCH_WORKSPACE_SSO_FLAG };
|
|
8
11
|
|
|
9
12
|
export const DESKTOP_WORKSPACE_SSO_FLAG = defineFeatureFlag({
|
|
10
13
|
key: "desktop.workspace-sso",
|
|
@@ -16,4 +19,5 @@ export const DESKTOP_WORKSPACE_SSO_FLAG = defineFeatureFlag({
|
|
|
16
19
|
export const DISPATCH_FEATURE_FLAGS = defineFeatureFlags([
|
|
17
20
|
DESKTOP_WORKSPACE_SSO_FLAG,
|
|
18
21
|
DISPATCH_WORKSPACE_SSO_FLAG,
|
|
22
|
+
DISPATCH_WORKSPACE_APP_LIST_FLAG,
|
|
19
23
|
]);
|
|
@@ -41,27 +41,27 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
|
|
|
41
41
|
thumbsUpRate: number;
|
|
42
42
|
avgEvalScore: number;
|
|
43
43
|
} | {
|
|
44
|
+
ok?: undefined;
|
|
45
|
+
error?: undefined;
|
|
44
46
|
summary: import("./types.js").TraceSummary;
|
|
45
47
|
spans: import("./types.js").TraceSpan[];
|
|
46
48
|
id?: undefined;
|
|
47
|
-
error?: undefined;
|
|
48
|
-
ok?: undefined;
|
|
49
49
|
} | {
|
|
50
|
+
ok?: undefined;
|
|
51
|
+
error?: undefined;
|
|
50
52
|
summary?: undefined;
|
|
51
53
|
spans?: undefined;
|
|
52
54
|
id: string;
|
|
53
|
-
error?: undefined;
|
|
54
|
-
ok?: undefined;
|
|
55
55
|
} | {
|
|
56
|
+
ok?: undefined;
|
|
56
57
|
summary?: undefined;
|
|
57
58
|
spans?: undefined;
|
|
58
59
|
id?: undefined;
|
|
59
60
|
error: any;
|
|
60
|
-
ok?: undefined;
|
|
61
61
|
} | {
|
|
62
|
+
error?: undefined;
|
|
62
63
|
summary?: undefined;
|
|
63
64
|
spans?: undefined;
|
|
64
65
|
id?: undefined;
|
|
65
|
-
error?: undefined;
|
|
66
66
|
ok: boolean;
|
|
67
67
|
}>>;
|
package/dist/secrets/routes.d.ts
CHANGED
|
@@ -34,16 +34,16 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
|
|
|
34
34
|
/** POST /_agent-native/secrets/:key — write a secret. */
|
|
35
35
|
export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
36
36
|
error: string;
|
|
37
|
-
status?: undefined;
|
|
38
37
|
ok?: undefined;
|
|
38
|
+
status?: undefined;
|
|
39
39
|
} | {
|
|
40
40
|
ok: boolean;
|
|
41
41
|
status: string;
|
|
42
42
|
error?: undefined;
|
|
43
43
|
} | {
|
|
44
|
+
ok?: undefined;
|
|
44
45
|
error: string;
|
|
45
46
|
removed?: undefined;
|
|
46
|
-
ok?: undefined;
|
|
47
47
|
} | {
|
|
48
48
|
ok: boolean;
|
|
49
49
|
removed: boolean;
|
|
@@ -54,9 +54,9 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
|
|
|
54
54
|
* or the current stored value without changing anything.
|
|
55
55
|
*/
|
|
56
56
|
export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
57
|
+
ok?: undefined;
|
|
57
58
|
error: string;
|
|
58
59
|
note?: undefined;
|
|
59
|
-
ok?: undefined;
|
|
60
60
|
} | {
|
|
61
61
|
ok: boolean;
|
|
62
62
|
note?: undefined;
|
|
@@ -27,10 +27,10 @@ export declare function resolveAgentEngineApiKeyWriteTarget(event: H3Event, scop
|
|
|
27
27
|
export declare function createAgentEngineApiKeyHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
|
|
28
28
|
error: any;
|
|
29
29
|
} | {
|
|
30
|
+
error?: undefined;
|
|
30
31
|
ok: boolean;
|
|
31
32
|
key: string;
|
|
32
33
|
baseUrlKey?: string;
|
|
33
34
|
scope: AgentEngineApiKeyScope;
|
|
34
|
-
error?: undefined;
|
|
35
35
|
}>>;
|
|
36
36
|
export {};
|
package/dist/server/auth.js
CHANGED
|
@@ -564,7 +564,8 @@ function shouldExposeSessionTokenInBody(event) {
|
|
|
564
564
|
// X-Request-Source; browsers can only use that cross-origin after our CORS
|
|
565
565
|
// allowlist has approved the origin, and same-origin pages already receive
|
|
566
566
|
// an equivalent httpOnly session cookie on successful login.
|
|
567
|
-
|
|
567
|
+
const requestSource = getHeader(event, "x-request-source");
|
|
568
|
+
return (!origin && (requestSource === "clips-desktop" || requestSource === "mobile"));
|
|
568
569
|
}
|
|
569
570
|
function authLoginResponse(event, token, email) {
|
|
570
571
|
if (!shouldExposeSessionTokenInBody(event))
|
|
@@ -2683,6 +2684,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2683
2684
|
}
|
|
2684
2685
|
const q = getQuery(event);
|
|
2685
2686
|
const desktop = isElectronRequest(event) || q.desktop === "1" || q.desktop === "true";
|
|
2687
|
+
const mobile = q.mobile === "1" || q.mobile === "true";
|
|
2686
2688
|
const flowId = desktop ? q.flow_id || undefined : undefined;
|
|
2687
2689
|
// Validate the caller's return param up front and only embed it
|
|
2688
2690
|
// into the OAuth state when it normalises to a non-root path —
|
|
@@ -2701,6 +2703,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2701
2703
|
const state = encodeOAuthState({
|
|
2702
2704
|
redirectUri,
|
|
2703
2705
|
desktop,
|
|
2706
|
+
mobile,
|
|
2704
2707
|
addAccount: false,
|
|
2705
2708
|
app: getOAuthStateAppId(),
|
|
2706
2709
|
returnUrl,
|
|
@@ -2711,6 +2714,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2711
2714
|
logGoogleOAuthDebug(event, "auth-url", {
|
|
2712
2715
|
flowId,
|
|
2713
2716
|
desktop,
|
|
2717
|
+
mobile,
|
|
2714
2718
|
redirectPath: oauthDebugUrlPath(redirectUri),
|
|
2715
2719
|
returnUrl,
|
|
2716
2720
|
redirect: q.redirect === "1",
|
|
@@ -2755,15 +2759,18 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2755
2759
|
return callbackRelay;
|
|
2756
2760
|
let callbackFlowId;
|
|
2757
2761
|
let callbackDesktop = false;
|
|
2762
|
+
let callbackMobile = false;
|
|
2758
2763
|
try {
|
|
2759
2764
|
const query = getQuery(event);
|
|
2760
2765
|
const code = query.code;
|
|
2761
|
-
const { redirectUri, desktop, returnUrl, flowId, signupAttribution, signupAnonymousId, } = decodeOAuthState(query.state, getAppUrl(event, "/_agent-native/google/callback"));
|
|
2766
|
+
const { redirectUri, desktop, mobile, returnUrl, flowId, signupAttribution, signupAnonymousId, } = decodeOAuthState(query.state, getAppUrl(event, "/_agent-native/google/callback"));
|
|
2762
2767
|
callbackFlowId = flowId;
|
|
2763
2768
|
callbackDesktop = desktop ?? false;
|
|
2769
|
+
callbackMobile = mobile ?? false;
|
|
2764
2770
|
logGoogleOAuthDebug(event, "callback-start", {
|
|
2765
2771
|
flowId,
|
|
2766
2772
|
desktop,
|
|
2773
|
+
mobile,
|
|
2767
2774
|
redirectPath: oauthDebugUrlPath(redirectUri),
|
|
2768
2775
|
hasCode: !!code,
|
|
2769
2776
|
returnUrl,
|
|
@@ -2870,6 +2877,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2870
2877
|
const { sessionToken } = await createOAuthSession(event, email, {
|
|
2871
2878
|
hasProductionSession: false,
|
|
2872
2879
|
desktop,
|
|
2880
|
+
mobile,
|
|
2873
2881
|
trackSignup: {
|
|
2874
2882
|
authProvider: "google",
|
|
2875
2883
|
authUserId: typeof user.id === "string" ? user.id : undefined,
|
|
@@ -2881,6 +2889,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2881
2889
|
logGoogleOAuthDebug(event, "callback-session-created", {
|
|
2882
2890
|
flowId,
|
|
2883
2891
|
desktop,
|
|
2892
|
+
mobile,
|
|
2884
2893
|
hasSessionToken: !!sessionToken,
|
|
2885
2894
|
emailDomain: email.split("@")[1] || "",
|
|
2886
2895
|
});
|
|
@@ -2894,6 +2903,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2894
2903
|
return oauthCallbackResponse(event, email, {
|
|
2895
2904
|
sessionToken,
|
|
2896
2905
|
desktop,
|
|
2906
|
+
mobile,
|
|
2897
2907
|
returnUrl,
|
|
2898
2908
|
flowId,
|
|
2899
2909
|
});
|
|
@@ -2909,6 +2919,7 @@ async function mountBetterAuthRoutes(app, options) {
|
|
|
2909
2919
|
logGoogleOAuthDebug(event, "callback-error", {
|
|
2910
2920
|
flowId: callbackFlowId,
|
|
2911
2921
|
desktop: callbackDesktop,
|
|
2922
|
+
mobile: callbackMobile,
|
|
2912
2923
|
message: msg,
|
|
2913
2924
|
});
|
|
2914
2925
|
return oauthErrorPage(AUTH_GOOGLE_FALLBACK);
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
import { type BetterAuthOptions } from "better-auth";
|
|
9
9
|
export { getAuthLoginMode, resolveAuthLoginMode, resolveAuthLoginModeFromReadiness, type AuthLoginMode, } from "./auth-login-mode.js";
|
|
10
10
|
export declare function hasBetterAuthUserEmail(email: string): Promise<boolean>;
|
|
11
|
+
/** Return whether the canonical user has a verified Google account link. */
|
|
12
|
+
export declare function hasGoogleAuthIdentity(email: string): Promise<boolean | undefined>;
|
|
11
13
|
export declare function trackSignupEvent({ authProvider, authUserId, email, name, attribution, anonymousId, }: {
|
|
12
14
|
authProvider: string;
|
|
13
15
|
authUserId?: string;
|
|
@@ -54,6 +54,17 @@ export async function hasBetterAuthUserEmail(email) {
|
|
|
54
54
|
.catch(() => null);
|
|
55
55
|
return !!existing?.user?.email;
|
|
56
56
|
}
|
|
57
|
+
/** Return whether the canonical user has a verified Google account link. */
|
|
58
|
+
export async function hasGoogleAuthIdentity(email) {
|
|
59
|
+
const adapter = await getBetterAuthInternalAdapter();
|
|
60
|
+
if (!adapter)
|
|
61
|
+
return undefined;
|
|
62
|
+
const existing = await adapter.findUserByEmail(email.trim().toLowerCase(), {
|
|
63
|
+
includeAccounts: true,
|
|
64
|
+
});
|
|
65
|
+
return (existing?.accounts.some((account) => account.providerId === "google") ??
|
|
66
|
+
false);
|
|
67
|
+
}
|
|
57
68
|
export async function trackSignupEvent({ authProvider, authUserId, email, name, attribution, anonymousId, }) {
|
|
58
69
|
identify(email, {
|
|
59
70
|
email,
|
|
@@ -88,6 +88,13 @@ export interface OAuthStatePayload {
|
|
|
88
88
|
owner?: string;
|
|
89
89
|
orgId?: string;
|
|
90
90
|
desktop?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Explicit native-mobile intent from an embedded app. The callback may be
|
|
93
|
+
* served by a browser with a non-mobile user-agent, so relying on
|
|
94
|
+
* `isMobile(event)` alone can strand the native client on the web sign-in
|
|
95
|
+
* page.
|
|
96
|
+
*/
|
|
97
|
+
mobile?: boolean;
|
|
91
98
|
addAccount?: boolean;
|
|
92
99
|
app?: string;
|
|
93
100
|
/**
|
|
@@ -113,6 +120,7 @@ export interface EncodeOAuthStateOptions {
|
|
|
113
120
|
owner?: string;
|
|
114
121
|
orgId?: string;
|
|
115
122
|
desktop?: boolean;
|
|
123
|
+
mobile?: boolean;
|
|
116
124
|
addAccount?: boolean;
|
|
117
125
|
app?: string;
|
|
118
126
|
returnUrl?: string;
|
|
@@ -165,6 +173,7 @@ export interface OAuthSessionResult {
|
|
|
165
173
|
export declare function createOAuthSession(event: H3Event, email: string, opts: {
|
|
166
174
|
hasProductionSession: boolean;
|
|
167
175
|
desktop?: boolean;
|
|
176
|
+
mobile?: boolean;
|
|
168
177
|
trackSignup?: {
|
|
169
178
|
authProvider: string;
|
|
170
179
|
authUserId?: string;
|
|
@@ -182,6 +191,7 @@ export declare function createOAuthSession(event: H3Event, email: string, opts:
|
|
|
182
191
|
export declare function oauthCallbackResponse(event: H3Event, email: string, opts: {
|
|
183
192
|
sessionToken?: string;
|
|
184
193
|
desktop?: boolean;
|
|
194
|
+
mobile?: boolean;
|
|
185
195
|
addAccount?: boolean;
|
|
186
196
|
/**
|
|
187
197
|
* Same-origin path to return the viewer to after a successful web
|
|
@@ -453,6 +453,8 @@ export function encodeOAuthState(redirectUriOrOpts, owner, desktop, addAccount,
|
|
|
453
453
|
payload.g = opts.orgId;
|
|
454
454
|
if (opts.desktop)
|
|
455
455
|
payload.d = true;
|
|
456
|
+
if (opts.mobile)
|
|
457
|
+
payload.m = true;
|
|
456
458
|
if (opts.addAccount)
|
|
457
459
|
payload.a = true;
|
|
458
460
|
if (opts.app)
|
|
@@ -500,6 +502,7 @@ export function decodeOAuthState(stateParam, fallbackUri) {
|
|
|
500
502
|
owner: parsed.o || undefined,
|
|
501
503
|
orgId: typeof parsed.g === "string" ? parsed.g : undefined,
|
|
502
504
|
desktop: !!parsed.d,
|
|
505
|
+
mobile: !!parsed.m,
|
|
503
506
|
addAccount: !!parsed.a,
|
|
504
507
|
app: typeof parsed.app === "string" ? parsed.app : undefined,
|
|
505
508
|
// Pass returnUrl through as-is — same-origin validation runs at the
|
|
@@ -537,7 +540,10 @@ export async function resolveOAuthOwner(event, stateOwner) {
|
|
|
537
540
|
* app can inject it.
|
|
538
541
|
*/
|
|
539
542
|
export async function createOAuthSession(event, email, opts) {
|
|
540
|
-
|
|
543
|
+
// A native callback can arrive through a browser whose callback request
|
|
544
|
+
// user-agent does not identify as mobile. Prefer the signed flow intent and
|
|
545
|
+
// retain UA detection for ordinary mobile web sign-ins.
|
|
546
|
+
const mobile = opts.mobile || isMobile(event);
|
|
541
547
|
const needsDeepLink = opts.desktop || mobile;
|
|
542
548
|
const maxAge = getSessionMaxAge();
|
|
543
549
|
let sessionToken;
|
|
@@ -592,7 +598,9 @@ export async function createOAuthSession(event, email, opts) {
|
|
|
592
598
|
* pages, and plain web redirects — so templates don't have to.
|
|
593
599
|
*/
|
|
594
600
|
export function oauthCallbackResponse(event, email, opts) {
|
|
595
|
-
|
|
601
|
+
// The mobile flag is carried inside HMAC-signed OAuth state by native
|
|
602
|
+
// clients. UA detection remains the fallback for ordinary mobile browsers.
|
|
603
|
+
const mobile = opts.mobile || isMobile(event);
|
|
596
604
|
const query = getQuery(event);
|
|
597
605
|
const callbackState = typeof query.state === "string" && query.state.length > 0
|
|
598
606
|
? query.state
|
|
@@ -218,6 +218,7 @@ async function verifyIdentityAssertion(assertion, binding) {
|
|
|
218
218
|
orgDomain: typeof payload.org_domain === "string" && payload.org_domain
|
|
219
219
|
? payload.org_domain
|
|
220
220
|
: undefined,
|
|
221
|
+
authProvider: payload.identity_auth_provider === "google" ? "google" : undefined,
|
|
221
222
|
sub: typeof payload.sub === "string" && payload.sub ? payload.sub : email,
|
|
222
223
|
jti,
|
|
223
224
|
};
|
|
@@ -416,7 +417,8 @@ export async function handleIdentitySso(event, subpath) {
|
|
|
416
417
|
if (!identity || (await isJtiReplayed(identity.jti))) {
|
|
417
418
|
return errorPage("We could not verify the sign-in response. Please try again.", loginPath);
|
|
418
419
|
}
|
|
419
|
-
if (await isGoogleSignInRequiredForEmail(identity.email))
|
|
420
|
+
if ((await isGoogleSignInRequiredForEmail(identity.email)) &&
|
|
421
|
+
identity.authProvider !== "google") {
|
|
420
422
|
return errorPage(GOOGLE_AUTH_REQUIRED_MESSAGE, loginPath);
|
|
421
423
|
}
|
|
422
424
|
try {
|
package/dist/server/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { createEmbedSessionTicket, consumeEmbedSessionTicket, normalizeEmbedTarg
|
|
|
9
9
|
export { createSSEHandler, type SSEHandlerOptions } from "./sse.js";
|
|
10
10
|
export { mountAuthMiddleware, autoMountAuth, registerAuthPublicPaths, getSession, COOKIE_NAME, addSession, removeSession, getSessionEmail, getFrameworkSessionCookieValues, setFrameworkSessionCookie, clearFrameworkSessionCookies, runAuthGuard, setDesktopExchange, setDesktopExchangeError, safeReturnPath, type DesktopExchangeErrorPayload, type AuthSession, type AuthOptions, } from "./auth.js";
|
|
11
11
|
export { handleIdentitySso, getIdentityHubUrl, isIdentitySsoEnabled, isIdentitySsoBypassPath, identitySsoLoginButtonHtml, IDENTITY_SSO_PROVIDER_ID, IDENTITY_SSO_SCOPE, IDENTITY_SSO_DESKTOP_COMPLETE_PATH, } from "./identity-sso.js";
|
|
12
|
+
export { hasGoogleAuthIdentity } from "./better-auth-instance.js";
|
|
12
13
|
export { requireEnvKey, type MissingKeyResponse } from "./missing-key.js";
|
|
13
14
|
export { assertCurrentRequestUserIsOrgAdmin, currentRequestUserIsOrgAdmin, } from "./org-admin.js";
|
|
14
15
|
export { verifyCaptcha, type CaptchaVerifyResult } from "./captcha.js";
|
package/dist/server/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export { createEmbedSessionTicket, consumeEmbedSessionTicket, normalizeEmbedTarg
|
|
|
9
9
|
export { createSSEHandler } from "./sse.js";
|
|
10
10
|
export { mountAuthMiddleware, autoMountAuth, registerAuthPublicPaths, getSession, COOKIE_NAME, addSession, removeSession, getSessionEmail, getFrameworkSessionCookieValues, setFrameworkSessionCookie, clearFrameworkSessionCookies, runAuthGuard, setDesktopExchange, setDesktopExchangeError, safeReturnPath, } from "./auth.js";
|
|
11
11
|
export { handleIdentitySso, getIdentityHubUrl, isIdentitySsoEnabled, isIdentitySsoBypassPath, identitySsoLoginButtonHtml, IDENTITY_SSO_PROVIDER_ID, IDENTITY_SSO_SCOPE, IDENTITY_SSO_DESKTOP_COMPLETE_PATH, } from "./identity-sso.js";
|
|
12
|
+
export { hasGoogleAuthIdentity } from "./better-auth-instance.js";
|
|
12
13
|
export { requireEnvKey } from "./missing-key.js";
|
|
13
14
|
export { assertCurrentRequestUserIsOrgAdmin, currentRequestUserIsOrgAdmin, } from "./org-admin.js";
|
|
14
15
|
export { verifyCaptcha } from "./captcha.js";
|
|
@@ -1299,6 +1299,16 @@ export function getOnboardingHtml(opts = {}) {
|
|
|
1299
1299
|
const googleAuthMode = resolveGoogleAuthMode(opts.googleAuthMode);
|
|
1300
1300
|
const builderPreviewLocalDevEnabled = isBuilderPreviewLocalDevEnabled();
|
|
1301
1301
|
const localeInitScript = getLocaleInitScript();
|
|
1302
|
+
const embeddedAuthInitScript = `(function() {
|
|
1303
|
+
try {
|
|
1304
|
+
var params = new URLSearchParams(window.location.search || "");
|
|
1305
|
+
if (params.get("embedded") === "1" || window.self !== window.top) {
|
|
1306
|
+
document.documentElement.setAttribute("data-agent-native-embedded", "1");
|
|
1307
|
+
}
|
|
1308
|
+
} catch (error) {
|
|
1309
|
+
void error;
|
|
1310
|
+
}
|
|
1311
|
+
})();`;
|
|
1302
1312
|
const marketing = opts.marketing ??
|
|
1303
1313
|
resolveBuiltInAuthMarketing({
|
|
1304
1314
|
requestHost: opts.requestHost,
|
|
@@ -1397,6 +1407,9 @@ ${localeMenuItemsHtml}
|
|
|
1397
1407
|
</div>`
|
|
1398
1408
|
: "";
|
|
1399
1409
|
const identitySsoHtml = identitySsoLoginButtonHtml();
|
|
1410
|
+
const embeddedAuthCss = identitySsoHtml
|
|
1411
|
+
? ' html[data-agent-native-embedded="1"] #identity-sso-btn { display: none !important; }\n'
|
|
1412
|
+
: "";
|
|
1400
1413
|
const localDevHtml = `
|
|
1401
1414
|
<div class="local-dev-signin" id="local-dev-signin" hidden>
|
|
1402
1415
|
<button type="button" class="btn-local-dev btn-primary" id="local-dev-btn" title="${esc(t("localDevDescription"))}"${i18nAttr("localDevButton")} data-i18n-title="localDevDescription" aria-describedby="local-dev-description">${esc(t("localDevButton"))}</button>
|
|
@@ -1758,6 +1771,7 @@ ${marketing.description ? ` <p class="app-desc" data-marketing-field="descr
|
|
|
1758
1771
|
<head>
|
|
1759
1772
|
<meta charset="UTF-8">
|
|
1760
1773
|
<script data-agent-native-locale-init>${localeInitScript}</script>
|
|
1774
|
+
<script data-agent-native-embedded-init>${embeddedAuthInitScript}</script>
|
|
1761
1775
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
|
1762
1776
|
<title>${hasMarketing ? esc(marketing.appName) + " — " + esc(t("pageTitleSignIn")) : esc(t("pageTitleWelcome"))}</title>
|
|
1763
1777
|
<link rel="icon" type="image/svg+xml" href="${withAppBasePath("/favicon.svg")}">
|
|
@@ -2305,6 +2319,7 @@ ${marketingStyles}
|
|
|
2305
2319
|
body.simplified-auth { background: #141414; }
|
|
2306
2320
|
body.simplified-auth .card { border-color: transparent; box-shadow: none; }
|
|
2307
2321
|
body.simplified-auth .local-note { display: none !important; }
|
|
2322
|
+
${embeddedAuthCss}
|
|
2308
2323
|
</style>
|
|
2309
2324
|
</head>
|
|
2310
2325
|
<body${simplifiedAuth ? ' class="simplified-auth"' : hasMarketing ? ' class="has-marketing"' : ""}>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.157.
|
|
3
|
+
"version": "0.157.18",
|
|
4
4
|
"description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|