Files
nhost/examples/demos/ReactNativeDemo/README_NATIVE_AUTHENTICATION.md
David Barroso 3a41251caf feat: added examples/demos (#3459)
### **PR Type**
Enhancement, Tests


___

### **Description**
- Add multi-framework auth demos and examples
  - React Native, React, Next.js SSR, Vue, SvelteKit, Express
  - Email/password, magic link, MFA, WebAuthn flows
  - File upload, profile, logs viewing

___



<details> <summary><h3> File Walkthrough</h3></summary>

<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><details><summary>18
files</summary><table>
<tr>
<td><strong>MFASettings.tsx</strong><dd><code>Add React Native MFA
settings screen</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-5d346e2efc6817f6c3de4360c27031e4d5a4f37423aedb329f645aa9ac33d6e8">+594/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>upload.tsx</strong><dd><code>Add React Native file upload
screen</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-3763882523f551153b1520c05a953fdd2b3e7e0c37e2db53318e6a2f05d09b9f">+552/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>Upload.tsx</strong><dd><code>Add React SPA file upload
page</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-aadcba440d58667ce429ae0caad0695a7dddc8d91e1b8f0dc52ce6633b0eddc4">+444/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>SecurityKeys.tsx</strong><dd><code>Add React SPA WebAuthn
security keys</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-3f9cf11913bfd3220c4c220618b5575c6a3ed59ffa42d6d5be32c77edfc7d610">+404/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>client.tsx</strong><dd><code>Add Next.js SSR file upload
client</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-f6fa7dea8d8baf6d7fcefaf4517a492dbfca213afcf6b5d08d180a889947c0e5">+399/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>SecurityKeyClient.tsx</strong><dd><code>Add Next.js SSR
WebAuthn key client</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-c05c21b6e3c47e8d17ba69ebba93e3ccd4266e26fd4062e6de552ed98d46c603">+351/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>signup.tsx</strong><dd><code>Add React Native signup screen
with tabs</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-262b09b9dd7234cad96fe092d7131a23451c9e50b98c126c9e36599b3a127ac6">+387/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>signin.tsx</strong><dd><code>Add React Native signin screen
with MFA</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-3a4105c32f4aa4290e4fc0a12c2cc3121b4b0901c1429917087e26569c1c0a7a">+367/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>MFASettings.tsx</strong><dd><code>Add React SPA MFA settings
component</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-e43f9e6aec21b8620c86b9cdf9986e2e8dace52d87609b1c74cf6ab9a0639d21">+288/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>mfa-settings.tsx</strong><dd><code>Add Next.js SSR MFA
settings client</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-75767c82cc4c7eb779f3beb7ca5c5f9b2cc090347029ab44cc7f93b8b881c85b">+292/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>profile.tsx</strong><dd><code>Add React Native profile
screen</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-e996eb818728427e1c131ac6f3cba0bd506ad8f28486ee47c9ceb5b33dbc7869">+262/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>verify.tsx</strong><dd><code>Add React Native email verify
screen</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-93aeb3cfb31ac602e11e6031bb4860e81385c3c9693e5285c8f69988341cd045">+265/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>index.ts</strong><dd><code>Add Express server Nhost client
example</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-337bf1c26c434751c7c3e356598278e8e05482e41936a30c587cf68fd375092f">+126/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>email-confirm-change.tsx</strong><dd><code>Add email
confirm-change template generator</code>&nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-5ee9a92531c20cb74b9a49335eb0a65cdd73efa7d8c59020ea80163bba2cec72">+129/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>AppleSignIn.tsx</strong><dd><code>Add Expo Apple Sign In
integration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-8fcf005753999f7770661afbc82063eb0637a2f959f03a77a94d4eef55b1d9cf">+112/-0</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>render-emails.ts</strong><dd><code>Add email templates
render script</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-760314d4066ab3171a8dd9ec712e5e6b4e373f86531e66c502df06dab8f9dc7d">+82/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>auth.ts</strong><dd><code>Add Vue demo Nhost auth
store</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-db6008d5d08993870173045b4d2e978832e91943222dad7f6093537c48277e4f">+75/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
<td><strong>App.tsx</strong><dd><code>Set up React SPA routes and auth
provider</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-849f3aa52970f348de49a27094aac4e4b8cb8cf29580cada70d37f1a04249725">+77/-0</a>&nbsp;
&nbsp; </td>

</tr>

</table></details></td></tr><tr><td><strong>Tests</strong></td><td><details><summary>2
files</summary><table>
<tr>
<td><strong>DateTimePicker.test.tsx</strong><dd><code>Simplify
DateTimePicker test awaits</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-c7076012eb33d6f60049710638b5ad19c2f310b8c250c79f1905be7e0a30b00a">+12/-12</a>&nbsp;
</td>

</tr>

<tr>
<td><strong>useProjectLogs.test.ts</strong><dd><code>Update project logs
tests to use CoreLogService</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-13d900aa08d06962a09628136b893801ad62a96c3ff89d380c5c4b7ae92d891e">+9/-9</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>
</table></details></td></tr><tr><td><strong>Additional
files</strong></td><td><details><summary>101 files</summary><table>
<tr>
  <td><strong>examples_demos_checks.yaml</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-48c5a14a5d1da9f35b409ecc95fae8f3a319f97bffbf0020efcb8c360347dc02">+94/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>biome.json</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-2bc8a1f5e9380d5a187a4e90f11b4dd36c3abad6aea44c84be354a4f44cdec55">+43/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>LogsServiceFilter.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-a590a7298a9f040df9f26c4eb37d10fc36f47c32996f71aec47796f08c44e892">+8/-7</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>DeploymentServcieLogsHeader.test.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-46fb5d0c9528168323c0e16ef4186d91fe6274b64292f43841258bdfc45dd581">+81/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>DeploymentServiceLogs.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-333a9783713e9a4bad1b5327e117cbe69148091abe8b9038d36132b5f4635bbe">+3/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>DeploymentServiceLogsHeader.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-4f102c06ed32bb3d8245e415e76b0b14d2d4ae3abca6e234edf69278325c7a95">+3/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>useProjectLogs.ts</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-10efc67700b3f024dd03442eacd339802e951696d04caa76bd5a864bd5c7c83f">+3/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>LogsBody.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-b628e511a7fb9b237ac691b27ab9585eed0d0803144cde66c3af7fa6f9a2dc40">+2/-2</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>LogsHeader.test.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-6a348a6b3f868aac854020f2b85ff9a7cf5d61f362a5201e77681e4d5a576f20">+86/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>LogsHeader.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-ebb3285aa776c9c5ea8b72672c4aafd55994c6c694998bbf56ca9c56d1e77664">+3/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>services.ts</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-8fcdaed33322718091b613ae22c65cc3eb61972904b5af46866b160c9bbbe48c">+13/-13</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>logs.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-77489a68a7526d74f06d59019ad68c44728b7620637308d70fba38d6649b73fa">+3/-3</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>Makefile</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-80fe306cf2e39e0852a6033f331195be7942ed7fb54b2cac6bd0139cc34d1bb6">+17/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>APPLE_SIGN_IN_SETUP.md</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-eeb0deb7c8dae0e6b49462b92e5e7176ee11236759847ea59253375ba48d6f5f">+95/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>README.md</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-29693cbc361101a671771703a3097f97b0ad6c344435cb3fa6d19b00c030ecac">+169/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>README_MAGIC_LINKS.md</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-9232aee57e45778533da1f08d80d794f612f3672b4e2fc0a77c4ca6e03e27d3a">+410/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>README_NATIVE_AUTHENTICATION.md</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-2bfce492dbe0824f4e47907f7269b57b6f8f092550647afc46cff13903a34750">+381/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>README_PROTECTED_ROUTES.md</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-25f106def41f290d4c471f95f4a25db4c7d411407f7937035f5e3e53ff90aa6f">+357/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>README_SOCIAL_SIGNIN.md</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-10c036291fe61ed79d9ce5c1764f10e44b6cc57232b0b6b981c19f84760f5e8e">+530/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>app.json</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-12f6ad4bacc187ec247d9ee19b2ed6a5b49c4304508fd3ba4321b265e0eec36f">+53/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>_layout.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-9d1054a3d9a27191c4056537dea211e6a19610581557e756db81677e49e7b929">+54/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>MagicLinkForm.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-169e79f10d79f5ff7df3e910f042005f0b139246098ad4a1d2284ed450409b42">+158/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>NativeLoginForm.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-46f2e1cd61d0d53b7fde9a0056f78e07a317e8fe60b6b237baa0a13d6cd4a4e0">+93/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>ProtectedScreen.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-4c71e8ae5499fc1dda82b020fd0b1f2ce2f972d18298af69d6c3c553742d2ad0">+40/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>SocialLoginForm.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-2e46da6c133f57ab9748231ab7985a6704893b6d99c2981007a3cfeaeaee2b79">+91/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>index.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-0db4f13bb27522261f48250563b1f9d6e6cd5805c9b89caf2bce331b229a4cca">+120/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>AsyncStorage.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-7c877423128ee8dadc8bafb89ea8a98b6c1b05130949a6c6d9f3f325a4ccacc1">+93/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>AuthProvider.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-72e3c353a156c42747b082a51cf6ab9e59a8b5009f8cb637d9e75fb9c31a7212">+110/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>utils.ts</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-4091a110ae026c9f130fb654f47381b503f7a04a1cefc3020831ecb3f0d971ad">+44/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>mfa.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-477afc36e3e2e62727b11e6d1f77592c89d120d8dfd676a05756dcff7625825a">+233/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>package.json</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-9d05689c57c194cb000eb7ff276476869ba4ee40a730c23e47016671d7f17521">+57/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>tsconfig.json</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-1d10ade4fc1aa2dce579be648ec4420554d626d814d1971a6ec8d59018eb3bc8">+15/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>.secrets.example</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-c47199e16c186acba1e9f82c541362d1a6ff05066299b04f61f358d636c3f5d0">+16/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>Makefile</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-5c1f8270d9eede9a24fa01068ede09fa13a8fca85eeda73328701b5781500ebe">+7/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>README.md</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-9a3489ab94ccfa9504db04213d3dbb603c609abf1435e5844d911a03210c3515">+29/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>env-up.sh</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-de9763dc0910faec77518ee1eacfdf7a74a257b88e6055a421f37b9ca85d4280">+8/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>package.json</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-c602bad387df10731905ad3a49ee0a1ba256823a7e1a4b8ca262fc2f407fea4b">+13/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>tsconfig.json</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-7635e254c708cde29d62250387c92e13e0d2a7f4180b11ba11b288a7205ed00a">+11/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>config.yaml</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-4446c65612d85e309d41f928643d796ca2d3970f2ac122559a2c3ced6cfd5a77">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-ebd226650ec2ccfdfa181eb52ac57c538d1258efcdd5eefa6521d661014b0beb">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-f4eec235a2ce395d582333d50a2358e5b4b4758a0d6de2d80bb9b983bb5788c3">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-546c1e26f4f5a58d38dab39ffcd1f1e14ecc6867fd1bece4b6a1dd76a310fe3b">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-a62a136fad466ff5def2190737d76bdb54d3ca7c31d5ed85c0c7b9bdb4adc33a">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-c70655567313cbdee9e332a73651c1e91e33f57fe8a168cb064f4206aca66a20">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-6a415c40c7cfaa325f53043b71d709714101b9f629f954095bcc64862abc744b">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-9fd0c953500d416a457aa8dfcfb3659a517ee82a964b860b1b8afeb8d09c098f">+43/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-fd472f081db09177df1548bebdf3141328273c89d3e76b14e01e62bfdfbe2443">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-236393a4f47ed882febf9eb4dd24d5525bc3f20e5e474f5e34ecdff091555965">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-8f0998f6df2005ee1436d74ad2ce2cd1bf91eb053a304160130d24ac64ecfae1">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-401c18113470ff2d38ad5b98eb3fc4fbb3aa1226c2db81b425a75eec28072607">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-3005f541570bcb879d647fbee1161de8b18981e4b07b8eac07a2250835f605a9">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-ed0a3cf5dabcbecde2cb9e914d827ca6a92cd0818aff7b45e51e8b5bdeec27df">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-b7ec4c42b59e6f2e23f1ba196befd43d08632b13747b48c6ceaba3d6d1e38209">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-026c60a66006fe593b8c30cba71ef52d4968f22f004c68bcde8356312de00dce">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-9e40803f1b207ababf5640dec47083efa84dfbc201e968b37730e515081a3101">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-143e6ac6a6fff08a7d6a68f98cae196d569fe4f30fc0215633c5b653ce349181">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-613a3b66d30c6e4b93257765df35c2d0be6379b6f7ab6f3478b4731c1d21ee96">+43/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-6f9c1433fcaabe15051bad4b1d888203d928a30ab2da829c724d54f74ecc818f">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-1727f5ec1686c6079eb733d7f3c5cc60a6f047635241bbf4c63fc5985a78b13e">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-f65964bb501d3baeba4974daaab7c0e6edaec0645860b119633d09424deb995a">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-b911f5720caca8a68bc5ba072284c4d40e1e6b3283a2070c17e84a3d9ba55f79">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-7f1525fdd0e82fd57d181d56acb7828c5838ef635403c560b1dfb88834cc5996">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-afb123c7657dcf08247b52027d73e4d9dcee51d0cb7e37a1de0f31200c4dc535">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-877b5d458de6de8bb1287075e3eb6a6f8aaba807ddd2db78705640512ce038a6">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-01135b2530cf6bf46ae7398f1ed4e1a684cbfff1af5fc7dc28eacbd3b1c66caf">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-efa400ab45cf1d64d014c44c6308be2146eb1657c02f8ac7e47c9f71fb34bd4f">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-a7a3eec9c3ce1f9d763a196f2a6af166c07c3c87f05967261847cb40219814fe">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-344d73792431a98012dcfe76f75525a383827ef6f81ab1688b46ba504c34a903">+43/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-d01fea24e87babd4f89d3ce27578f96a35578511bebad2c0c2f2689160c0fc63">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-dd12ae6e043b5085514f43691a710f3e65a0c3df93c8847ffd9678c8fae36c08">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-fa998d4e425d971dd546898cac4d1183dd4c10d4d95f19d4f4650d322a58505c">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-1c7f1643eece2cc9fdad3cc032d60826d541d6ad9123c9674ea1de20f4ef429f">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-bd2c5decf64f436932af01d32dc08a35fb745c8aacf97952be9f8925fb94eed4">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-f4187b87464106220110abbc03675a30e6e5154248be0880f75e7b976419e3ef">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-9d893b6b685830b2cdce2b05a925482b93c5407a7d7e2f2f76d159e55a089d6a">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-05b560e90f649549a7d98396179732b15c4af26b8df6fa5dc3b594b49eb548d0">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-2bef9e62baac1dabaf929cbb9113ad6c5711eb8ba364defebefd9cb9e65703e8">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-d0ca2cbd1895c7841755f2f8bb109ba17f6018a53f6615ab45445660c947742d">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-2df8e46ef47cbebf23bfe3bba1e963ebc93d1bde4c09d6bc18cafd008810fe11">+43/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-21ce711d17cbc8006a363b11e33f0b9009b3c7c7ba6885e9b6fccb64755ac0cd">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-692abf5d4051aa5afc5342cdd36c24879777744678ea1cf6a1a246f2d4cb60ea">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-1443b315ed3da71a04c3d532a46dd6fc837cbf67a9813d6d9b7dcaad4fe8f617">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-b034b26b4c5e31f76049c742cf80148731a686744a3dd0e440399521ad2491d9">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-52643cd4c649348b055c9659cda85a7570f974c47aaa7af473814cea851fb9a7">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-c1538ae90cc62160dbcb4be240cef50eaf241b88ce4cb391e5851e8b346d73bf">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-5e695a8cb0adf5ff5a530a922bc8f5b52d00972a99951f2bd690e4f44ccefae8">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-532dbd0e9c1f0d9d76715a27d174745824cfc68d0f7e499afa7e7e2c150b8d19">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-07b82a5305d283da9c6781f44a88c0ff4131b575fe5ee78a15d69abf6bd91b87">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-9b9ee0fb48f9f8815980bfe94d7d93d41bb9f1511cad6852a6d4f7e9192faf47">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-d3755355865e28005c894a10824920696b06e64e7c1c8765268908d56aa12261">+43/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-d5c3314a4bfa1667da59c2072178f80a4654ac0f10f714b378496a69b6e21dcd">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-a0fda535693ff92e05311d4a63884ae0063e90eadeba20e958a7f9da51016ec5">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-de84bf86da7ce4481559b7037412128a4928746589b35575b9dd811c9f50b391">+52/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>subject.txt</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-d17f4b74ce523c6ed011f1d7a3426dcbe430716a50803ee2afdb0f81e5d047fe">+1/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>README.md</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-3266bd27efc68f506fa6c7d9ab41567c63a9fabf3d2d247f5719be551efbf250">+23/-0</a>&nbsp;
&nbsp; </td>

</tr>

<tr>
  <td><strong>email-verify.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-80cd5ff8c6c8b4f7a588058618ed27b3316304b1786a92ea8ffbb4c3fda96ac5">+127/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>password-reset.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-cef64f7d4eab756b7c68477e4fcc323cf8197832c92fd07e65f24dc71c9e3e80">+127/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>signin-otp.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-a2a5f9edf38127ed8168a60248e51e6ba1b4026fe7e8366111f28e0502749e6f">+125/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>signin-passwordless.tsx</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-38ef73aa255432a72a30ab3e63da415b26598ce7b267a1e39957c26aefac1d88">+127/-0</a>&nbsp;
</td>

</tr>

<tr>
  <td><strong>body.html</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-1c287ea5a500ae373cd8c021d07dc0f3994db71077e892d7b5761baf5c2ef037">+8/-0</a>&nbsp;
&nbsp; &nbsp; </td>

</tr>

<tr>
  <td><strong>Additional files not shown</strong></td>
<td><a
href="https://github.com/nhost/nhost/pull/3459/files#diff-2f328e4cd8dbe3ad193e49d92bcf045f47a6b72b1e9487d366f6b8288589b4ca"></a></td>

</tr>
</table></details></td></tr></tr></tbody></table>

</details>

___
2025-09-24 14:50:55 +02:00

11 KiB

Native Authentication - Apple Sign-In

This document explains how native authentication with Apple Sign-In is implemented in the Nhost React Native demo, including deep linking, nonce generation, ID tokens, and security considerations.

Overview

Apple Sign-In provides a secure, privacy-focused authentication method for iOS users. The implementation uses cryptographic nonces, identity tokens, and deep linking to ensure a secure authentication flow between the app, Apple's servers, and Nhost.

Architecture

Authentication Flow

  1. Nonce Generation: Create a cryptographic nonce for request verification
  2. Apple Authentication: Request user authentication from Apple
  3. Identity Token: Receive signed JWT from Apple containing user information
  4. Nhost Verification: Send identity token and nonce to Nhost for verification
  5. Session Creation: Nhost validates the token and creates a user session

Implementation Details

Apple Sign-In Component

// app/components/AppleSignIn.tsx
const AppleSignIn: React.FC<AppleSignInProps> = ({ setIsLoading }) => {
  const { nhost } = useAuth();
  const [appleAuthAvailable, setAppleAuthAvailable] = useState(false);

  // Check Apple authentication availability
  useEffect(() => {
    const checkAvailability = async () => {
      if (Platform.OS === "ios") {
        const isAvailable = await AppleAuthentication.isAvailableAsync();
        setAppleAuthAvailable(isAvailable);
      }
    };
    checkAvailability();
  }, []);

  const handleAppleSignIn = async () => {
    try {
      // Generate cryptographic nonce
      const nonce = Math.random().toString(36).substring(2, 15);
      const hashedNonce = await Crypto.digestStringAsync(
        Crypto.CryptoDigestAlgorithm.SHA256,
        nonce,
      );

      // Request Apple authentication
      const credential = await AppleAuthentication.signInAsync({
        requestedScopes: [
          AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
          AppleAuthentication.AppleAuthenticationScope.EMAIL,
        ],
        nonce: hashedNonce,
      });

      // Authenticate with Nhost
      if (credential.identityToken) {
        const response = await nhost.auth.signInIdToken({
          provider: "apple",
          idToken: credential.identityToken,
          nonce, // Original unhashed nonce
        });

        if (response.body?.session) {
          router.replace("/profile");
        }
      }
    } catch (error) {
      // Handle authentication errors
    }
  };
};

Security Mechanisms

Cryptographic Nonce

The nonce prevents replay attacks and ensures request authenticity:

// Generate random nonce
const nonce = Math.random().toString(36).substring(2, 15);

// Hash nonce for Apple (SHA256)
const hashedNonce = await Crypto.digestStringAsync(
  Crypto.CryptoDigestAlgorithm.SHA256,
  nonce,
);

// Send hashed nonce to Apple
const credential = await AppleAuthentication.signInAsync({
  nonce: hashedNonce,
  // ...
});

// Send original nonce to Nhost for verification
await nhost.auth.signInIdToken({
  provider: "apple",
  idToken: credential.identityToken,
  nonce, // Original unhashed nonce
});

Why Nonce is Important

  1. Replay Attack Prevention: Ensures each authentication request is unique
  2. Request Binding: Links the Apple response to the specific app request
  3. Tampering Detection: Detects if the response has been modified
  4. Time-bound Security: Nonces typically have short lifespans

Identity Token Structure

Apple returns a JWT (JSON Web Token) containing:

{
  "iss": "https://appleid.apple.com",
  "aud": "com.nhost.reactnativewebdemo",
  "exp": 1634567890,
  "iat": 1634564290,
  "sub": "000123.abc456def789...",
  "nonce": "hashed_nonce_value",
  "email": "user@example.com",
  "email_verified": "true",
  "real_user_indicator": "true"
}

Platform Requirements

iOS Configuration

The app must be properly configured for Apple Sign-In:

// app.json
{
  "expo": {
    "ios": {
      "bundleIdentifier": "com.nhost.reactnativewebdemo",
      "infoPlist": {
        "NSFaceIDUsageDescription": "This app uses Face ID for signing in"
      }
    },
    "plugins": ["expo-apple-authentication"]
  }
}

Availability Check

Apple Sign-In is only available on iOS 13+ devices:

const checkAvailability = async () => {
  if (Platform.OS === "ios") {
    const isAvailable = await AppleAuthentication.isAvailableAsync();
    setAppleAuthAvailable(isAvailable);
  }
};

Nhost Configuration

Apple Provider Setup

Configure Apple as an authentication provider in your Nhost dashboard:

  1. Team ID: Your Apple Developer Team ID
  2. Service ID: Apple Services ID for your app
  3. Key ID: Apple Sign-In key identifier
  4. Private Key: Apple Sign-In private key (P8 file content)

Server-Side Verification

Nhost performs server-side verification of the identity token:

  1. Signature Verification: Validates JWT signature using Apple's public keys
  2. Nonce Verification: Compares hashed nonce in token with provided nonce
  3. Audience Verification: Ensures token is intended for your app
  4. Expiration Check: Validates token hasn't expired
  5. Issuer Validation: Confirms token comes from Apple

Deep Linking Integration

URL Scheme Configuration

The app is configured with custom URL schemes for deep linking:

// app.json
{
  "expo": {
    "scheme": "reactnativewebdemo",
    "ios": {
      "infoPlist": {
        "CFBundleURLTypes": [
          {
            "CFBundleURLSchemes": ["reactnativewebdemo"]
          }
        ]
      }
    }
  }
}

While Apple Sign-In typically doesn't require custom deep linking (it's handled within the app), the configuration supports it for other authentication flows:

