SETUP GUIDE

Add Sign in with ZapQR to your site

Passwordless sign-in for your visitors — passkeys and email magic links over standard OpenID Connect. About 10 minutes, no code required for WordPress or Drupal.

Using the ZapQR app to fill passwords on a computer? That's the QR autofill setup — this page is for site owners.

1 Get your Client ID and Secret

Every site that uses Sign in with ZapQR needs its own credentials. They're free and self-serve:

Go to auth.zapqr.ai/account and sign in — the first time, that just means entering your email and clicking the magic link we send you. Once you're in, add a passkey so future sign-ins are one touch.

Under Your sites, choose your platform (WordPress, Shopify, or Custom), enter your site's URL, and click Add site. You'll get a Client ID and a Client Secret.

The secret is shown once. Copy it somewhere safe before closing the page — if you lose it, use Rotate to mint a new one.

For WordPress sites, the redirect URL is derived automatically from your site URL — you don't need to configure it. For other platforms, paste the redirect (callback) URL your plugin shows you.

2 WordPress

Install ZapQR Login from the WordPress plugin directory (version 1.1.0 or later) — in your admin, Plugins → Add New, search "ZapQR Login", install and activate.

Then in Settings → ZapQR Login:

  • Paste your Client ID and Client Secret from step 1.
  • Leave the Identity Provider as https://auth.zapqr.ai.
  • Optionally enable Link Existing Users so people who already have a WordPress account with the same (verified) email get connected to it instead of a new account.

That's it — your login page now shows a Sign in with ZapQR button. New visitors are provisioned with your default role; signing out of WordPress also signs them out of ZapQR for that site.

Requires WordPress 5.8+ and HTTPS on your site.

3 Drupal

Install the OpenID Connect / OAuth client module, then add a Generic OAuth 2.0 client:

  • Client ID / Client secret: from step 1 — when adding your site at auth.zapqr.ai, choose Custom, paste the redirect URL Drupal shows you (it looks like https://yoursite.com/openid-connect/<name>), and set the token auth method to client_secret_post.
  • Authorization endpoint: https://auth.zapqr.ai/auth
  • Token endpoint: https://auth.zapqr.ai/token
  • UserInfo endpoint: https://auth.zapqr.ai/me
  • Scopes: openid email

Enable the client, and the OpenID Connect login option appears on your Drupal login form.

4 Any OpenID Connect platform

ZapQR is a standard OpenID Connect provider — Ghost, Discourse, Grafana, Nextcloud, and most SaaS admin panels can consume it with their OIDC / SSO plugin. Most tools only need the discovery URL:

https://auth.zapqr.ai/.well-known/openid-configuration
Setting Value
Issuerhttps://auth.zapqr.ai
Authorizationhttps://auth.zapqr.ai/auth
Tokenhttps://auth.zapqr.ai/token
UserInfohttps://auth.zapqr.ai/me
JWKShttps://auth.zapqr.ai/jwks
End session (logout)https://auth.zapqr.ai/session/end
Scopesopenid email profile
FlowAuthorization code (PKCE S256 supported)
Token authclient_secret_basic (default) or client_secret_post — selectable per site
Claimssub, email, email_verified, name

Refresh tokens and RP-initiated logout are supported. Every account's email is verified before your platform ever sees it, so email_verified is honest.

5 Scan-to-sign-in QR on your login page

Every Sign in with ZapQR integration already includes QR sign-in. There's nothing extra to install and no QR library to host. Your visitor clicks your button, taps Scan with your phone on the ZapQR page, scans the code, and taps the matching number on their phone. Your site gets a signed-in user and never handles a password.

1 · Your login page

Sign in with ZapQR

They click your button.

2 · auth.zapqr.ai

Scan with your phone

A QR and a number appear.

3 · Their phone

They tap the number the screen shows.

4 · Back on your site

✓ Signed in

You receive their verified email.

They can scan with the phone's camera or the ZapQR app. The same page also offers passkeys, Approve on my phone (a push to a phone they've already set up), and an email link, so nobody gets stuck.

Put the button on your login page

WordPress or Drupal: done already. Steps 2 and 3 add the button to your login form, and QR sign-in comes with it.

Your own site, any framework: send the button to a route on your server that starts the sign-in, then handle the callback. Here's a complete Express 5 example using openid-client. Register the site as Custom in step 1 and paste https://yoursite.com/auth/zapqr/callback as the redirect URL.

import express from 'express';
import session from 'express-session';
import * as oidc from 'openid-client';

