Wink Developers
Home Wink Core SDKs Web Layer 1 · Primitives
Wink Core · SDK

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.

Package wink-identity-sdk Version 0.2.0+ Distribution npm Modules ESM Environment stage
i

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.

Shell terminal
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:

TypeScript web/lib/winkAuth.ts
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.

  1. Mint a sessionId on your backend

    Hit POST /wink/v1/session from your server with merchant Basic auth. The response contains sessionId — surface it to the browser via your own API endpoint.

  2. Pass the sessionId into the SDK and call winkInit

    TypeScript web/components/WinkAuthProvider.tsx
    const 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 cancelUrl with 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).

TypeScript web/components/WinkAuthProvider.tsx
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.

TypeScript web/components/WinkAuthProvider.tsx
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.

1

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.

Blocker
2

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.

High
3

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.

Blocker
4

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).

High
5

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.

High
6

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.

Medium
7

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.

Medium
8

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.

Medium
!

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.

core / sdks / web · v0