> ## Documentation Index
> Fetch the complete documentation index at: https://veriqa.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Sign in with a Google or Apple account, confirmed on the phone

> The sign-in device shows a QR code; the user signs in to Google or Apple on their own phone and confirms the sign-in there. No password is typed on the sign-in device.

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](https://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](/docs/concepts/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](https://developers.google.com/identity/openid-connect/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](/docs/concepts/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](/docs/guides/custom-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](/docs/guides/supported-channels#pseudo-channels-google-apple-and-huawei); a separate
service for signing in with Google, Apple and Huawei is upcoming — see [Coming next](/docs/releases/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"](https://developers.google.com/identity/protocols/oauth2/policies),
  [Google: authorization errors](https://developers.google.com/identity/protocols/oauth2/web-server)).
* **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](https://developers.google.com/identity/openid-connect/openid-connect)),
  and the scopes Apple accepts are `name` and `email`
  ([Apple: request an authorization](https://developer.apple.com/documentation/signinwithapple/incorporating-sign-in-with-apple-into-other-platforms)).
  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](https://developer.apple.com/documentation/signinwithapple/communicating-using-the-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](https://developer.apple.com/documentation/signinwithapple/incorporating-sign-in-with-apple-into-other-platforms)),
  and only the first time the user signs in
  ([Apple: authenticating users](https://developer.apple.com/documentation/signinwithapple/authenticating-users-with-sign-in-with-apple)).
  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

* [Sign-in surfaces](/docs/scenarios/surfaces) — where a sign-in can start and how surfaces differ for the
  integration.
* [Smart TV and keyboard-free surfaces](/docs/scenarios/smart-tv) — sign-in where there is nothing to type
  on.
* [Build your own channel adapter](/docs/guides/custom-channel-adapter) — the SPI the sample's adapter is
  written against.
* [Samples](/docs/samples) — every sample in the repository.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.