Web SDK
Add biometric login to a web app with one client object. The SDK handles the OAuth round-trip to Wink's hosted capture flow, then hands you a Keycloak-style token your backend can verify.
This page reflects the integration WinkKey ships in production. Code samples are pulled from winkkey_app/web/ and pre-bake the workarounds for the SDK's known sharp edges (see Gotchas).
Install
The Web SDK ships from npm.
pnpm add wink-identity-sdk
# or
npm install wink-identity-sdk
# or
yarn add wink-identity-sdk
It's a tiny ESM module — no peer deps. Use it in any modern bundler (Next.js, Vite, Remix, plain Webpack).
Configure
The SDK exports a getWinkLoginClient(config) factory that returns a Keycloak-style client. Build the config from environment variables your merchant dashboard issued:
export const getWinkConfig = (sessionId?: string) => {
const config = {
clientId: process.env.NEXT_PUBLIC_WINK_CLIENT_ID!,
realm: process.env.NEXT_PUBLIC_WINK_REALM!,
loggingEnabled: true,
cancelUrl: `${window.location.origin}/callback`,
onAuthErrorFailure: (e: unknown) => console.error("Wink auth error:", e),
override: true,
overrideValues: {
// Stage hosts — what every external developer's keys point at.
BASE_URL: "https://stagelogin-api.winkapis.com",
AUTH_URL: "https://stagelogin-api.winkapis.com/auth",
},
};
if (sessionId) config.sessionId = sessionId;
return config;
};
Start a login
Logging in is a two-step pas-de-deux: your backend mints a sessionId from the Wink Core API, then the SDK redirects the browser into Wink's hosted capture flow with that session as state. Don't try to skip the backend session step — anonymous calls fail.
-
Mint a sessionId on your backend
Hit
POST /wink/v1/sessionfrom your server with merchant Basic auth. The response containssessionId— surface it to the browser via your own API endpoint. -
Pass the sessionId into the SDK and call
winkInitTypeScript web/components/WinkAuthProvider.tsxconst login = async () => { const sessionId = await fetchSessionFromBackend("/api/wink/session"); const client = getWinkLoginClient(getWinkConfig(sessionId)); client.winkInit({ onLoad: "login-required", checkLoginIframe: false, // always — see Gotcha #2 onFailure(error) { console.error(error); }, }); };The SDK redirects the browser to Wink's hosted capture flow. The user enrols or authenticates, then bounces back to your
cancelUrlwith an authorization code in the URL fragment.
Handle the callback
On return, the SDK has a separate code path for redeeming the authorization code. Critical: only call winkInit when a code is actually present in the URL — calling it on every mount produces redirect loops (see Gotcha #1).
useEffect(() => {
const client = getWinkLoginClient(getWinkConfig()); // no sessionId on return
// OAuth code can land in either the query string or the hash fragment.
const haveCode = /[?#&]code=/.test(window.location.search) ||
/[?#&]code=/.test(window.location.hash);
if (!haveCode) return; // fresh load — idle
client.winkInit({
checkLoginIframe: false,
onSuccess() {
const token = (client as any).token;
// hydrate user profile via your backend, set authenticated state
},
onFailure(error) {
console.error("winkInit onFailure:", error);
},
});
}, []);
Logout
Call winkLogout to end the merchant OIDC session. Note that this does not end the underlying Wink realm SSO cookie — see Gotcha #5.
const logout = async () => {
if ((client as any).authenticated) {
await client.winkLogout({
redirectUri: window.location.origin,
});
return; // winkLogout redirects; nothing more to do
}
// Local-only sign-out fallback
window.location.assign(window.location.origin);
};
Gotchas
Eight things WinkKey paid for in integration time. Front-load these — most show up only on iPhone Safari or in production Keycloak configurations, so they're easy to miss in dev.
Don't call winkInit on initial mount
The SDK has two auto-redirect paths that loop on a fresh page load. Only call winkInit when you've detected an OAuth callback (code= present in the URL). On a cold load, just instantiate the client and idle.
Always pass checkLoginIframe: false
The silent-SSO iframe fires after the real OAuth callback on iPhone Safari. Its error=login_required bubbles up and clobbers the callback's code=. Setting checkLoginIframe: false on every winkInit call avoids the race.
Configure both Valid Redirect URIs and Web Origins in Keycloak
The Wink Keycloak client requires both fields. The docs only mention redirect URIs. Without Web Origins, your POST /token exchange fails with a CORS error after the redirect already worked — confusing because the redirect itself succeeded. Ask your Wink admin to add your origin to both.
The OAuth code can land in the hash, not the query
Wink's Keycloak uses response_mode=fragment, so the code shows up after # rather than ?. Server logs never see fragments — easy to miss. Test for both: /[?#&]code=/.test(location.search) or ...test(location.hash).
Don't pass sessionId on the return leg
sessionId is for starting a login. The token-exchange endpoint rejects a stale sessionId on the return leg with an opaque "Load failed". Build a sessionId-less config when re-creating the client to redeem the code.
localStorage.login_session_state can trigger redirect loops
If login_session_state === "1" is set and silent-SSO fails, the SDK auto-redirects. There's no public API to disable. Workaround: clear it on every mount before instantiating the client.
winkLogout doesn't end the realm SSO
It terminates the merchant OIDC client session only. The user's underlying Keycloak realm cookie remains. The next login against any Wink merchant on the same browser silently re-authenticates without a capture step. If you need a hard logout, call the realm's end_session_endpoint directly after winkLogout.
Error messages are generic
onFailure typically receives Error("Load failed") with no status code or Keycloak error. CORS, network, and 4xx all look identical. Wrap your onFailure with a network-tab check during development; the real cause is almost always visible there.
Reporting bugs: the WinkKey team is the upstream channel for SDK feedback while the public tracker is being stood up. File issues at winkkey/wink-web-feedback.md with reproduction steps; the Wink web team triages weekly.