// app/verify.tsx - Used by other auth methods
useEffect(() => {
  const subscription = Linking.addEventListener("url", handleDeepLink);
  return () => subscription?.remove();
}, []);

const handleDeepLink = (event: { url: string }) => {
  // Handle incoming deep links from authentication providers
};

Error Handling

Common Apple Sign-In Errors

const handleAppleSignIn = async () => {
  try {
    // ... authentication logic
  } catch (error: any) {
    if (error.code === "ERR_CANCELED") {
      // User canceled authentication
      return;
    }

    if (error.code === "ERR_INVALID_RESPONSE") {
      Alert.alert("Error", "Invalid response from Apple");
      return;
    }

    if (error.code === "ERR_NOT_AVAILABLE") {
      Alert.alert("Error", "Apple Sign-In not available on this device");
      return;
    }

    // Generic error handling
    Alert.alert("Authentication Error", error.message || "Unknown error");
  }
};

Nhost Integration Errors

const response = await nhost.auth.signInIdToken({
  provider: "apple",
  idToken: credential.identityToken,
  nonce,
});

if (response.error) {
  switch (response.error.message) {
    case "Invalid identity token":
      Alert.alert("Error", "Authentication failed. Please try again.");
      break;
    case "Invalid nonce":
      Alert.alert("Error", "Security verification failed");
      break;
    default:
      Alert.alert("Error", "Authentication error occurred");
  }
}

