@oxy.so/services 11.0.0 → 11.1.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.
Files changed (128) hide show
  1. package/lib/commonjs/ui/boot/runProviderColdBoot.js +14 -1
  2. package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
  3. package/lib/commonjs/ui/components/OxySignInButton.js +25 -7
  4. package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
  5. package/lib/commonjs/ui/context/OxyContext.js +198 -41
  6. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  7. package/lib/commonjs/ui/context/commitSessionFlow.js +5 -0
  8. package/lib/commonjs/ui/context/commitSessionFlow.js.map +1 -1
  9. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +56 -9
  10. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  11. package/lib/commonjs/ui/context/isolatedOAuthSession.js +52 -0
  12. package/lib/commonjs/ui/context/isolatedOAuthSession.js.map +1 -0
  13. package/lib/commonjs/ui/context/useOxyAccountGraph.js +7 -5
  14. package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
  15. package/lib/commonjs/ui/hooks/useAuth.js +7 -3
  16. package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
  17. package/lib/commonjs/ui/oauth/applicationSessionLane.js +32 -0
  18. package/lib/commonjs/ui/oauth/applicationSessionLane.js.map +1 -0
  19. package/lib/commonjs/ui/oauth/completeOAuthCode.js +4 -2
  20. package/lib/commonjs/ui/oauth/completeOAuthCode.js.map +1 -1
  21. package/lib/commonjs/ui/oauth/nativeAuthTransport.js +119 -0
  22. package/lib/commonjs/ui/oauth/nativeAuthTransport.js.map +1 -0
  23. package/lib/commonjs/ui/session/createSessionClient.js +14 -2
  24. package/lib/commonjs/ui/session/createSessionClient.js.map +1 -1
  25. package/lib/commonjs/ui/session/deviceCredentialTracker.js +8 -0
  26. package/lib/commonjs/ui/session/deviceCredentialTracker.js.map +1 -1
  27. package/lib/module/ui/boot/runProviderColdBoot.js +14 -1
  28. package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
  29. package/lib/module/ui/components/OxySignInButton.js +25 -7
  30. package/lib/module/ui/components/OxySignInButton.js.map +1 -1
  31. package/lib/module/ui/context/OxyContext.js +200 -43
  32. package/lib/module/ui/context/OxyContext.js.map +1 -1
  33. package/lib/module/ui/context/commitSessionFlow.js +5 -0
  34. package/lib/module/ui/context/commitSessionFlow.js.map +1 -1
  35. package/lib/module/ui/context/hooks/useAuthOperations.js +56 -9
  36. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  37. package/lib/module/ui/context/isolatedOAuthSession.js +44 -0
  38. package/lib/module/ui/context/isolatedOAuthSession.js.map +1 -0
  39. package/lib/module/ui/context/useOxyAccountGraph.js +7 -5
  40. package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
  41. package/lib/module/ui/hooks/useAuth.js +7 -3
  42. package/lib/module/ui/hooks/useAuth.js.map +1 -1
  43. package/lib/module/ui/oauth/applicationSessionLane.js +26 -0
  44. package/lib/module/ui/oauth/applicationSessionLane.js.map +1 -0
  45. package/lib/module/ui/oauth/completeOAuthCode.js +4 -2
  46. package/lib/module/ui/oauth/completeOAuthCode.js.map +1 -1
  47. package/lib/module/ui/oauth/nativeAuthTransport.js +115 -0
  48. package/lib/module/ui/oauth/nativeAuthTransport.js.map +1 -0
  49. package/lib/module/ui/session/createSessionClient.js +14 -2
  50. package/lib/module/ui/session/createSessionClient.js.map +1 -1
  51. package/lib/module/ui/session/deviceCredentialTracker.js +8 -0
  52. package/lib/module/ui/session/deviceCredentialTracker.js.map +1 -1
  53. package/lib/typescript/commonjs/index.d.ts +1 -0
  54. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  55. package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts +7 -0
  56. package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts +2 -0
  58. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
  59. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +3 -1
  60. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  61. package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts +2 -0
  62. package/lib/typescript/commonjs/ui/context/commitSessionFlow.d.ts.map +1 -1
  63. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +3 -2
  64. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/ui/context/isolatedOAuthSession.d.ts +19 -0
  66. package/lib/typescript/commonjs/ui/context/isolatedOAuthSession.d.ts.map +1 -0
  67. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +3 -0
  68. package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
  69. package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts +2 -1
  70. package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
  71. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
  72. package/lib/typescript/commonjs/ui/oauth/applicationSessionLane.d.ts +6 -0
  73. package/lib/typescript/commonjs/ui/oauth/applicationSessionLane.d.ts.map +1 -0
  74. package/lib/typescript/commonjs/ui/oauth/completeOAuthCode.d.ts +2 -2
  75. package/lib/typescript/commonjs/ui/oauth/nativeAuthTransport.d.ts +28 -0
  76. package/lib/typescript/commonjs/ui/oauth/nativeAuthTransport.d.ts.map +1 -0
  77. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +1 -1
  78. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -1
  79. package/lib/typescript/commonjs/ui/session/deviceCredentialTracker.d.ts.map +1 -1
  80. package/lib/typescript/module/index.d.ts +1 -0
  81. package/lib/typescript/module/index.d.ts.map +1 -1
  82. package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts +7 -0
  83. package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
  84. package/lib/typescript/module/ui/components/OxySignInButton.d.ts +2 -0
  85. package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
  86. package/lib/typescript/module/ui/context/OxyContext.d.ts +3 -1
  87. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  88. package/lib/typescript/module/ui/context/commitSessionFlow.d.ts +2 -0
  89. package/lib/typescript/module/ui/context/commitSessionFlow.d.ts.map +1 -1
  90. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +3 -2
  91. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  92. package/lib/typescript/module/ui/context/isolatedOAuthSession.d.ts +19 -0
  93. package/lib/typescript/module/ui/context/isolatedOAuthSession.d.ts.map +1 -0
  94. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +3 -0
  95. package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
  96. package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts +2 -1
  97. package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
  98. package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
  99. package/lib/typescript/module/ui/oauth/applicationSessionLane.d.ts +6 -0
  100. package/lib/typescript/module/ui/oauth/applicationSessionLane.d.ts.map +1 -0
  101. package/lib/typescript/module/ui/oauth/completeOAuthCode.d.ts +2 -2
  102. package/lib/typescript/module/ui/oauth/nativeAuthTransport.d.ts +28 -0
  103. package/lib/typescript/module/ui/oauth/nativeAuthTransport.d.ts.map +1 -0
  104. package/lib/typescript/module/ui/session/createSessionClient.d.ts +1 -1
  105. package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -1
  106. package/lib/typescript/module/ui/session/deviceCredentialTracker.d.ts.map +1 -1
  107. package/package.json +30 -30
  108. package/src/index.ts +2 -0
  109. package/src/ui/boot/__tests__/coldBootNoIdpRedirect.test.ts +56 -2
  110. package/src/ui/boot/runProviderColdBoot.ts +22 -2
  111. package/src/ui/components/OxySignInButton.tsx +20 -4
  112. package/src/ui/context/OxyContext.tsx +171 -40
  113. package/src/ui/context/__tests__/commitSessionFlow.test.ts +13 -0
  114. package/src/ui/context/__tests__/isolatedOAuthSession.test.ts +35 -0
  115. package/src/ui/context/commitSessionFlow.ts +8 -0
  116. package/src/ui/context/hooks/useAuthOperations.ts +55 -9
  117. package/src/ui/context/isolatedOAuthSession.ts +48 -0
  118. package/src/ui/context/oxyContextTypes.ts +4 -0
  119. package/src/ui/context/useOxyAccountGraph.ts +8 -5
  120. package/src/ui/hooks/useAuth.ts +6 -2
  121. package/src/ui/oauth/__tests__/completeOAuthCode.test.ts +1 -1
  122. package/src/ui/oauth/__tests__/nativeAuthTransport.test.ts +57 -0
  123. package/src/ui/oauth/__tests__/oauthBearerCommit.http.test.ts +131 -0
  124. package/src/ui/oauth/applicationSessionLane.ts +26 -0
  125. package/src/ui/oauth/completeOAuthCode.ts +3 -3
  126. package/src/ui/oauth/nativeAuthTransport.ts +67 -0
  127. package/src/ui/session/createSessionClient.ts +13 -1
  128. package/src/ui/session/deviceCredentialTracker.ts +7 -1
