Skip to main content
The sign-in device shows a QR code. The user scans it with their phone, signs in to their Google or Apple account on the phone and confirms the sign-in there, and the sign-in completes on the device. No password is typed on the sign-in device at all. What sets this apart from an ordinary “Sign in with Google” button is where the credentials are entered. The button opens the provider’s sign-in in the browser of the device the user is signing in on, so the Google username and password are typed there. Here the provider’s sign-in happens on the phone, and the sign-in device receives only the result. This is how the samples/dotnet/pseudo-channels sample in the repository (veriqa.app/source) works. Its README.md explains how to set it up and run it: you need a public HTTPS address, a Google OAuth client and an Apple Services ID.

When it helps

A device where your users would rather not type their passwords:
  • someone else’s computer or a shared one;
  • a colleague’s workstation;
  • a computer in a hotel, a coworking space or a classroom;
  • a kiosk or a terminal at a counter.
A surface with no keyboard:
  • a TV or a set-top box, where a password would have to be typed with a remote;
  • a kiosk with a touch screen;
  • a device with a few buttons;
  • a screen in a meeting room.

How it works

  1. The sign-in device starts an ordinary OpenID Connect sign-in through /connect/authorize. The Veriqa sign-in page shows a QR code with a hop mode link.
  2. The user scans the QR code with the phone camera. The phone opens the hop page of the node; when both providers are enabled, the user picks Google or Apple.
  3. The sample host sends the phone to the provider with the authorization code flow. The user picks an account. If they are not yet signed in to Google or Apple on the phone, they sign in now and type the provider password on the phone.
  4. The provider returns the phone to the sample’s return address, and the host validates the claims it received.
  5. The host reports the start to the core with a signed webhook. The core asks its question through the adapter, and the adapter keeps the question context for the transaction.
  6. The phone shows the Confirm the sign-in page: the application being signed in to, the browser and system the sign-in started from, the location, the start time and the account. The user taps Yes, sign me in or No.
  7. The host passes the answer to the core with a signed webhook: a confirmation or a decline.
  8. The sign-in device completes the OpenID Connect flow, and the phone shows the outcome.
Your application receives a sub of the form google-id:<the user's identifier at Google> or apple-id:<the user's identifier at Apple>, along with channel_user_id, channel_type, email and email_verified. If the channel link is opened in the browser of the sign-in device itself rather than scanned with a phone, the flow becomes an ordinary sign-in at the provider followed by the same question page. Nothing cross-device happens then, and nothing breaks.

Why the Yes/No step

A provider may return a result without asking anything: without prompt, Google shows a consent screen only to a user who has not authorized the application yet, so on a phone signed in to a single Google account that has authorized it, the request goes through with no screen at all. The sample sends prompt=select_account so that the account chooser at least shows which account is used (Google: OpenID Connect). Choosing an account also says nothing about whose sign-in it completes. Without step 6, an attacker could show a victim the QR code of the attacker’s own sign-in, and the victim’s sign-in at Google would silently complete it — on the attacker’s device, under the victim’s account. The question page names the application and the device the sign-in started from, so the victim can answer No. The question and the answer are bound to one sign-in:
  • the question page works only in the browser that has just signed in at the provider: the host sets a short-lived cookie in it after the provider’s return;
  • a second account that signs in at the provider for the same sign-in sees neither the details nor the buttons;
  • a sign-in that was canceled or timed out is not asked about again.
When the core withholds the details of the initiator, the page still asks, with only the account above the buttons. What is shown about whoever started the sign-in is covered in Initiator context.

What it is not

Google and Apple are not built-in channels of Veriqa. This is a sample built on the public channel SPI (Build your own channel adapter): one IChannelAdapter class is registered twice, as google-id and as apple-id, and depends on Veriqa.Core.Contracts only. The sample host itself plays the platform behind the channel. Nor is it a delivery channel. There is no chat or mailbox behind it, so no message can be sent to the user “in the channel”: the question “is it you signing in?” is asked by a page of the sample on the phone. Where pseudo-channels sit among the other channels is covered in Pseudo-channels; a separate service for signing in with Google, Apple and Huawei is upcoming — see Coming next.

What to keep in mind

  • Scan the QR code with the system camera, not inside a messaging app. A QR code or a link opened in an app’s built-in browser reaches Google in an embedded user agent, and Google refuses the sign-in with disallowed_useragent (Google: OAuth 2.0 policies, “Use secure browsers”, Google: authorization errors).
  • No phone number. The ID token claims Google documents are sub, email, email_verified, name, given_name, family_name, picture and locale (Google: OpenID Connect), and the scopes Apple accepts are name and email (Apple: request an authorization). An application that needs a verified phone number needs a channel that has one.
  • Apple: the email may be a relay address. A user may choose an anonymous address that forwards to their real one; mail reaches it only from sender domains registered with Apple (Apple: private email relay service). Treat email as a contact address, not as an identifier: the identifier is sub.
  • Apple: no name. The sample requests openid email only. Apple returns the name in a separate user field of the authorization response rather than as an ID token claim (Apple: request an authorization), and only the first time the user signs in (Apple: authenticating users). The display name of an Apple user is therefore their email address.
  • State lives in memory. The question context and the outcome are kept in the host process for the lifetime of the transaction (Veriqa:TransactionEngine:TransactionTtlSeconds). A restart drops every sign-in in progress.

See also