const SITE = 'https://yoursite.com';
const config = await oidc.discovery(
  new URL('https://auth.zapqr.ai'),
  process.env.ZAPQR_CLIENT_ID,
  process.env.ZAPQR_CLIENT_SECRET,
);

const app = express();
app.use(session({
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: { secure: true, httpOnly: true, sameSite: 'lax' },
}));

// Your "Sign in with ZapQR" button links here.
app.get('/auth/zapqr/start', async (req, res) => {
  const verifier = oidc.randomPKCECodeVerifier();
  const state = oidc.randomState();
  req.session.zapqr = { verifier, state };
  const url = oidc.buildAuthorizationUrl(config, {
    redirect_uri: `${SITE}/auth/zapqr/callback`,
    scope: 'openid email profile',
    code_challenge: await oidc.calculatePKCECodeChallenge(verifier),
    code_challenge_method: 'S256',
    state,
  });
  res.redirect(url.href);
});

app.get('/auth/zapqr/callback', async (req, res) => {
  const { verifier, state } = req.session.zapqr ?? {};
  const tokens = await oidc.authorizationCodeGrant(
    config,
    new URL(req.originalUrl, SITE),
    { pkceCodeVerifier: verifier, expectedState: state },
  );
  const { sub, email } = tokens.claims();
  delete req.session.zapqr;
  // Find or create your user by `sub` (stable), not by email.
  req.session.user = { sub, email, idToken: tokens.id_token };
  res.redirect('/');
});

app.get('/logout', (req, res) => {
  const idToken = req.session.user?.idToken;
  req.session.destroy(() => {
    res.redirect(oidc.buildEndSessionUrl(config, {
      id_token_hint: idToken,
      post_logout_redirect_uri: `${SITE}/`,
    }).href);
  });
});

Your site's root and /login are registered as sign-out return addresses automatically. Add any others in the Extra post-logout URL(s) field when you add the site.

Sites on Supabase Auth: add ZapQR as a custom OpenID Connect provider, and Supabase handles the exchange and creates an ordinary Supabase session.

  1. In step 1, choose Custom and paste your Supabase callback URL, https://<project-ref>.supabase.co/auth/v1/callback.
  2. In your Supabase project's auth provider settings, add a custom OpenID Connect provider with the identifier zapqr, issuer https://auth.zapqr.ai, and your Client ID and Secret. Don't use the built-in Keycloak provider: it expects Keycloak's URL layout and won't read ZapQR's discovery document.
  3. Add your site's address to Supabase's allowed redirect URLs.
  4. Wire the button:
// Sign in
await supabase.auth.signInWithOAuth({
  provider: 'custom:zapqr',
  options: { scopes: 'openid email profile', redirectTo: `${location.origin}/` },
});

// Sign out of your site, then offer to sign out of ZapQR too
await supabase.auth.signOut();
location.href = 'https://auth.zapqr.ai/session/end';

A button that looks right

Use any styling you like. This one matches the ZapQR mark and works on light or dark pages:

<a href="/auth/zapqr/start" style="display:inline-flex;align-items:center;gap:10px;
   padding:12px 20px;border-radius:8px;background:#0E131F;color:#fff;
   font:500 15px/1 system-ui,sans-serif;text-decoration:none">
  <img src="https://zapqr.ai/favicon.svg" alt="" width="20" height="20"
       style="border-radius:4px">
  Sign in with ZapQR
</a>

Why the scan is safe to offer

  • A photo of the QR is useless. The code in the QR only asks for approval. The secret that collects the session never leaves the browser that showed it.
  • Number matching. The phone offers several numbers and the visitor must tap the one on the screen. One wrong tap cancels the code, so a QR relayed from somewhere else fails.
  • Right account, or no sign-in. If the visitor typed an email on the screen, a phone signed in to a different ZapQR account is refused and offered a switch.
  • Shared computers. Ticking I'm on a shared or public computer makes the session end by itself, with End and Extend controls on the visitor's phone.
  • You never hold a password. Your site receives sub, a verified email and (optionally) name.

What your visitors experience

They click Sign in with ZapQR and land on auth.zapqr.ai. First-timers enter their email and click a magic link — that's the whole signup. Then they're offered a passkey, so every sign-in after that is Face ID or Touch ID, no typing.

They approve sharing their email with your site once, then return signed in. No password ever existed, so there's nothing for your database to leak and nothing for a phishing page to steal.

If the screen can't use a passkey — a TV, a kiosk, a shared computer — they tap Scan with your phone and approve on their phone instead (see step 5). Your integration gets this automatically.

Stuck, or on a platform we haven't documented?

If your platform speaks OIDC, we'll get you running. Shopify support for new customer accounts is in the works.