@@ -78,7 +78,7 @@ function makeHarness(overrides: Partial<RunProviderColdBootOptions> = {}): Harne
78
78
  const sessionClientStart = jest.fn(async () => undefined);
79
79
 
80
80
  const options: RunProviderColdBootOptions = {
81
- oxyServices: {} as RunProviderColdBootOptions['oxyServices'],
81
+ oxyServices: { apps: { getPublic: jest.fn(async () => ({ id: 'registered-app', name: 'Registered App', type: 'first_party', isOfficial: false, isInternal: false, scopes: [] })) } } as unknown as RunProviderColdBootOptions['oxyServices'],
82
82
  authStore: makeEmptyAuthStore(),
83
83
  clientId: CLIENT_ID,
84
84
  sessionClient: { start: sessionClientStart } as unknown as SessionClient,
@@ -198,7 +198,7 @@ describe('runProviderColdBoot — no SDK-initiated IdP navigation (#691 phase 7b
198
198
  await commit.commitSession({ sessionId: 's1', userId: 'u1' });
199
199
  expect(options.commitSession).toHaveBeenCalledWith(
200
200
  { sessionId: 's1', userId: 'u1' },
201
- { activate: true },
201
+ { activate: true, oauth: true },
202
202
  );
203
203
  });
204
204
  });
