@iqauth/sdk 2.6.0 → 2.6.2
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/README.md +125 -0
- package/dist/react.d.mts +23 -1
- package/dist/react.d.ts +23 -1
- package/dist/react.js +29 -6
- package/dist/react.mjs +29 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,8 @@ The canonical TypeScript SDK for **IQAuthService** — DispositionIQ's multi-ten
|
|
|
17
17
|
- [Install](#install)
|
|
18
18
|
- [Five-line integration](#five-line-integration)
|
|
19
19
|
- [Pick your environment](#pick-your-environment)
|
|
20
|
+
- [What's new in 2.6.2](#whats-new-in-262)
|
|
21
|
+
- [What's new in 2.6.1](#whats-new-in-261)
|
|
20
22
|
- [What's new in 2.0.3](#whats-new-in-203)
|
|
21
23
|
- [Browser apps with a backend (recommended)](#browser-apps-with-a-backend-recommended)
|
|
22
24
|
- [Server reference (Express, Fastify, Hono, Next.js)](#server-reference)
|
|
@@ -89,6 +91,13 @@ gap, mirroring Clerk's `<ClerkLoading/>` / `<ClerkLoaded/>`.
|
|
|
89
91
|
Available hooks: `useUser()`, `useSession()`, `useAuth()`, `useOrganization()`. Each returns `{ data, isLoading, error }`.
|
|
90
92
|
Drop-in components: `<SignIn/>`, `<SignUp/>`, `<UserButton/>`, `<UserProfile/>`, `<OrganizationSwitcher/>`, `<AuthCallback/>`.
|
|
91
93
|
|
|
94
|
+
> **Silent SSO is opt-in as of 2.6.1.** `<SignIn/>` always renders the
|
|
95
|
+
> form on first paint by default — even for returning users with an active
|
|
96
|
+
> issuer-side `iq_sso` session. To restore the old auto-resume behavior,
|
|
97
|
+
> add `silentSso` to the provider or a specific `<SignIn/>` instance:
|
|
98
|
+
> `<IQAuthProvider publishableKey={…} silentSso>` or `<SignIn silentSso />`.
|
|
99
|
+
> See "What's new in 2.6.1" below.
|
|
100
|
+
|
|
92
101
|
### Express
|
|
93
102
|
|
|
94
103
|
```ts
|
|
@@ -165,6 +174,74 @@ The SDK is not "one auth model for every environment". The safe pattern depends
|
|
|
165
174
|
|
|
166
175
|
---
|
|
167
176
|
|
|
177
|
+
## What's new in 2.6.2
|
|
178
|
+
|
|
179
|
+
### Card grows responsively on desktop (no more phone-sized form)
|
|
180
|
+
|
|
181
|
+
`.iqauth-sdk-card` used to cap at `max-width: 460px` at every viewport,
|
|
182
|
+
which meant a desktop user staring at a 1440px monitor saw a thin mobile
|
|
183
|
+
column floating inside a 720px-wide pane. The cap is now responsive:
|
|
184
|
+
|
|
185
|
+
- mobile (default): **480px**
|
|
186
|
+
- tablet / desktop (`@container ≥ 768px`): **540px**
|
|
187
|
+
- extra-wide (`@container ≥ 1280px`): **580px**
|
|
188
|
+
|
|
189
|
+
Inner header/body padding and the title size also bump up at the desktop
|
|
190
|
+
breakpoint so the form actually fills its half of the split layout. No
|
|
191
|
+
opt-in required — the change applies to every consumer of `<SignIn/>` and
|
|
192
|
+
`<SignUp/>` after upgrading.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## What's new in 2.6.1
|
|
197
|
+
|
|
198
|
+
### 1. Silent SSO is now opt-in (default off)
|
|
199
|
+
|
|
200
|
+
Previously, `<SignIn/>` would detect an active issuer-side `iq_sso` session
|
|
201
|
+
on mount and silently redirect through `/oidc/sso-resume` — users never saw
|
|
202
|
+
the form, never clicked anything, and were transparently signed back in.
|
|
203
|
+
That was surprising for embedded use cases and made it impossible to switch
|
|
204
|
+
accounts without reaching for `?prompt=login`. As of 2.6.1, silent SSO is
|
|
205
|
+
**disabled by default** and must be explicitly enabled:
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
// Provider-wide opt-in (covers every <SignIn/> below it)
|
|
209
|
+
<IQAuthProvider publishableKey={…} silentSso>
|
|
210
|
+
…
|
|
211
|
+
</IQAuthProvider>
|
|
212
|
+
|
|
213
|
+
// Per-instance opt-in (overrides the provider value)
|
|
214
|
+
<SignIn silentSso />
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
When silent SSO is off (default), `<SignIn/>` always renders the form on
|
|
218
|
+
first paint regardless of `iq_sso` cookie state. `prompt="login"` and
|
|
219
|
+
`?prompt=login` continue to work as additional force-form switches.
|
|
220
|
+
|
|
221
|
+
### 2. Embedded card no longer forces full-viewport height
|
|
222
|
+
|
|
223
|
+
The internal `.iqauth-sdk-pane` declared `min-height: 100vh` unconditionally,
|
|
224
|
+
which worked for hosted full-page sign-in but broke when `<SignIn/>` was
|
|
225
|
+
embedded in a card, modal, or sidebar — pushing content below the fold and
|
|
226
|
+
creating large empty areas. The 100vh height now applies only inside the
|
|
227
|
+
wide side-by-side layout (`@container iqauth-sdk (min-width: 768px)`) where
|
|
228
|
+
the hero pane needs height parity. Narrow embeds size naturally to their
|
|
229
|
+
content.
|
|
230
|
+
|
|
231
|
+
### 3. Better error reporting on misconfigured `<SignIn/>`
|
|
232
|
+
|
|
233
|
+
- `useIQAuthSignInContext` now detects HTML responses (CORS preflight, wrong
|
|
234
|
+
`iqAuthBaseUrl`, wrong `appKey`) and prints an actionable `console.error`
|
|
235
|
+
naming the three likely causes — instead of the cryptic
|
|
236
|
+
`Unexpected token '<' in JSON`.
|
|
237
|
+
- The "returnTo not in allowed origins" console error now also lists the
|
|
238
|
+
app's actual `allowedOrigins`, so the diff with the rejected `returnTo`
|
|
239
|
+
is visible at a glance instead of requiring a Network-tab spelunk.
|
|
240
|
+
|
|
241
|
+
For older versions see [`CHANGELOG.md`](./CHANGELOG.md).
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
168
245
|
## What's new in 2.0.3
|
|
169
246
|
|
|
170
247
|
### 1. `serverManagedSession: true` for `SessionManager` / `IQAuthProvider`
|
|
@@ -547,6 +624,54 @@ Common codes you'll handle:
|
|
|
547
624
|
| Cross-origin call to a sister IQAuth-protected app gets CORS-blocked | Add the caller's origin to the target app's `app_allowed_origins`. Allow up to 60s for the cache to refresh. |
|
|
548
625
|
| `req.auth` is undefined under Passport | Verify you're reading `req.auth`, not `req.user`. The SDK deliberately uses `req.auth` to avoid Passport collision. |
|
|
549
626
|
| Hosted sign-in page won't accept your `return_to` | The origin isn't in this app's allowlist. Add it in the admin dashboard. |
|
|
627
|
+
| Server logs `[AUTH-MW] Missing auth credentials` on `GET /api/v1/auth/me` at app boot — **once**, immediately followed by a successful `[OIDC] SSO resume issued code` AND the next `/auth/me` returns 200 | **Benign.** SDK's `bootstrap()` probe. See ["Why does the server log `Missing auth credentials` at startup?"](#why-does-the-server-log-missing-auth-credentials-at-startup) below. |
|
|
628
|
+
| `[AUTH-MW] Missing auth credentials` repeats in a **loop** (warn → resume → token → warn → resume → token …), user can't actually finish login | **NOT benign.** The SDK's callback handler on YOUR app's domain isn't completing the token exchange, so no cookie ever gets set. See ["When the bootstrap loop never ends"](#when-the-bootstrap-loop-never-ends) below — almost always a middleware-ordering bug on the consumer app. |
|
|
629
|
+
| Same `Missing auth credentials` warning on `/api/v1/auth/me` but **no** subsequent `SSO resume issued code` or `/oidc/token` for the same user within ~1s | Real problem — your fetch is dropping cookies. Check `credentials: "include"`, CORS `Access-Control-Allow-Credentials: true`, `SameSite`/`COOKIE_DOMAIN`. |
|
|
630
|
+
|
|
631
|
+
### Why does the server log `Missing auth credentials` at startup?
|
|
632
|
+
|
|
633
|
+
When `<IQAuthProvider>` mounts, the SDK's `bootstrap()` runs `GET /api/v1/auth/me` once to ask the issuer "do I already have a session on this app?" That call is *defined* to be anonymous when the user has never signed in here yet — there is no `iqauth_at` cookie scoped to your origin yet to send.
|
|
634
|
+
|
|
635
|
+
So the very first request you'll see for a returning user is:
|
|
636
|
+
|
|
637
|
+
```
|
|
638
|
+
WARN [AUTH-MW] Missing auth credentials GET /api/v1/auth/me origin=https://your-app.com
|
|
639
|
+
INFO [OIDC] SSO resume issued code userId=… clientId=iq_…
|
|
640
|
+
INFO [AuthMetrics] /oidc/token latency
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
That sequence is the **happy path** for silent SSO: probe → no session here → resume from the issuer's `iq_sso` cookie → exchange code for tokens → cookie now set on your origin → subsequent requests succeed against the cookie. The warning was misleading log severity for a defined-anonymous endpoint, and as of the next IQAuth server release the bootstrap probe on `/auth/me` and `/auth/refresh` is logged at **`debug`** rather than `warn`. Other routes still log at `warn` — so seeing this message on, say, `GET /api/v1/users` remains a real signal that cookies aren't traveling.
|
|
644
|
+
|
|
645
|
+
**If you're on an older IQAuth server and want to silence the noise without an upgrade,** add a server-side log filter on the `[AUTH-MW] Missing auth credentials` line where `route` matches `/api/v1/auth/(me|refresh)`. Don't blanket-mute the message — you'll lose visibility on real CORS/cookie failures.
|
|
646
|
+
|
|
647
|
+
### When the bootstrap loop never ends
|
|
648
|
+
|
|
649
|
+
If you see `Missing auth credentials` → `SSO resume issued code` → `/oidc/token` happen **on repeat** and the user never actually gets signed in, the issuer side is doing its job perfectly — the failure is on **your** server. The SDK's callback helper at `/api/iqauth/callback` is responsible for taking the `?code=` from the OAuth redirect, POSTing it to `/oidc/token`, and setting the `iqauth_at` cookie on your app's domain. If that handler doesn't run (or returns an error), no cookie ever gets set and the SDK's next bootstrap probe is anonymous again — forever.
|
|
650
|
+
|
|
651
|
+
The single most common cause: **your own auth middleware is intercepting `/api/iqauth/callback` before `attachHelpers()` can handle it.** The OAuth return trip is a fresh GET from the issuer with no `Authorization` header (correctly so — that's the whole point of the callback), so an `app.use(requireAuth)` mounted globally will reject it as 401.
|
|
652
|
+
|
|
653
|
+
Confirm with these signals — if you see *any* of them, this is your bug:
|
|
654
|
+
|
|
655
|
+
- Browser: `GET https://your-app.com/api/iqauth/callback?code=…` returns **401** (or any 4xx other than 302)
|
|
656
|
+
- Browser DevTools → Application → Cookies for your app's domain: **no `iqauth_at` cookie present** after a sign-in attempt
|
|
657
|
+
- Your app's server log shows your **own** auth middleware rejecting `path=/iqauth/callback` or `/api/iqauth/callback` with "missing authorization header"
|
|
658
|
+
|
|
659
|
+
**Fix — pick one:**
|
|
660
|
+
|
|
661
|
+
```ts
|
|
662
|
+
// ✅ Option 1 (recommended) — mount SDK helpers BEFORE your auth middleware.
|
|
663
|
+
const auth = iqAuth({ publishableKey, secretKey });
|
|
664
|
+
auth.attachHelpers(app); // public: /api/iqauth/{callback,refresh,signout}
|
|
665
|
+
app.use(yourAuthMiddleware); // anything below here requires a session
|
|
666
|
+
|
|
667
|
+
// ✅ Option 2 — exempt /api/iqauth/* from your own auth gate.
|
|
668
|
+
app.use((req, res, next) => {
|
|
669
|
+
if (req.path.startsWith("/api/iqauth/")) return next();
|
|
670
|
+
return yourAuthMiddleware(req, res, next);
|
|
671
|
+
});
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
After the fix you should see `/api/iqauth/callback` return **302**, a `Set-Cookie: iqauth_at=…` header on the response, the cookie appearing under your app's domain in DevTools, and the next `/api/v1/auth/me` returning 200. The `Missing auth credentials` warning drops to the one harmless boot probe per fresh visitor.
|
|
550
675
|
|
|
551
676
|
---
|
|
552
677
|
|
package/dist/react.d.mts
CHANGED
|
@@ -116,6 +116,13 @@ interface IQAuthContextValue {
|
|
|
116
116
|
roleMapper: ((claims: JwtClaims | null) => string | null) | null;
|
|
117
117
|
/** Task #95 — fully resolved localization bundle (default + override). */
|
|
118
118
|
localization: IQAuthLocaleBundle;
|
|
119
|
+
/**
|
|
120
|
+
* 2.6.1 — Whether `<SignIn/>` is allowed to silently resume an active SSO
|
|
121
|
+
* session and redirect the user without showing the form. Opt-in. Default
|
|
122
|
+
* `false` so consumers always see the sign-in UI on first paint unless they
|
|
123
|
+
* explicitly enable it. Per-instance `<SignIn silentSso>` overrides this.
|
|
124
|
+
*/
|
|
125
|
+
silentSso: boolean;
|
|
119
126
|
}
|
|
120
127
|
interface IQAuthProviderProps {
|
|
121
128
|
publishableKey: string;
|
|
@@ -154,6 +161,14 @@ interface IQAuthProviderProps {
|
|
|
154
161
|
* default `enUS` strings.
|
|
155
162
|
*/
|
|
156
163
|
localization?: IQAuthLocaleBundle | IQAuthLocaleOverride;
|
|
164
|
+
/**
|
|
165
|
+
* 2.6.1 — Opt into silent SSO resume in `<SignIn/>` (default `false`).
|
|
166
|
+
* When `true`, `<SignIn/>` will detect an active issuer-side `iq_sso`
|
|
167
|
+
* session and redirect through `/oidc/sso-resume` without rendering the
|
|
168
|
+
* form. When `false` (default) the form always renders and users must
|
|
169
|
+
* click to continue. Per-instance `<SignIn silentSso>` overrides this.
|
|
170
|
+
*/
|
|
171
|
+
silentSso?: boolean;
|
|
157
172
|
children?: ReactNode;
|
|
158
173
|
}
|
|
159
174
|
/**
|
|
@@ -162,7 +177,7 @@ interface IQAuthProviderProps {
|
|
|
162
177
|
* safe — a single SessionManager instance is created per provider and reused
|
|
163
178
|
* across remounts.
|
|
164
179
|
*/
|
|
165
|
-
declare function IQAuthProvider({ publishableKey, issuer, channelName, proactiveRefresh, manager: externalManager, allowedReturnOrigins, appearance, roleMapper, cookieNames, localization, children, }: IQAuthProviderProps): React.FunctionComponentElement<React.ProviderProps<IQAuthContextValue | null>>;
|
|
180
|
+
declare function IQAuthProvider({ publishableKey, issuer, channelName, proactiveRefresh, manager: externalManager, allowedReturnOrigins, appearance, roleMapper, cookieNames, localization, silentSso, children, }: IQAuthProviderProps): React.FunctionComponentElement<React.ProviderProps<IQAuthContextValue | null>>;
|
|
166
181
|
/**
|
|
167
182
|
* Task #95 — Returns the active localization bundle resolved from the
|
|
168
183
|
* `localization` prop on `<IQAuthProvider>` (merged on top of `enUS`). Safe
|
|
@@ -578,6 +593,13 @@ interface SignInProps extends Partial<SharedComponentProps> {
|
|
|
578
593
|
prompt?: "login";
|
|
579
594
|
/** F11 — Per-instance appearance overrides; merged on top of provider-level appearance. */
|
|
580
595
|
appearance?: IQAuthAppearance;
|
|
596
|
+
/**
|
|
597
|
+
* 2.6.1 — Per-instance opt-in to silent SSO resume. Overrides the
|
|
598
|
+
* `<IQAuthProvider silentSso>` value when supplied. Default (when both are
|
|
599
|
+
* unset): `false` — the form always renders and the user must click to
|
|
600
|
+
* continue.
|
|
601
|
+
*/
|
|
602
|
+
silentSso?: boolean;
|
|
581
603
|
}
|
|
582
604
|
/**
|
|
583
605
|
* Pure render-decision helper. When this returns `true`, `<SignIn/>` MUST
|
package/dist/react.d.ts
CHANGED
|
@@ -116,6 +116,13 @@ interface IQAuthContextValue {
|
|
|
116
116
|
roleMapper: ((claims: JwtClaims | null) => string | null) | null;
|
|
117
117
|
/** Task #95 — fully resolved localization bundle (default + override). */
|
|
118
118
|
localization: IQAuthLocaleBundle;
|
|
119
|
+
/**
|
|
120
|
+
* 2.6.1 — Whether `<SignIn/>` is allowed to silently resume an active SSO
|
|
121
|
+
* session and redirect the user without showing the form. Opt-in. Default
|
|
122
|
+
* `false` so consumers always see the sign-in UI on first paint unless they
|
|
123
|
+
* explicitly enable it. Per-instance `<SignIn silentSso>` overrides this.
|
|
124
|
+
*/
|
|
125
|
+
silentSso: boolean;
|
|
119
126
|
}
|
|
120
127
|
interface IQAuthProviderProps {
|
|
121
128
|
publishableKey: string;
|
|
@@ -154,6 +161,14 @@ interface IQAuthProviderProps {
|
|
|
154
161
|
* default `enUS` strings.
|
|
155
162
|
*/
|
|
156
163
|
localization?: IQAuthLocaleBundle | IQAuthLocaleOverride;
|
|
164
|
+
/**
|
|
165
|
+
* 2.6.1 — Opt into silent SSO resume in `<SignIn/>` (default `false`).
|
|
166
|
+
* When `true`, `<SignIn/>` will detect an active issuer-side `iq_sso`
|
|
167
|
+
* session and redirect through `/oidc/sso-resume` without rendering the
|
|
168
|
+
* form. When `false` (default) the form always renders and users must
|
|
169
|
+
* click to continue. Per-instance `<SignIn silentSso>` overrides this.
|
|
170
|
+
*/
|
|
171
|
+
silentSso?: boolean;
|
|
157
172
|
children?: ReactNode;
|
|
158
173
|
}
|
|
159
174
|
/**
|
|
@@ -162,7 +177,7 @@ interface IQAuthProviderProps {
|
|
|
162
177
|
* safe — a single SessionManager instance is created per provider and reused
|
|
163
178
|
* across remounts.
|
|
164
179
|
*/
|
|
165
|
-
declare function IQAuthProvider({ publishableKey, issuer, channelName, proactiveRefresh, manager: externalManager, allowedReturnOrigins, appearance, roleMapper, cookieNames, localization, children, }: IQAuthProviderProps): React.FunctionComponentElement<React.ProviderProps<IQAuthContextValue | null>>;
|
|
180
|
+
declare function IQAuthProvider({ publishableKey, issuer, channelName, proactiveRefresh, manager: externalManager, allowedReturnOrigins, appearance, roleMapper, cookieNames, localization, silentSso, children, }: IQAuthProviderProps): React.FunctionComponentElement<React.ProviderProps<IQAuthContextValue | null>>;
|
|
166
181
|
/**
|
|
167
182
|
* Task #95 — Returns the active localization bundle resolved from the
|
|
168
183
|
* `localization` prop on `<IQAuthProvider>` (merged on top of `enUS`). Safe
|
|
@@ -578,6 +593,13 @@ interface SignInProps extends Partial<SharedComponentProps> {
|
|
|
578
593
|
prompt?: "login";
|
|
579
594
|
/** F11 — Per-instance appearance overrides; merged on top of provider-level appearance. */
|
|
580
595
|
appearance?: IQAuthAppearance;
|
|
596
|
+
/**
|
|
597
|
+
* 2.6.1 — Per-instance opt-in to silent SSO resume. Overrides the
|
|
598
|
+
* `<IQAuthProvider silentSso>` value when supplied. Default (when both are
|
|
599
|
+
* unset): `false` — the form always renders and the user must click to
|
|
600
|
+
* continue.
|
|
601
|
+
*/
|
|
602
|
+
silentSso?: boolean;
|
|
581
603
|
}
|
|
582
604
|
/**
|
|
583
605
|
* Pure render-decision helper. When this returns `true`, `<SignIn/>` MUST
|
package/dist/react.js
CHANGED
|
@@ -1847,6 +1847,7 @@ function IQAuthProvider({
|
|
|
1847
1847
|
roleMapper,
|
|
1848
1848
|
cookieNames,
|
|
1849
1849
|
localization,
|
|
1850
|
+
silentSso,
|
|
1850
1851
|
children
|
|
1851
1852
|
}) {
|
|
1852
1853
|
const managerRef = (0, import_react.useRef)(null);
|
|
@@ -1895,9 +1896,10 @@ function IQAuthProvider({
|
|
|
1895
1896
|
allowedReturnOrigins: allowedReturnOrigins ?? [],
|
|
1896
1897
|
appearance: appearance ?? null,
|
|
1897
1898
|
roleMapper: roleMapper ?? null,
|
|
1898
|
-
localization: resolvedLocalization
|
|
1899
|
+
localization: resolvedLocalization,
|
|
1900
|
+
silentSso: silentSso ?? false
|
|
1899
1901
|
}),
|
|
1900
|
-
[manager, snapshot, allowedReturnOrigins, appearance, roleMapper, resolvedLocalization]
|
|
1902
|
+
[manager, snapshot, allowedReturnOrigins, appearance, roleMapper, resolvedLocalization, silentSso]
|
|
1901
1903
|
);
|
|
1902
1904
|
return (0, import_react.createElement)(IQAuthContext.Provider, { value }, children);
|
|
1903
1905
|
}
|
|
@@ -2421,10 +2423,16 @@ var SHELL_CSS = `
|
|
|
2421
2423
|
.iqauth-sdk-hero { display: none; }
|
|
2422
2424
|
.iqauth-sdk-pane {
|
|
2423
2425
|
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
2424
|
-
padding:
|
|
2426
|
+
padding: 32px 20px;
|
|
2427
|
+
/* 2.6.1 \u2014 Default to natural height so embedded usage (inside a card,
|
|
2428
|
+
modal, or sidebar) sizes to its content instead of forcing a full
|
|
2429
|
+
viewport. The hosted/full-page layout re-introduces 100vh below the
|
|
2430
|
+
768px container-query threshold for the side-by-side hero variant. */
|
|
2431
|
+
min-height: auto;
|
|
2432
|
+
box-sizing: border-box;
|
|
2425
2433
|
}
|
|
2426
2434
|
.iqauth-sdk-card {
|
|
2427
|
-
width: 100%; max-width:
|
|
2435
|
+
width: 100%; max-width: 480px;
|
|
2428
2436
|
background: var(--brand-surface, #ffffff);
|
|
2429
2437
|
border: 1px solid rgba(15,23,42,0.08);
|
|
2430
2438
|
border-radius: var(--brand-radius, 16px);
|
|
@@ -2468,6 +2476,16 @@ var SHELL_CSS = `
|
|
|
2468
2476
|
|
|
2469
2477
|
@container iqauth-sdk (min-width: 768px) {
|
|
2470
2478
|
.iqauth-sdk-shell:not([data-layout="centered_card"]):not([data-layout="full_bleed"]) { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
|
|
2479
|
+
/* 2.6.1 \u2014 Restore 100vh ONLY for the wide side-by-side layout where
|
|
2480
|
+
hero+pane need height parity. Embedded narrow uses keep natural height. */
|
|
2481
|
+
.iqauth-sdk-pane { padding: 48px 24px; min-height: 100vh; }
|
|
2482
|
+
/* 2.6.2 \u2014 Let the card breathe on desktop. The 480px mobile cap looks
|
|
2483
|
+
phone-sized on a 720px+ pane; bump to 540px and grow the inner padding
|
|
2484
|
+
so the form fills its half of the split layout. */
|
|
2485
|
+
.iqauth-sdk-card { max-width: 540px; }
|
|
2486
|
+
.iqauth-sdk-card-header { padding: 40px 44px 0; }
|
|
2487
|
+
.iqauth-sdk-card-body { padding: 32px 44px 32px; }
|
|
2488
|
+
.iqauth-sdk-card-header h1 { font-size: 26px; }
|
|
2471
2489
|
.iqauth-sdk-hero {
|
|
2472
2490
|
display: flex; flex-direction: column; justify-content: space-between;
|
|
2473
2491
|
padding: clamp(32px, 4vw, 56px); color: #ffffff;
|
|
@@ -2489,6 +2507,9 @@ var SHELL_CSS = `
|
|
|
2489
2507
|
}
|
|
2490
2508
|
@container iqauth-sdk (min-width: 1280px) {
|
|
2491
2509
|
.iqauth-sdk-shell:not([data-layout="centered_card"]):not([data-layout="full_bleed"]) { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); }
|
|
2510
|
+
/* 2.6.2 \u2014 Extra-wide screens: card grows once more so the form keeps
|
|
2511
|
+
pace with the hero pane on 1440px+ monitors. */
|
|
2512
|
+
.iqauth-sdk-card { max-width: 580px; }
|
|
2492
2513
|
}
|
|
2493
2514
|
`;
|
|
2494
2515
|
var sdkShellStylesInjected = false;
|
|
@@ -2793,7 +2814,8 @@ function SignIn(props) {
|
|
|
2793
2814
|
const iqAuthBaseUrl = props.iqAuthBaseUrl ?? providerCtx?.manager.issuerUrl ?? "";
|
|
2794
2815
|
const appKey = props.appKey ?? providerCtx?.manager.appKey ?? "";
|
|
2795
2816
|
const returnTo = props.returnTo ?? (typeof window !== "undefined" ? `${window.location.origin}/api/iqauth/callback` : "");
|
|
2796
|
-
const { onRedirect, className, prompt, appearance: instanceAppearance } = props;
|
|
2817
|
+
const { onRedirect, className, prompt, appearance: instanceAppearance, silentSso: instanceSilentSso } = props;
|
|
2818
|
+
const silentSsoEnabled = instanceSilentSso ?? providerCtx?.silentSso ?? false;
|
|
2797
2819
|
const appearance = instanceAppearance && providerCtx?.appearance ? { elements: { ...providerCtx.appearance.elements, ...instanceAppearance.elements } } : instanceAppearance ?? providerCtx?.appearance ?? null;
|
|
2798
2820
|
if (!iqAuthBaseUrl || !appKey) {
|
|
2799
2821
|
console.error(
|
|
@@ -2823,6 +2845,7 @@ function SignIn(props) {
|
|
|
2823
2845
|
const [forcePrompt, setForcePrompt] = (0, import_react.useState)(false);
|
|
2824
2846
|
const effectivePrompt = (0, import_react.useMemo)(() => {
|
|
2825
2847
|
if (prompt === "login" || forcePrompt) return "login";
|
|
2848
|
+
if (!silentSsoEnabled) return "login";
|
|
2826
2849
|
if (typeof window !== "undefined") {
|
|
2827
2850
|
try {
|
|
2828
2851
|
if (new URLSearchParams(window.location.search).get("prompt") === "login") return "login";
|
|
@@ -2830,7 +2853,7 @@ function SignIn(props) {
|
|
|
2830
2853
|
}
|
|
2831
2854
|
}
|
|
2832
2855
|
return void 0;
|
|
2833
|
-
}, [prompt, forcePrompt]);
|
|
2856
|
+
}, [prompt, forcePrompt, silentSsoEnabled]);
|
|
2834
2857
|
const oidcPayload = () => ({
|
|
2835
2858
|
client_id: ctx?.app.defaultClientId,
|
|
2836
2859
|
redirect_uri: returnTo,
|
package/dist/react.mjs
CHANGED
|
@@ -102,6 +102,7 @@ function IQAuthProvider({
|
|
|
102
102
|
roleMapper,
|
|
103
103
|
cookieNames,
|
|
104
104
|
localization,
|
|
105
|
+
silentSso,
|
|
105
106
|
children
|
|
106
107
|
}) {
|
|
107
108
|
const managerRef = useRef(null);
|
|
@@ -150,9 +151,10 @@ function IQAuthProvider({
|
|
|
150
151
|
allowedReturnOrigins: allowedReturnOrigins ?? [],
|
|
151
152
|
appearance: appearance ?? null,
|
|
152
153
|
roleMapper: roleMapper ?? null,
|
|
153
|
-
localization: resolvedLocalization
|
|
154
|
+
localization: resolvedLocalization,
|
|
155
|
+
silentSso: silentSso ?? false
|
|
154
156
|
}),
|
|
155
|
-
[manager, snapshot, allowedReturnOrigins, appearance, roleMapper, resolvedLocalization]
|
|
157
|
+
[manager, snapshot, allowedReturnOrigins, appearance, roleMapper, resolvedLocalization, silentSso]
|
|
156
158
|
);
|
|
157
159
|
return createElement(IQAuthContext.Provider, { value }, children);
|
|
158
160
|
}
|
|
@@ -676,10 +678,16 @@ var SHELL_CSS = `
|
|
|
676
678
|
.iqauth-sdk-hero { display: none; }
|
|
677
679
|
.iqauth-sdk-pane {
|
|
678
680
|
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
679
|
-
padding:
|
|
681
|
+
padding: 32px 20px;
|
|
682
|
+
/* 2.6.1 \u2014 Default to natural height so embedded usage (inside a card,
|
|
683
|
+
modal, or sidebar) sizes to its content instead of forcing a full
|
|
684
|
+
viewport. The hosted/full-page layout re-introduces 100vh below the
|
|
685
|
+
768px container-query threshold for the side-by-side hero variant. */
|
|
686
|
+
min-height: auto;
|
|
687
|
+
box-sizing: border-box;
|
|
680
688
|
}
|
|
681
689
|
.iqauth-sdk-card {
|
|
682
|
-
width: 100%; max-width:
|
|
690
|
+
width: 100%; max-width: 480px;
|
|
683
691
|
background: var(--brand-surface, #ffffff);
|
|
684
692
|
border: 1px solid rgba(15,23,42,0.08);
|
|
685
693
|
border-radius: var(--brand-radius, 16px);
|
|
@@ -723,6 +731,16 @@ var SHELL_CSS = `
|
|
|
723
731
|
|
|
724
732
|
@container iqauth-sdk (min-width: 768px) {
|
|
725
733
|
.iqauth-sdk-shell:not([data-layout="centered_card"]):not([data-layout="full_bleed"]) { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
|
|
734
|
+
/* 2.6.1 \u2014 Restore 100vh ONLY for the wide side-by-side layout where
|
|
735
|
+
hero+pane need height parity. Embedded narrow uses keep natural height. */
|
|
736
|
+
.iqauth-sdk-pane { padding: 48px 24px; min-height: 100vh; }
|
|
737
|
+
/* 2.6.2 \u2014 Let the card breathe on desktop. The 480px mobile cap looks
|
|
738
|
+
phone-sized on a 720px+ pane; bump to 540px and grow the inner padding
|
|
739
|
+
so the form fills its half of the split layout. */
|
|
740
|
+
.iqauth-sdk-card { max-width: 540px; }
|
|
741
|
+
.iqauth-sdk-card-header { padding: 40px 44px 0; }
|
|
742
|
+
.iqauth-sdk-card-body { padding: 32px 44px 32px; }
|
|
743
|
+
.iqauth-sdk-card-header h1 { font-size: 26px; }
|
|
726
744
|
.iqauth-sdk-hero {
|
|
727
745
|
display: flex; flex-direction: column; justify-content: space-between;
|
|
728
746
|
padding: clamp(32px, 4vw, 56px); color: #ffffff;
|
|
@@ -744,6 +762,9 @@ var SHELL_CSS = `
|
|
|
744
762
|
}
|
|
745
763
|
@container iqauth-sdk (min-width: 1280px) {
|
|
746
764
|
.iqauth-sdk-shell:not([data-layout="centered_card"]):not([data-layout="full_bleed"]) { grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); }
|
|
765
|
+
/* 2.6.2 \u2014 Extra-wide screens: card grows once more so the form keeps
|
|
766
|
+
pace with the hero pane on 1440px+ monitors. */
|
|
767
|
+
.iqauth-sdk-card { max-width: 580px; }
|
|
747
768
|
}
|
|
748
769
|
`;
|
|
749
770
|
var sdkShellStylesInjected = false;
|
|
@@ -1048,7 +1069,8 @@ function SignIn(props) {
|
|
|
1048
1069
|
const iqAuthBaseUrl = props.iqAuthBaseUrl ?? providerCtx?.manager.issuerUrl ?? "";
|
|
1049
1070
|
const appKey = props.appKey ?? providerCtx?.manager.appKey ?? "";
|
|
1050
1071
|
const returnTo = props.returnTo ?? (typeof window !== "undefined" ? `${window.location.origin}/api/iqauth/callback` : "");
|
|
1051
|
-
const { onRedirect, className, prompt, appearance: instanceAppearance } = props;
|
|
1072
|
+
const { onRedirect, className, prompt, appearance: instanceAppearance, silentSso: instanceSilentSso } = props;
|
|
1073
|
+
const silentSsoEnabled = instanceSilentSso ?? providerCtx?.silentSso ?? false;
|
|
1052
1074
|
const appearance = instanceAppearance && providerCtx?.appearance ? { elements: { ...providerCtx.appearance.elements, ...instanceAppearance.elements } } : instanceAppearance ?? providerCtx?.appearance ?? null;
|
|
1053
1075
|
if (!iqAuthBaseUrl || !appKey) {
|
|
1054
1076
|
console.error(
|
|
@@ -1078,6 +1100,7 @@ function SignIn(props) {
|
|
|
1078
1100
|
const [forcePrompt, setForcePrompt] = useState(false);
|
|
1079
1101
|
const effectivePrompt = useMemo(() => {
|
|
1080
1102
|
if (prompt === "login" || forcePrompt) return "login";
|
|
1103
|
+
if (!silentSsoEnabled) return "login";
|
|
1081
1104
|
if (typeof window !== "undefined") {
|
|
1082
1105
|
try {
|
|
1083
1106
|
if (new URLSearchParams(window.location.search).get("prompt") === "login") return "login";
|
|
@@ -1085,7 +1108,7 @@ function SignIn(props) {
|
|
|
1085
1108
|
}
|
|
1086
1109
|
}
|
|
1087
1110
|
return void 0;
|
|
1088
|
-
}, [prompt, forcePrompt]);
|
|
1111
|
+
}, [prompt, forcePrompt, silentSsoEnabled]);
|
|
1089
1112
|
const oidcPayload = () => ({
|
|
1090
1113
|
client_id: ctx?.app.defaultClientId,
|
|
1091
1114
|
redirect_uri: returnTo,
|
package/package.json
CHANGED