@iqauth/sdk 2.6.1 → 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 +68 -0
- package/dist/react.js +11 -1
- package/dist/react.mjs +11 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,6 +17,7 @@ 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)
|
|
20
21
|
- [What's new in 2.6.1](#whats-new-in-261)
|
|
21
22
|
- [What's new in 2.0.3](#whats-new-in-203)
|
|
22
23
|
- [Browser apps with a backend (recommended)](#browser-apps-with-a-backend-recommended)
|
|
@@ -173,6 +174,25 @@ The SDK is not "one auth model for every environment". The safe pattern depends
|
|
|
173
174
|
|
|
174
175
|
---
|
|
175
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
|
+
|
|
176
196
|
## What's new in 2.6.1
|
|
177
197
|
|
|
178
198
|
### 1. Silent SSO is now opt-in (default off)
|
|
@@ -604,6 +624,54 @@ Common codes you'll handle:
|
|
|
604
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. |
|
|
605
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. |
|
|
606
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.
|
|
607
675
|
|
|
608
676
|
---
|
|
609
677
|
|
package/dist/react.js
CHANGED
|
@@ -2432,7 +2432,7 @@ var SHELL_CSS = `
|
|
|
2432
2432
|
box-sizing: border-box;
|
|
2433
2433
|
}
|
|
2434
2434
|
.iqauth-sdk-card {
|
|
2435
|
-
width: 100%; max-width:
|
|
2435
|
+
width: 100%; max-width: 480px;
|
|
2436
2436
|
background: var(--brand-surface, #ffffff);
|
|
2437
2437
|
border: 1px solid rgba(15,23,42,0.08);
|
|
2438
2438
|
border-radius: var(--brand-radius, 16px);
|
|
@@ -2479,6 +2479,13 @@ var SHELL_CSS = `
|
|
|
2479
2479
|
/* 2.6.1 \u2014 Restore 100vh ONLY for the wide side-by-side layout where
|
|
2480
2480
|
hero+pane need height parity. Embedded narrow uses keep natural height. */
|
|
2481
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; }
|
|
2482
2489
|
.iqauth-sdk-hero {
|
|
2483
2490
|
display: flex; flex-direction: column; justify-content: space-between;
|
|
2484
2491
|
padding: clamp(32px, 4vw, 56px); color: #ffffff;
|
|
@@ -2500,6 +2507,9 @@ var SHELL_CSS = `
|
|
|
2500
2507
|
}
|
|
2501
2508
|
@container iqauth-sdk (min-width: 1280px) {
|
|
2502
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; }
|
|
2503
2513
|
}
|
|
2504
2514
|
`;
|
|
2505
2515
|
var sdkShellStylesInjected = false;
|
package/dist/react.mjs
CHANGED
|
@@ -687,7 +687,7 @@ var SHELL_CSS = `
|
|
|
687
687
|
box-sizing: border-box;
|
|
688
688
|
}
|
|
689
689
|
.iqauth-sdk-card {
|
|
690
|
-
width: 100%; max-width:
|
|
690
|
+
width: 100%; max-width: 480px;
|
|
691
691
|
background: var(--brand-surface, #ffffff);
|
|
692
692
|
border: 1px solid rgba(15,23,42,0.08);
|
|
693
693
|
border-radius: var(--brand-radius, 16px);
|
|
@@ -734,6 +734,13 @@ var SHELL_CSS = `
|
|
|
734
734
|
/* 2.6.1 \u2014 Restore 100vh ONLY for the wide side-by-side layout where
|
|
735
735
|
hero+pane need height parity. Embedded narrow uses keep natural height. */
|
|
736
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; }
|
|
737
744
|
.iqauth-sdk-hero {
|
|
738
745
|
display: flex; flex-direction: column; justify-content: space-between;
|
|
739
746
|
padding: clamp(32px, 4vw, 56px); color: #ffffff;
|
|
@@ -755,6 +762,9 @@ var SHELL_CSS = `
|
|
|
755
762
|
}
|
|
756
763
|
@container iqauth-sdk (min-width: 1280px) {
|
|
757
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; }
|
|
758
768
|
}
|
|
759
769
|
`;
|
|
760
770
|
var sdkShellStylesInjected = false;
|
package/package.json
CHANGED