@@ -213,3 +213,57 @@ describe('runProviderColdBoot — no SDK-initiated IdP navigation (#691 phase 7b
213
213
  expect(mockRedirectToAuthorize).not.toHaveBeenCalled();
214
214
  });
215
215
  });
216
+
217
+ describe('registered cold-boot classification', () => {
218
+ beforeEach(() => { mockRunSessionColdBoot.mockReset(); mockTryCompleteOAuthReturn.mockReset(); mockTryCompleteOAuthReturn.mockResolvedValue(false); });
219
+ it('external classification bypasses prior secrets, shared device and identity pin work', async () => {
220
+ const { options } = makeHarness();
221
+ (options.oxyServices.apps.getPublic as jest.Mock).mockResolvedValue({ id: 'external', name: 'External', type: 'third_party', isOfficial: false, isInternal: false, scopes: [] });
222
+ const beforeDeviceBoot = jest.fn();
223
+ await runProviderColdBoot({ ...options, beforeDeviceBoot });
224
+ expect(options.oxyServices.apps.getPublic).toHaveBeenCalledWith(CLIENT_ID, { cache: false });
225
+ expect(options.authStore.load).not.toHaveBeenCalled();
226
+ expect(options.authStore.clear).not.toHaveBeenCalled();
227
+ expect(options.syncDeviceCredentialToHost).not.toHaveBeenCalled();
228
+ expect(beforeDeviceBoot).not.toHaveBeenCalled();
229
+ expect(mockRunSessionColdBoot).not.toHaveBeenCalled();
230
+ });
231
+ it.each(['missing', 'inactive', 'offline', 'invalid'])('fails closed for %s before secrets and callback work', async (mode) => {
232
+ const { options } = makeHarness();
233
+ if (mode === 'missing') options.clientId = undefined;
234
+ else if (mode === 'invalid') (options.oxyServices.apps.getPublic as jest.Mock).mockResolvedValue({ type: 'unknown' });
235
+ else (options.oxyServices.apps.getPublic as jest.Mock).mockRejectedValue(new Error(mode));
236
+ await runProviderColdBoot(options);
237
+ expect(options.markAuthResolved).toHaveBeenCalled();
238
+ expect(options.authStore.load).not.toHaveBeenCalled();
239
+ expect(options.authStore.clear).not.toHaveBeenCalled();
240
+ expect(options.syncDeviceCredentialToHost).not.toHaveBeenCalled();
241
+ expect(mockRunSessionColdBoot).not.toHaveBeenCalled();
242
+ expect(mockTryCompleteOAuthReturn).not.toHaveBeenCalled();
243
+ });
244
+ it('a cold external OAuth callback never probes a previous device secret', async () => {
245
+ const { options } = makeHarness();
246
+ (options.oxyServices.apps.getPublic as jest.Mock).mockResolvedValue({ id: 'external', name: 'External', type: 'third_party', isOfficial: false, isInternal: false, scopes: [] });
247
+ mockTryCompleteOAuthReturn.mockImplementation(async ({ commitSession }) => {
248
+ await commitSession({ sessionId: 'isolated', userId: 'external-user', accessToken: 'isolated-bearer' }); return true;
249
+ });
250
+ await runProviderColdBoot(options);
251
+ expect(options.commitSession).toHaveBeenCalledWith({ sessionId: 'isolated', userId: 'external-user', accessToken: 'isolated-bearer' }, { activate: true, oauth: true });
252
+ expect(options.authStore.load).not.toHaveBeenCalled();
253
+ expect(options.syncDeviceCredentialToHost).not.toHaveBeenCalled();
254
+ expect(mockRunSessionColdBoot).not.toHaveBeenCalled();
255
+ });
256
+ });
257
+
258
+ it('an unresponsive registry resolves signed-out without a late privileged continuation', async () => {
259
+ jest.useFakeTimers();
260
+ try {
261
+ const {options}=makeHarness(); let release!: (app:unknown)=>void;
262
+ (options.oxyServices.apps.getPublic as jest.Mock).mockImplementation(()=>new Promise(resolve=>{release=resolve;}));
263
+ mockRunSessionColdBoot.mockReset(); mockTryCompleteOAuthReturn.mockReset();
264
+ const boot=runProviderColdBoot(options); await jest.advanceTimersByTimeAsync(5000); await boot;
265
+ expect(options.markAuthResolved).toHaveBeenCalled(); expect(options.authStore.load).not.toHaveBeenCalled();
266
+ release({id:'app',name:'Late',type:'first_party',isOfficial:true,isInternal:false,scopes:[]}); await Promise.resolve();
267
+ expect(mockRunSessionColdBoot).not.toHaveBeenCalled(); expect(mockTryCompleteOAuthReturn).not.toHaveBeenCalled();
268
+ } finally {jest.useRealTimers();}
269
+ });
@@ -1,3 +1,4 @@
1
+ import { resolveApplicationSessionLane } from '../oauth/applicationSessionLane';
1
2
  import { logger as loggerUtil, type OxyServices } from '@oxy.so/core';