Privacy Features

Apple's Privacy Protection

Apple Sign-In provides enhanced privacy features:

  1. Email Relay: Apple can provide relay emails to protect user's real email
  2. Minimal Data: Only requests necessary user information
  3. User Control: Users can choose what information to share
  4. Private Email: Option to hide real email address

Handling Private Emails

// Handle Apple's private relay emails
const credential = await AppleAuthentication.signInAsync({
  requestedScopes: [
    AppleAuthentication.AppleAuthenticationScope.EMAIL,
    AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
  ],
  nonce: hashedNonce,
});

// Email might be a private relay address
console.log("Email:", credential.email); // Could be privaterelay@example.com

Testing

Development Testing

  1. iOS Simulator: Apple Sign-In works in iOS Simulator (iOS 14+)
  2. Physical Device: Test on real iOS devices for complete functionality
  3. Xcode Console: Monitor authentication flow through Xcode logs

Test Scenarios

// Test different authentication states
const testScenarios = [
  "First-time sign in with Apple ID",
  "Returning user authentication",
  "User cancels authentication",
  "Network connection issues",
  "Invalid Apple ID credentials",
  "Apple ID with 2FA enabled",
];

Security Best Practices

Implementation Guidelines

  1. Always Use Nonce: Never skip nonce generation for production apps
  2. Validate Server-Side: Let Nhost handle token validation
  3. Handle Errors Gracefully: Provide clear feedback to users
  4. Secure Storage: Let Nhost handle session storage securely
  5. Regular Updates: Keep Apple authentication libraries updated

Production Considerations

  1. Apple Developer Account: Requires paid Apple Developer membership
  2. App Store Review: Apple Sign-In must be implemented if other social logins exist
  3. Bundle ID Matching: Ensure bundle ID matches Apple configuration
  4. Certificate Management: Keep Apple certificates and keys updated

Troubleshooting

Common Issues

Issue Cause Solution
"Not Available" Error iOS version < 13 or not configured Check device compatibility and configuration
Invalid Identity Token Incorrect Nhost Apple configuration Verify Apple provider settings in Nhost dashboard
Nonce Mismatch Sending hashed nonce to Nhost Send original unhashed nonce to Nhost
Bundle ID Mismatch App bundle ID doesn't match Apple config Ensure bundle IDs match in all configurations

Debug Tools

// Enable debug logging
if (__DEV__) {
  console.log("Apple Auth Available:", appleAuthAvailable);
  console.log("Generated Nonce:", nonce);
  console.log("Hashed Nonce:", hashedNonce);
  console.log("Identity Token:", credential.identityToken);
}

External Resources