2
3
  import { runSessionColdBoot, type AuthStateStore, type IdentityBinding, type SessionMode } from '@oxy.so/core/session';
3
4
  import type { SessionClient } from '@oxy.so/core/session';
@@ -80,8 +81,14 @@ export interface RunProviderColdBootOptions {
80
81
  /** The identity binding required by `sessionMode: 'identity'`. */
81
82
  identity?: IdentityBinding;
82
83
  sessionClient: SessionClient;
84
+ /** Provider-owned fresh classifier, also used by explicit action recovery. */
85
+ resolveSessionLane?: () => Promise<'device' | 'oauth'>;
86
+ /** Identity pin work may run only after a fresh device classification. */
87
+ onClassified?: (lane: 'device' | 'oauth') => void | Promise<void>;
88
+ beforeDeviceBoot?: () => Promise<void>;
89
+ afterDeviceBoot?: () => Promise<void>;
83
90
  syncDeviceCredentialToHost: () => Promise<void>;
84
- commitSession: (input: CommitInput, options: { activate: boolean }) => Promise<void>;
91
+ commitSession: (input: CommitInput, options: { activate: boolean; oauth?: boolean }) => Promise<void>;
85
92
  markAuthResolved: () => void;
86
93
  setTokenReady: (ready: boolean) => void;
87
94
  }
@@ -124,6 +131,11 @@ export async function runProviderColdBoot(opts: RunProviderColdBootOptions): Pro
124
131
  setTokenReady(false);
125
132
 
126
133
  try {
134
+ // Classification precedes every local-secret, shared-credential and identity
135
+ // probe. Unavailable registry metadata fails closed, including offline boot.
136
+ const lane = await (opts.resolveSessionLane?.() ?? resolveApplicationSessionLane(oxyServices, clientId));
137
+ await opts.onClassified?.(lane);
138
+ if (identityBound && lane !== 'device') return;
127
139
  // The redirect transport's RETURN leg. An app reaches it whenever the browser
128
140
  // BLOCKED the sign-in popup and `startWebOAuthSignIn` fell back to a
129
141
  // full-page redirect (`popup-blocked` / `popup-navigation-failed`), so it is
@@ -136,7 +148,7 @@ export async function runProviderColdBoot(opts: RunProviderColdBootOptions): Pro
136
148
  oxyServices,
137
149
  clientId,
138
150
  authRedirectUri,
139
- commitSession: (input) => commitSession(input, { activate: true }),
151
+ commitSession: (input) => commitSession(input, { activate: true, oauth: true }),
140
152
  });
141
153
  if (oauthCompleted) {
142
154
  setTokenReady(true);
@@ -144,6 +156,13 @@ export async function runProviderColdBoot(opts: RunProviderColdBootOptions): Pro
144
156
  return;
145
157
  }
146
158
 
159
+ if (lane === 'oauth') {
160
+ setTokenReady(true);
161
+ return;
162
+ }
163
+
164
+ await opts.beforeDeviceBoot?.();
165
+
147
166
  // Best-effort connectivity probe up front: an EXPLICIT offline verdict skips
148
167
  // the two doomed network steps so routing settles immediately instead of
149
168
  // burning the overall deadline on a mint that cannot succeed. Any ambiguity
@@ -224,6 +243,7 @@ export async function runProviderColdBoot(opts: RunProviderColdBootOptions): Pro
224
243
  }
225
244
  },
226
245
  });
246
+ await opts.afterDeviceBoot?.();
227
247
  } catch (error) {
228
248
  if (__DEV__) {
229
249
  loggerUtil.error(
@@ -1,3 +1,4 @@
1
+ import { classifyApplicationSessionLane } from '../oauth/applicationSessionLane';
1
2
  import type React from 'react';
2
3
  import { useCallback, useState, useEffect, useRef } from 'react';
3
4
  import { type ViewStyle, type TextStyle, type StyleProp, Platform } from 'react-native';
@@ -142,6 +143,8 @@ export interface OxySignInButtonProps {
142
143
  * ```
143
144
  */
144
145
  onOAuthResult?: (result: OxyOAuthResult) => void;
146
+ /** Opt in to the shared SDK native finalizer. Mutually exclusive with onOAuthResult. */
147
+ nativeOAuthCompletion?: 'sdk';
145
148
  }
146
149
 
147
150
  const ACTION_KEYS: Record<OxySignInButtonAction, string> = {
@@ -188,9 +191,10 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
188
191
  showWhenAuthenticated = false,
189
192
  oauthRedirectUri,
190
193
  onOAuthResult,
194
+ nativeOAuthCompletion,
191
195
  }) => {
192
196
  const { t } = useI18n();
193
- const { openAccountDialog, oxyServices, clientId, webAuthMode, startWebOAuthSignIn } = useOxy();
197
+ const { openAccountDialog, oxyServices, clientId, webAuthMode, startWebOAuthSignIn, startNativeOAuthSignIn } = useOxy();
194
198
  const { isAuthenticated, isLoading } = useAuthStore(
195
199
  useShallow((state) => ({ isAuthenticated: state.isAuthenticated, isLoading: state.isLoading }))
196
200
  );
@@ -228,7 +232,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
228
232
  if (cached && cached.clientId === clientId && cached.oxyServices === oxyServices) {
229
233
  return cached.promise;
230
234
  }
231
- const promise = oxyServices.apps.getPublic(clientId).catch((error) => {
235
+ const promise = oxyServices.apps.getPublic(clientId, { cache: false }).catch((error) => {
232
236
  // Only clear if this is still the live entry (a later resolve may have
233
237
  // replaced it after a clientId/oxyServices change).
234
238
  if (appResolutionRef.current?.promise === promise) {
@@ -308,6 +312,14 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
308
312
  return;
309
313
  }
310
314
 
315
+ if (nativeOAuthCompletion === 'sdk') {
316
+ if (onOAuthResult) { notifyNotConfigured(app.name); return; }
317
+ const result = await startNativeOAuthSignIn({ redirectUri: oauthRedirectUri });
318
+ if (result.status === 'failed') notifyFailed();
319
+ if (result.status === 'unsupported') notifyNotConfigured(app.name);
320
+ return;
321
+ }
322
+
311
323
  // Native: open the in-app auth session, then hand the handshake to the
312
324
  // RP so it can complete the token exchange from its deep-link callback.
313
325
  const [pkce, state] = await Promise.all([generatePkcePair(), generateOAuthState()]);
@@ -332,6 +344,8 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
332
344
  clientId,
333
345
  oauthRedirectUri,
334
346
  onOAuthResult,
347
+ nativeOAuthCompletion,
348
+ startNativeOAuthSignIn,
335
349
  startOfficialSignIn,
336
350
  startWebOAuthSignIn,
337
351
  shouldPreOpenPopup,
@@ -357,7 +371,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
357
371
  const resolving = resolvePublicApplication();
358
372
  if (!resolving) {
359
373
  closeOAuthPopup(popup);
360
- startOfficialSignIn();
374
+ notifyNotConfigured('this app');
361
375
  return;
362
376
  }
363
377
  let app: PublicApplication;
@@ -373,7 +387,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
373
387
  notifyFailed();
374
388
  return;
375
389
  }
376
- if (app.type === 'third_party' && !app.isOfficial) {
390
+ if (classifyApplicationSessionLane(app) === 'oauth') {
377
391
  await startThirdPartyOAuth(app, popup);
378
392
  return;
379
393
  }
@@ -392,6 +406,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
392
406
  );
393
407
  notifyFailed();
394
408
  } finally {
409
+ appResolutionRef.current = null;
395
410
  routingRef.current = false;
396
411
  }
397
412
  },
@@ -400,6 +415,7 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
400
415
  startOfficialSignIn,
401
416
  startThirdPartyOAuth,
402
417
  clientId,
418
+ notifyNotConfigured,
403
419
  notifyFailed,
404
420
  ],
405
421
  );