Compare commits
41 Commits
@nhost/rea
...
@nhost/rea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79f268ea08 | ||
|
|
d5f56f7e18 | ||
|
|
6ddaf47a4b | ||
|
|
11200934a8 | ||
|
|
653f6b5766 | ||
|
|
653c886ec0 | ||
|
|
ecba8f78ac | ||
|
|
d7b5261f5a | ||
|
|
164e8cac28 | ||
|
|
ad0d56416c | ||
|
|
c47ba63dd9 | ||
|
|
47fb75302f | ||
|
|
2c95ed2d14 | ||
|
|
07eb26ca29 | ||
|
|
90843a9feb | ||
|
|
5fc3b643a7 | ||
|
|
eb93a20bc0 | ||
|
|
ac2cd3f34e | ||
|
|
26cb0de087 | ||
|
|
6d93aa4235 | ||
|
|
03d7083d3e | ||
|
|
618e5a8db2 | ||
|
|
3a217fd490 | ||
|
|
9eb78e06db | ||
|
|
3d7369a19e | ||
|
|
8e03774bc1 | ||
|
|
13937fa026 | ||
|
|
9e57a2bfe6 | ||
|
|
bf4cc38f8d | ||
|
|
4e7d1fbe90 | ||
|
|
2f432b5c5f | ||
|
|
4629b952c6 | ||
|
|
f6cfca9288 | ||
|
|
b9c012e263 | ||
|
|
0e112674a4 | ||
|
|
0feac15d10 | ||
|
|
f42b693aa2 | ||
|
|
9687581dc7 | ||
|
|
9bf938a507 | ||
|
|
06bc8856dd | ||
|
|
1c3c7c225f |
17
README.md
17
README.md
@@ -383,14 +383,21 @@ Here are some ways of contributing to making Nhost better:
|
||||
<sub><b>Hoang Do</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/eltociear">
|
||||
<img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="100;" alt="eltociear"/>
|
||||
<br />
|
||||
<sub><b>Ikko Ashimine</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/jladuval">
|
||||
<img src="https://avatars.githubusercontent.com/u/1935359?v=4" width="100;" alt="jladuval"/>
|
||||
<br />
|
||||
<sub><b>Jacob Duval</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/kylehayes">
|
||||
<img src="https://avatars.githubusercontent.com/u/509932?v=4" width="100;" alt="kylehayes"/>
|
||||
@@ -425,15 +432,15 @@ Here are some ways of contributing to making Nhost better:
|
||||
<br />
|
||||
<sub><b>Nacho Aldama</b></sub>
|
||||
</a>
|
||||
</td>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/ghoshnirmalya">
|
||||
<img src="https://avatars.githubusercontent.com/u/6391763?v=4" width="100;" alt="ghoshnirmalya"/>
|
||||
<br />
|
||||
<sub><b>Nirmalya Ghosh</b></sub>
|
||||
</a>
|
||||
</td></tr>
|
||||
<tr>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://github.com/quentin-decre">
|
||||
<img src="https://avatars.githubusercontent.com/u/1137511?v=4" width="100;" alt="quentin-decre"/>
|
||||
|
||||
@@ -3,7 +3,7 @@ title: 'init'
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
Intialize a local Nhost project in the current working directory.
|
||||
Initialize a local Nhost project in the current working directory.
|
||||
|
||||
```
|
||||
nhost init
|
||||
|
||||
@@ -4,7 +4,7 @@ title: signUp()
|
||||
sidebar_label: signUp()
|
||||
slug: /reference/javascript/auth/sign-up
|
||||
description: Use `nhost.auth.signUp` to sign up a user using email and password. If you want to sign up a user using passwordless email (Magic Link), SMS, or an OAuth provider, use the `signIn` function instead.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L102
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L106
|
||||
---
|
||||
|
||||
# `signUp()`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: signIn()
|
||||
sidebar_label: signIn()
|
||||
slug: /reference/javascript/auth/sign-in
|
||||
description: Use `nhost.auth.signIn` to sign in a user using email and password, passwordless (email or sms) or an external provider. `signIn` can be used to sign in a user in various ways depending on the parameters.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L145
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L149
|
||||
---
|
||||
|
||||
# `signIn()`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: signOut()
|
||||
sidebar_label: signOut()
|
||||
slug: /reference/javascript/auth/sign-out
|
||||
description: Use `nhost.auth.signOut` to sign out the user.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L233
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L245
|
||||
---
|
||||
|
||||
# `signOut()`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: resetPassword()
|
||||
sidebar_label: resetPassword()
|
||||
slug: /reference/javascript/auth/reset-password
|
||||
description: Use `nhost.auth.resetPassword` to reset the password for a user. This will send a reset-password link in an email to the user. When the user clicks the reset-password link the user is automatically signed-in. Once signed-in, the user can change their password using `nhost.auth.changePassword()`.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L249
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L261
|
||||
---
|
||||
|
||||
# `resetPassword()`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: changePassword()
|
||||
sidebar_label: changePassword()
|
||||
slug: /reference/javascript/auth/change-password
|
||||
description: Use `nhost.auth.changePassword` to change the password for the signed-in user. The old password is not needed. In case the user is not signed-in, a password reset ticket needs to be provided.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L265
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L277
|
||||
---
|
||||
|
||||
# `changePassword()`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: sendVerificationEmail()
|
||||
sidebar_label: sendVerificationEmail()
|
||||
slug: /reference/javascript/auth/send-verification-email
|
||||
description: Use `nhost.auth.sendVerificationEmail` to send a verification email to the specified email. The email contains a verification-email link. When the user clicks the verification-email link their email is verified.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L284
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L296
|
||||
---
|
||||
|
||||
# `sendVerificationEmail()`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: changeEmail()
|
||||
sidebar_label: changeEmail()
|
||||
slug: /reference/javascript/auth/change-email
|
||||
description: Use `nhost.auth.changeEmail` to change a user's email. This will send a confirm-email-change link in an email to the new email. Once the user clicks on the confirm-email-change link the email will be change to the new email.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L303
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L315
|
||||
---
|
||||
|
||||
# `changeEmail()`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: deanonymize()
|
||||
sidebar_label: deanonymize()
|
||||
slug: /reference/javascript/auth/deanonymize
|
||||
description: Use `nhost.auth.deanonymize` to deanonymize a user.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L319
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L331
|
||||
---
|
||||
|
||||
# `deanonymize()`
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: addSecurityKey()
|
||||
sidebar_label: addSecurityKey()
|
||||
slug: /reference/javascript/auth/add-security-key
|
||||
description: Use `nhost.auth.addSecurityKey to add a security key to the user, using the WebAuthn API.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L369
|
||||
---
|
||||
|
||||
# `addSecurityKey()`
|
||||
|
||||
Use `nhost.auth.addSecurityKey to add a security key to the user, using the WebAuthn API.
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">nickname</span>** <span className="optional-status">optional</span> `string`
|
||||
|
||||
optional human-readable nickname for the security key
|
||||
|
||||
---
|
||||
@@ -4,7 +4,7 @@ title: onTokenChanged()
|
||||
sidebar_label: onTokenChanged()
|
||||
slug: /reference/javascript/auth/on-token-changed
|
||||
description: Use `nhost.auth.onTokenChanged` to add a custom function that runs every time the access or refresh token is changed.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L362
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L387
|
||||
---
|
||||
|
||||
# `onTokenChanged()`
|
||||
@@ -4,7 +4,7 @@ title: onAuthStateChanged()
|
||||
sidebar_label: onAuthStateChanged()
|
||||
slug: /reference/javascript/auth/on-auth-state-changed
|
||||
description: Use `nhost.auth.onAuthStateChanged` to add a custom function that runs every time the authentication status of the user changes. E.g. add a custom function that runs every time the authentication status changes from signed-in to signed-out.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L397
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L422
|
||||
---
|
||||
|
||||
# `onAuthStateChanged()`
|
||||
@@ -4,7 +4,7 @@ title: isAuthenticated()
|
||||
sidebar_label: isAuthenticated()
|
||||
slug: /reference/javascript/auth/is-authenticated
|
||||
description: Use `nhost.auth.isAuthenticated` to check if the user is authenticated or not.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L439
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L464
|
||||
---
|
||||
|
||||
# `isAuthenticated()`
|
||||
@@ -4,7 +4,7 @@ title: isAuthenticatedAsync()
|
||||
sidebar_label: isAuthenticatedAsync()
|
||||
slug: /reference/javascript/auth/is-authenticated-async
|
||||
description: Use `nhost.auth.isAuthenticatedAsync` to wait (await) for any internal authentication network requests to finish and then return the authentication status.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L457
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L482
|
||||
---
|
||||
|
||||
# `isAuthenticatedAsync()`
|
||||
@@ -4,7 +4,7 @@ title: getAuthenticationStatus()
|
||||
sidebar_label: getAuthenticationStatus()
|
||||
slug: /reference/javascript/auth/get-authentication-status
|
||||
description: Use `nhost.auth.getAuthenticationStatus` to get the authentication status of the user.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L483
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L508
|
||||
---
|
||||
|
||||
# `getAuthenticationStatus()`
|
||||
@@ -4,7 +4,7 @@ title: getAccessToken()
|
||||
sidebar_label: getAccessToken()
|
||||
slug: /reference/javascript/auth/get-access-token
|
||||
description: Use `nhost.auth.getAccessToken` to get the access token of the user.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L513
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L538
|
||||
---
|
||||
|
||||
# `getAccessToken()`
|
||||
@@ -4,7 +4,7 @@ title: getDecodedAccessToken()
|
||||
sidebar_label: getDecodedAccessToken()
|
||||
slug: /reference/javascript/auth/get-decoded-access-token
|
||||
description: Use `nhost.auth.getDecodedAccessToken` to get the decoded access token of the user.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L528
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L553
|
||||
---
|
||||
|
||||
# `getDecodedAccessToken()`
|
||||
@@ -4,7 +4,7 @@ title: getHasuraClaims()
|
||||
sidebar_label: getHasuraClaims()
|
||||
slug: /reference/javascript/auth/get-hasura-claims
|
||||
description: Use `nhost.auth.getHasuraClaims` to get the Hasura claims of the user.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L545
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L570
|
||||
---
|
||||
|
||||
# `getHasuraClaims()`
|
||||
@@ -4,7 +4,7 @@ title: getHasuraClaim()
|
||||
sidebar_label: getHasuraClaim()
|
||||
slug: /reference/javascript/auth/get-hasura-claim
|
||||
description: Use `nhost.auth.getHasuraClaim` to get the value of a specific Hasura claim of the user.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L563
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L588
|
||||
---
|
||||
|
||||
# `getHasuraClaim()`
|
||||
@@ -4,7 +4,7 @@ title: refreshSession()
|
||||
sidebar_label: refreshSession()
|
||||
slug: /reference/javascript/auth/refresh-session
|
||||
description: Use `nhost.auth.refreshSession` to refresh the session with either the current internal refresh token or an external refresh token.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L586
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L611
|
||||
---
|
||||
|
||||
# `refreshSession()`
|
||||
@@ -4,7 +4,7 @@ title: getSession()
|
||||
sidebar_label: getSession()
|
||||
slug: /reference/javascript/auth/get-session
|
||||
description: Use `nhost.auth.getSession()` to get the session of the user.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L627
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L652
|
||||
---
|
||||
|
||||
# `getSession()`
|
||||
@@ -4,7 +4,7 @@ title: getUser()
|
||||
sidebar_label: getUser()
|
||||
slug: /reference/javascript/auth/get-user
|
||||
description: Use `nhost.auth.getUser()` to get the signed-in user.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L642
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L667
|
||||
---
|
||||
|
||||
# `getUser()`
|
||||
@@ -4,7 +4,7 @@ title: HasuraAuthClient
|
||||
sidebar_label: Auth
|
||||
description: No description provided.
|
||||
slug: /reference/javascript/auth
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L59
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/hasura-auth-client.ts#L63
|
||||
---
|
||||
|
||||
# `HasuraAuthClient`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: AuthChangeEvent
|
||||
sidebar_label: AuthChangeEvent
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L100
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L107
|
||||
---
|
||||
|
||||
# `AuthChangeEvent`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: AuthChangedFunction
|
||||
sidebar_label: AuthChangedFunction
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L102
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L109
|
||||
---
|
||||
|
||||
# `AuthChangedFunction`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: ChangeEmailParams
|
||||
sidebar_label: ChangeEmailParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L82
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L89
|
||||
---
|
||||
|
||||
# `ChangeEmailParams`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: ChangePasswordParams
|
||||
sidebar_label: ChangePasswordParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L72
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L79
|
||||
---
|
||||
|
||||
# `ChangePasswordParams`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: DeanonymizeParams
|
||||
sidebar_label: DeanonymizeParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L87
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L94
|
||||
---
|
||||
|
||||
# `DeanonymizeParams`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: NhostAuthConstructorParams
|
||||
sidebar_label: NhostAuthConstructorParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L15
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L16
|
||||
---
|
||||
|
||||
# `NhostAuthConstructorParams`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: OnTokenChangedFunction
|
||||
sidebar_label: OnTokenChangedFunction
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L104
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L111
|
||||
---
|
||||
|
||||
# `OnTokenChangedFunction`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: Provider
|
||||
sidebar_label: Provider
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/core/src/types.ts#L141
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/core/src/types.ts#L148
|
||||
---
|
||||
|
||||
# `Provider`
|
||||
@@ -24,4 +24,5 @@ type Provider =
|
||||
| 'bitbucket'
|
||||
| 'discord'
|
||||
| 'twitch'
|
||||
| 'workos'
|
||||
```
|
||||
|
||||
@@ -4,7 +4,7 @@ title: ResetPasswordParams
|
||||
sidebar_label: ResetPasswordParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L67
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L74
|
||||
---
|
||||
|
||||
# `ResetPasswordParams`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: SendVerificationEmailParams
|
||||
sidebar_label: SendVerificationEmailParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L77
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L84
|
||||
---
|
||||
|
||||
# `SendVerificationEmailParams`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: SignInEmailPasswordOtpParams
|
||||
sidebar_label: SignInEmailPasswordOtpParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L35
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L36
|
||||
---
|
||||
|
||||
# `SignInEmailPasswordOtpParams`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: SignInEmailPasswordParams
|
||||
sidebar_label: SignInEmailPasswordParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L30
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L31
|
||||
---
|
||||
|
||||
# `SignInEmailPasswordParams`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: SignInParams
|
||||
sidebar_label: SignInParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L59
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L65
|
||||
---
|
||||
|
||||
# `SignInParams`
|
||||
@@ -14,6 +14,7 @@ type SignInParams =
|
||||
| SignInEmailPasswordParams
|
||||
| SignInEmailPasswordOtpParams
|
||||
| SignInPasswordlessEmailParams
|
||||
| SignInPasswordlessSecurityKeyParams
|
||||
| SignInPasswordlessSmsOtpParams
|
||||
| SignInPasswordlessSmsParams
|
||||
| SignInWithProviderOptions
|
||||
|
||||
@@ -4,7 +4,7 @@ title: SignInPasswordlessEmailParams
|
||||
sidebar_label: SignInPasswordlessEmailParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L40
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L41
|
||||
---
|
||||
|
||||
# `SignInPasswordlessEmailParams`
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: SignInPasswordlessSecurityKeyParams
|
||||
sidebar_label: SignInPasswordlessSecurityKeyParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L46
|
||||
---
|
||||
|
||||
# `SignInPasswordlessSecurityKeyParams`
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">email</span>** <span className="optional-status">required</span> `string`
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">securityKey</span>** <span className="optional-status">required</span> `"true"`
|
||||
|
||||
---
|
||||
@@ -4,7 +4,7 @@ title: SignInPasswordlessSmsOtpParams
|
||||
sidebar_label: SignInPasswordlessSmsOtpParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L50
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L56
|
||||
---
|
||||
|
||||
# `SignInPasswordlessSmsOtpParams`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: SignInPasswordlessSmsParams
|
||||
sidebar_label: SignInPasswordlessSmsParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L45
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L51
|
||||
---
|
||||
|
||||
# `SignInPasswordlessSmsParams`
|
||||
|
||||
@@ -4,19 +4,13 @@ title: SignInWithProviderOptions
|
||||
sidebar_label: SignInWithProviderOptions
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L54
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L61
|
||||
---
|
||||
|
||||
# `SignInWithProviderOptions`
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">provider</span>** <span className="optional-status">required</span> [`Provider`](/reference/docgen/javascript/auth/types/provider)
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">options</span>** <span className="optional-status">optional</span> `ProviderOptions`
|
||||
|
||||
---
|
||||
```ts
|
||||
type SignInWithProviderOptions =
|
||||
| { provider: Exclude<Provider, 'workos'>; options: CommonProviderOptions }
|
||||
| { provider: 'workos'; options: WorkOsOptions }
|
||||
```
|
||||
|
||||
@@ -4,7 +4,7 @@ title: SignUpEmailPasswordParams
|
||||
sidebar_label: SignUpEmailPasswordParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L23
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L24
|
||||
---
|
||||
|
||||
# `SignUpEmailPasswordParams`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: SignUpParams
|
||||
sidebar_label: SignUpParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L29
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L30
|
||||
---
|
||||
|
||||
# `SignUpParams`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: User
|
||||
sidebar_label: User
|
||||
description: User information
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/core/src/types.ts#L94
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/core/src/types.ts#L101
|
||||
---
|
||||
|
||||
# `User`
|
||||
|
||||
@@ -4,7 +4,7 @@ title: NhostAuthConstructorParams
|
||||
sidebar_label: NhostAuthConstructorParams
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L15
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/hasura-auth-js/src/utils/types.ts#L16
|
||||
---
|
||||
|
||||
# `NhostAuthConstructorParams`
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: useAddSecurityKey()
|
||||
sidebar_label: useAddSecurityKey()
|
||||
slug: /reference/nextjs/use-add-security-key
|
||||
description: Use the hook `useAddSecurityKey` to add a WebAuthn security key.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useAddSecurityKey.ts#L45
|
||||
---
|
||||
|
||||
# `useAddSecurityKey()`
|
||||
|
||||
Use the hook `useAddSecurityKey` to add a WebAuthn security key.
|
||||
|
||||
```tsx
|
||||
const { add, isLoading, isSuccess, isError, error } = useAddSecurityKey()
|
||||
|
||||
const handleFormSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
await add('key nickname')
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: useSignInSecurityKeyEmail()
|
||||
sidebar_label: useSignInSecurityKeyEmail()
|
||||
slug: /reference/nextjs/use-sign-in-security-key-email
|
||||
description: Use the hook `useSignInSecurityKeyEmail` to sign in a user using their email and a security key using the WebAuthn API.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useSignInSecurityKeyEmail.ts#L41
|
||||
---
|
||||
|
||||
# `useSignInSecurityKeyEmail()`
|
||||
|
||||
Use the hook `useSignInSecurityKeyEmail` to sign in a user using their email and a security key using the WebAuthn API.
|
||||
|
||||
```tsx
|
||||
const {
|
||||
signInSecurityKeyEmail,
|
||||
needsEmailVerification,
|
||||
isLoading,
|
||||
isSuccess,
|
||||
isError,
|
||||
error
|
||||
} = useSignInSecurityKeyEmail()
|
||||
|
||||
console.log({ needsEmailVerification, isLoading, isSuccess, isError, error })
|
||||
|
||||
const handleFormSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
await signInSecurityKeyEmail('joe@example.com')
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: AddSecuritKeyHookResult
|
||||
sidebar_label: AddSecuritKeyHookResult
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useAddSecurityKey.ts#L20
|
||||
---
|
||||
|
||||
# `AddSecuritKeyHookResult`
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isError</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an error occurred
|
||||
|
||||
**`@depreacted`**
|
||||
|
||||
use `!isSuccess` or `!!error` instead
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">error</span>** <span className="optional-status">required</span> `null` | `ErrorPayload`
|
||||
|
||||
Provides details about the error
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isSuccess</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
Returns `true` if the action is successful.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">add</span>** <span className="optional-status">required</span> `AddSecurityKeyHandler`
|
||||
|
||||
Add a security key to the current user with the WebAuthn API
|
||||
|
||||
---
|
||||
@@ -4,7 +4,7 @@ title: NhostSession
|
||||
sidebar_label: NhostSession
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/core/src/types.ts#L129
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/core/src/types.ts#L136
|
||||
---
|
||||
|
||||
# `NhostSession`
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: SignInSecurityKeyPasswordlessHookResult
|
||||
sidebar_label: SignInSecurityKeyPasswordlessHookResult
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useSignInSecurityKeyEmail.ts#L14
|
||||
---
|
||||
|
||||
# `SignInSecurityKeyPasswordlessHookResult`
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isError</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an error occurred
|
||||
|
||||
**`@depreacted`**
|
||||
|
||||
use `!isSuccess` or `!!error` instead
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">error</span>** <span className="optional-status">required</span> `null` | `ErrorPayload`
|
||||
|
||||
Provides details about the error
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isLoading</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` when the action is executing, `false` when it finished its execution.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isSuccess</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
Returns `true` if the action is successful.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">user</span>** <span className="optional-status">required</span> `null` | `User`
|
||||
|
||||
User information
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">accessToken</span>** <span className="optional-status">required</span> `null` | `string`
|
||||
|
||||
Access token (JWT)
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">needsEmailVerification</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an email is required to complete the action, and that a verification email has been sent to complete the action.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">signInSecurityKeyEmail</span>** <span className="optional-status">required</span> `SignInSecurityKeyPasswordlessHandler`
|
||||
|
||||
---
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: useAddSecurityKey()
|
||||
sidebar_label: useAddSecurityKey()
|
||||
slug: /reference/react/use-add-security-key
|
||||
description: Use the hook `useAddSecurityKey` to add a WebAuthn security key.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useAddSecurityKey.ts#L45
|
||||
---
|
||||
|
||||
# `useAddSecurityKey()`
|
||||
|
||||
Use the hook `useAddSecurityKey` to add a WebAuthn security key.
|
||||
|
||||
```tsx
|
||||
const { add, isLoading, isSuccess, isError, error } = useAddSecurityKey()
|
||||
|
||||
const handleFormSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
await add('key nickname')
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: useSignInSecurityKeyEmail()
|
||||
sidebar_label: useSignInSecurityKeyEmail()
|
||||
slug: /reference/react/use-sign-in-security-key-email
|
||||
description: Use the hook `useSignInSecurityKeyEmail` to sign in a user using their email and a security key using the WebAuthn API.
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useSignInSecurityKeyEmail.ts#L41
|
||||
---
|
||||
|
||||
# `useSignInSecurityKeyEmail()`
|
||||
|
||||
Use the hook `useSignInSecurityKeyEmail` to sign in a user using their email and a security key using the WebAuthn API.
|
||||
|
||||
```tsx
|
||||
const {
|
||||
signInSecurityKeyEmail,
|
||||
needsEmailVerification,
|
||||
isLoading,
|
||||
isSuccess,
|
||||
isError,
|
||||
error
|
||||
} = useSignInSecurityKeyEmail()
|
||||
|
||||
console.log({ needsEmailVerification, isLoading, isSuccess, isError, error })
|
||||
|
||||
const handleFormSubmit = async (e) => {
|
||||
e.preventDefault()
|
||||
|
||||
await signInSecurityKeyEmail('joe@example.com')
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: AddSecuritKeyHookResult
|
||||
sidebar_label: AddSecuritKeyHookResult
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useAddSecurityKey.ts#L20
|
||||
---
|
||||
|
||||
# `AddSecuritKeyHookResult`
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isError</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an error occurred
|
||||
|
||||
**`@depreacted`**
|
||||
|
||||
use `!isSuccess` or `!!error` instead
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">error</span>** <span className="optional-status">required</span> `null` | `ErrorPayload`
|
||||
|
||||
Provides details about the error
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isSuccess</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
Returns `true` if the action is successful.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">add</span>** <span className="optional-status">required</span> `AddSecurityKeyHandler`
|
||||
|
||||
Add a security key to the current user with the WebAuthn API
|
||||
|
||||
---
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
# ⚠️ AUTO-GENERATED CONTENT. DO NOT EDIT THIS FILE DIRECTLY! ⚠️
|
||||
title: SignInSecurityKeyPasswordlessHookResult
|
||||
sidebar_label: SignInSecurityKeyPasswordlessHookResult
|
||||
description: No description provided.
|
||||
displayed_sidebar: referenceSidebar
|
||||
custom_edit_url: https://github.com/nhost/nhost/edit/main/packages/react/src/useSignInSecurityKeyEmail.ts#L14
|
||||
---
|
||||
|
||||
# `SignInSecurityKeyPasswordlessHookResult`
|
||||
|
||||
## Parameters
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isError</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an error occurred
|
||||
|
||||
**`@depreacted`**
|
||||
|
||||
use `!isSuccess` or `!!error` instead
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">error</span>** <span className="optional-status">required</span> `null` | `ErrorPayload`
|
||||
|
||||
Provides details about the error
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isLoading</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` when the action is executing, `false` when it finished its execution.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">isSuccess</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
Returns `true` if the action is successful.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">user</span>** <span className="optional-status">required</span> `null` | `User`
|
||||
|
||||
User information
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">accessToken</span>** <span className="optional-status">required</span> `null` | `string`
|
||||
|
||||
Access token (JWT)
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">needsEmailVerification</span>** <span className="optional-status">required</span> `boolean`
|
||||
|
||||
**`@returns`**
|
||||
|
||||
`true` if an email is required to complete the action, and that a verification email has been sent to complete the action.
|
||||
|
||||
---
|
||||
|
||||
**<span className="parameter-name">signInSecurityKeyEmail</span>** <span className="optional-status">required</span> `SignInSecurityKeyPasswordlessHandler`
|
||||
|
||||
---
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.10.0
|
||||
image: nhost/hasura-auth:0.11.0
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.4
|
||||
auth:
|
||||
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.10.0
|
||||
image: nhost/hasura-auth:0.11.0
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.4
|
||||
auth:
|
||||
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.10.0
|
||||
image: nhost/hasura-auth:0.11.0
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.4
|
||||
auth:
|
||||
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.10.0
|
||||
image: nhost/hasura-auth:0.11.0
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.4
|
||||
auth:
|
||||
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.10.0
|
||||
image: nhost/hasura-auth:0.11.0
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.4
|
||||
auth:
|
||||
|
||||
@@ -10,6 +10,12 @@ generates:
|
||||
namingConvention:
|
||||
typeNames: change-case-all#pascalCase
|
||||
transformUnderscore: true
|
||||
scalars:
|
||||
uuid: string
|
||||
bigint: number
|
||||
citext: string
|
||||
timestamptz: string
|
||||
|
||||
plugins:
|
||||
- typescript
|
||||
- typescript-operations
|
||||
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.10.0
|
||||
image: nhost/hasura-auth:0.11.0
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.4
|
||||
auth:
|
||||
@@ -127,6 +127,9 @@ auth:
|
||||
mfa:
|
||||
enabled: true
|
||||
issuer: nhost
|
||||
webauthn:
|
||||
enabled: true
|
||||
rp_name: 'Nhost React Apollo Example'
|
||||
storage:
|
||||
force_download_for_content_types: text/html,application/javascript
|
||||
version: 3
|
||||
|
||||
@@ -28,3 +28,22 @@ object_relationships:
|
||||
- name: user
|
||||
using:
|
||||
foreign_key_constraint_on: user_id
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- counter
|
||||
- id
|
||||
- nickname
|
||||
- user_id
|
||||
filter:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
allow_aggregations: true
|
||||
delete_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
backend_only: false
|
||||
filter:
|
||||
user_id:
|
||||
_eq: X-Hasura-User-Id
|
||||
|
||||
@@ -105,3 +105,29 @@ array_relationships:
|
||||
table:
|
||||
name: user_providers
|
||||
schema: auth
|
||||
select_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
columns:
|
||||
- active_mfa_type
|
||||
- avatar_url
|
||||
- created_at
|
||||
- default_role
|
||||
- disabled
|
||||
- display_name
|
||||
- email
|
||||
- email_verified
|
||||
- id
|
||||
- is_anonymous
|
||||
- last_seen
|
||||
- locale
|
||||
- metadata
|
||||
- otp_hash
|
||||
- otp_method_last_used
|
||||
- phone_number
|
||||
- phone_number_verified
|
||||
- updated_at
|
||||
- webauthn_current_challenge
|
||||
filter:
|
||||
id:
|
||||
_eq: X-Hasura-User-Id
|
||||
|
||||
4735
examples/react-apollo/pnpm-lock.yaml
generated
Normal file
4735
examples/react-apollo/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,89 @@
|
||||
import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { Button, Modal, SimpleGrid, TextInput } from '@mantine/core'
|
||||
import { showNotification } from '@mantine/notifications'
|
||||
import { useSignInEmailPasswordless, useSignInSecurityKeyEmail } from '@nhost/react'
|
||||
|
||||
export const SignUpPasswordlessForm: React.FC = () => {
|
||||
const navigate = useNavigate()
|
||||
|
||||
const { signInEmailPasswordless } = useSignInEmailPasswordless({ redirectTo: '/profile' })
|
||||
const { signInSecurityKeyEmail } = useSignInSecurityKeyEmail()
|
||||
|
||||
const [emailVerificationToggle, setEmailVerificationToggle] = useState(false)
|
||||
const [emailNeedsVerificationToggle, setEmailNeedsVerificationToggle] = useState(false)
|
||||
|
||||
const [email, setEmail] = useState('')
|
||||
|
||||
const signInEmail = async () => {
|
||||
const result = await signInEmailPasswordless(email)
|
||||
if (result.isError) {
|
||||
showNotification({
|
||||
color: 'red',
|
||||
title: 'Error',
|
||||
message: result.error?.message || null
|
||||
})
|
||||
} else {
|
||||
setEmailVerificationToggle(true)
|
||||
}
|
||||
}
|
||||
const signInDevice = async () => {
|
||||
const result = await signInSecurityKeyEmail(email)
|
||||
if (result.needsEmailVerification) {
|
||||
return
|
||||
}
|
||||
if (result.isError) {
|
||||
showNotification({
|
||||
color: 'red',
|
||||
title: 'Error',
|
||||
message: result.error?.message || null
|
||||
})
|
||||
return
|
||||
}
|
||||
navigate('/', { replace: true })
|
||||
}
|
||||
return (
|
||||
<SimpleGrid cols={1} spacing={6}>
|
||||
<Modal
|
||||
title="Verification email sent"
|
||||
centered
|
||||
opened={emailVerificationToggle}
|
||||
onClose={() => {
|
||||
setEmailVerificationToggle(false)
|
||||
}}
|
||||
>
|
||||
A verification email has been sent. Please check your inbox and follow the link to complete
|
||||
authentication. This page will automatically redirect you to the authenticated home page
|
||||
once the email has been verified.
|
||||
</Modal>
|
||||
<Modal
|
||||
title="Awaiting email verification"
|
||||
transition="fade"
|
||||
centered
|
||||
transitionDuration={600}
|
||||
opened={emailNeedsVerificationToggle}
|
||||
onClose={() => {
|
||||
setEmailNeedsVerificationToggle(false)
|
||||
}}
|
||||
>
|
||||
You need to verify your email first. Please check your mailbox and follow the confirmation
|
||||
link to complete the registration.
|
||||
</Modal>
|
||||
<TextInput
|
||||
type="email"
|
||||
placeholder="Email Address"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
<Button fullWidth onClick={signInDevice}>
|
||||
Use a security key
|
||||
</Button>
|
||||
<Button fullWidth onClick={signInEmail}>
|
||||
Send a magic link
|
||||
</Button>
|
||||
</SimpleGrid>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignUpPasswordlessForm
|
||||
@@ -10,8 +10,37 @@ export type Scalars = {
|
||||
Boolean: boolean;
|
||||
Int: number;
|
||||
Float: number;
|
||||
timestamptz: any;
|
||||
uuid: any;
|
||||
bigint: number;
|
||||
citext: string;
|
||||
jsonb: any;
|
||||
timestamptz: string;
|
||||
uuid: string;
|
||||
};
|
||||
|
||||
/** Boolean expression to compare columns of type "Boolean". All fields are combined with logical 'AND'. */
|
||||
export type BooleanComparisonExp = {
|
||||
_eq?: InputMaybe<Scalars['Boolean']>;
|
||||
_gt?: InputMaybe<Scalars['Boolean']>;
|
||||
_gte?: InputMaybe<Scalars['Boolean']>;
|
||||
_in?: InputMaybe<Array<Scalars['Boolean']>>;
|
||||
_is_null?: InputMaybe<Scalars['Boolean']>;
|
||||
_lt?: InputMaybe<Scalars['Boolean']>;
|
||||
_lte?: InputMaybe<Scalars['Boolean']>;
|
||||
_neq?: InputMaybe<Scalars['Boolean']>;
|
||||
_nin?: InputMaybe<Array<Scalars['Boolean']>>;
|
||||
};
|
||||
|
||||
/** Boolean expression to compare columns of type "Int". All fields are combined with logical 'AND'. */
|
||||
export type IntComparisonExp = {
|
||||
_eq?: InputMaybe<Scalars['Int']>;
|
||||
_gt?: InputMaybe<Scalars['Int']>;
|
||||
_gte?: InputMaybe<Scalars['Int']>;
|
||||
_in?: InputMaybe<Array<Scalars['Int']>>;
|
||||
_is_null?: InputMaybe<Scalars['Boolean']>;
|
||||
_lt?: InputMaybe<Scalars['Int']>;
|
||||
_lte?: InputMaybe<Scalars['Int']>;
|
||||
_neq?: InputMaybe<Scalars['Int']>;
|
||||
_nin?: InputMaybe<Array<Scalars['Int']>>;
|
||||
};
|
||||
|
||||
/** Boolean expression to compare columns of type "String". All fields are combined with logical 'AND'. */
|
||||
@@ -47,21 +76,521 @@ export type StringComparisonExp = {
|
||||
_similar?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
/** User webauthn authenticators. Don't modify its structure as Hasura Auth relies on it to function properly. */
|
||||
export type AuthUserAuthenticators = {
|
||||
__typename?: 'authUserAuthenticators';
|
||||
counter: Scalars['bigint'];
|
||||
id: Scalars['uuid'];
|
||||
nickname?: Maybe<Scalars['String']>;
|
||||
/** An object relationship */
|
||||
user: Users;
|
||||
userId: Scalars['uuid'];
|
||||
};
|
||||
|
||||
/** aggregated selection of "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsAggregate = {
|
||||
__typename?: 'authUserAuthenticators_aggregate';
|
||||
aggregate?: Maybe<AuthUserAuthenticatorsAggregateFields>;
|
||||
nodes: Array<AuthUserAuthenticators>;
|
||||
};
|
||||
|
||||
/** aggregate fields of "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsAggregateFields = {
|
||||
__typename?: 'authUserAuthenticators_aggregate_fields';
|
||||
avg?: Maybe<AuthUserAuthenticatorsAvgFields>;
|
||||
count: Scalars['Int'];
|
||||
max?: Maybe<AuthUserAuthenticatorsMaxFields>;
|
||||
min?: Maybe<AuthUserAuthenticatorsMinFields>;
|
||||
stddev?: Maybe<AuthUserAuthenticatorsStddevFields>;
|
||||
stddev_pop?: Maybe<AuthUserAuthenticatorsStddevPopFields>;
|
||||
stddev_samp?: Maybe<AuthUserAuthenticatorsStddevSampFields>;
|
||||
sum?: Maybe<AuthUserAuthenticatorsSumFields>;
|
||||
var_pop?: Maybe<AuthUserAuthenticatorsVarPopFields>;
|
||||
var_samp?: Maybe<AuthUserAuthenticatorsVarSampFields>;
|
||||
variance?: Maybe<AuthUserAuthenticatorsVarianceFields>;
|
||||
};
|
||||
|
||||
|
||||
/** aggregate fields of "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsAggregateFieldsCountArgs = {
|
||||
columns?: InputMaybe<Array<AuthUserAuthenticatorsSelectColumn>>;
|
||||
distinct?: InputMaybe<Scalars['Boolean']>;
|
||||
};
|
||||
|
||||
/** order by aggregate values of table "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsAggregateOrderBy = {
|
||||
avg?: InputMaybe<AuthUserAuthenticatorsAvgOrderBy>;
|
||||
count?: InputMaybe<OrderBy>;
|
||||
max?: InputMaybe<AuthUserAuthenticatorsMaxOrderBy>;
|
||||
min?: InputMaybe<AuthUserAuthenticatorsMinOrderBy>;
|
||||
stddev?: InputMaybe<AuthUserAuthenticatorsStddevOrderBy>;
|
||||
stddev_pop?: InputMaybe<AuthUserAuthenticatorsStddevPopOrderBy>;
|
||||
stddev_samp?: InputMaybe<AuthUserAuthenticatorsStddevSampOrderBy>;
|
||||
sum?: InputMaybe<AuthUserAuthenticatorsSumOrderBy>;
|
||||
var_pop?: InputMaybe<AuthUserAuthenticatorsVarPopOrderBy>;
|
||||
var_samp?: InputMaybe<AuthUserAuthenticatorsVarSampOrderBy>;
|
||||
variance?: InputMaybe<AuthUserAuthenticatorsVarianceOrderBy>;
|
||||
};
|
||||
|
||||
/** aggregate avg on columns */
|
||||
export type AuthUserAuthenticatorsAvgFields = {
|
||||
__typename?: 'authUserAuthenticators_avg_fields';
|
||||
counter?: Maybe<Scalars['Float']>;
|
||||
};
|
||||
|
||||
/** order by avg() on columns of table "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsAvgOrderBy = {
|
||||
counter?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
/** Boolean expression to filter rows from the table "auth.user_authenticators". All fields are combined with a logical 'AND'. */
|
||||
export type AuthUserAuthenticatorsBoolExp = {
|
||||
_and?: InputMaybe<Array<AuthUserAuthenticatorsBoolExp>>;
|
||||
_not?: InputMaybe<AuthUserAuthenticatorsBoolExp>;
|
||||
_or?: InputMaybe<Array<AuthUserAuthenticatorsBoolExp>>;
|
||||
counter?: InputMaybe<BigintComparisonExp>;
|
||||
id?: InputMaybe<UuidComparisonExp>;
|
||||
nickname?: InputMaybe<StringComparisonExp>;
|
||||
user?: InputMaybe<UsersBoolExp>;
|
||||
userId?: InputMaybe<UuidComparisonExp>;
|
||||
};
|
||||
|
||||
/** aggregate max on columns */
|
||||
export type AuthUserAuthenticatorsMaxFields = {
|
||||
__typename?: 'authUserAuthenticators_max_fields';
|
||||
counter?: Maybe<Scalars['bigint']>;
|
||||
id?: Maybe<Scalars['uuid']>;
|
||||
nickname?: Maybe<Scalars['String']>;
|
||||
userId?: Maybe<Scalars['uuid']>;
|
||||
};
|
||||
|
||||
/** order by max() on columns of table "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsMaxOrderBy = {
|
||||
counter?: InputMaybe<OrderBy>;
|
||||
id?: InputMaybe<OrderBy>;
|
||||
nickname?: InputMaybe<OrderBy>;
|
||||
userId?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
/** aggregate min on columns */
|
||||
export type AuthUserAuthenticatorsMinFields = {
|
||||
__typename?: 'authUserAuthenticators_min_fields';
|
||||
counter?: Maybe<Scalars['bigint']>;
|
||||
id?: Maybe<Scalars['uuid']>;
|
||||
nickname?: Maybe<Scalars['String']>;
|
||||
userId?: Maybe<Scalars['uuid']>;
|
||||
};
|
||||
|
||||
/** order by min() on columns of table "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsMinOrderBy = {
|
||||
counter?: InputMaybe<OrderBy>;
|
||||
id?: InputMaybe<OrderBy>;
|
||||
nickname?: InputMaybe<OrderBy>;
|
||||
userId?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
/** response of any mutation on the table "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsMutationResponse = {
|
||||
__typename?: 'authUserAuthenticators_mutation_response';
|
||||
/** number of rows affected by the mutation */
|
||||
affected_rows: Scalars['Int'];
|
||||
/** data from the rows affected by the mutation */
|
||||
returning: Array<AuthUserAuthenticators>;
|
||||
};
|
||||
|
||||
/** Ordering options when selecting data from "auth.user_authenticators". */
|
||||
export type AuthUserAuthenticatorsOrderBy = {
|
||||
counter?: InputMaybe<OrderBy>;
|
||||
id?: InputMaybe<OrderBy>;
|
||||
nickname?: InputMaybe<OrderBy>;
|
||||
user?: InputMaybe<UsersOrderBy>;
|
||||
userId?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
/** select columns of table "auth.user_authenticators" */
|
||||
export enum AuthUserAuthenticatorsSelectColumn {
|
||||
/** column name */
|
||||
Counter = 'counter',
|
||||
/** column name */
|
||||
Id = 'id',
|
||||
/** column name */
|
||||
Nickname = 'nickname',
|
||||
/** column name */
|
||||
UserId = 'userId'
|
||||
}
|
||||
|
||||
/** aggregate stddev on columns */
|
||||
export type AuthUserAuthenticatorsStddevFields = {
|
||||
__typename?: 'authUserAuthenticators_stddev_fields';
|
||||
counter?: Maybe<Scalars['Float']>;
|
||||
};
|
||||
|
||||
/** order by stddev() on columns of table "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsStddevOrderBy = {
|
||||
counter?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
/** aggregate stddev_pop on columns */
|
||||
export type AuthUserAuthenticatorsStddevPopFields = {
|
||||
__typename?: 'authUserAuthenticators_stddev_pop_fields';
|
||||
counter?: Maybe<Scalars['Float']>;
|
||||
};
|
||||
|
||||
/** order by stddev_pop() on columns of table "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsStddevPopOrderBy = {
|
||||
counter?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
/** aggregate stddev_samp on columns */
|
||||
export type AuthUserAuthenticatorsStddevSampFields = {
|
||||
__typename?: 'authUserAuthenticators_stddev_samp_fields';
|
||||
counter?: Maybe<Scalars['Float']>;
|
||||
};
|
||||
|
||||
/** order by stddev_samp() on columns of table "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsStddevSampOrderBy = {
|
||||
counter?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
/** aggregate sum on columns */
|
||||
export type AuthUserAuthenticatorsSumFields = {
|
||||
__typename?: 'authUserAuthenticators_sum_fields';
|
||||
counter?: Maybe<Scalars['bigint']>;
|
||||
};
|
||||
|
||||
/** order by sum() on columns of table "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsSumOrderBy = {
|
||||
counter?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
/** aggregate var_pop on columns */
|
||||
export type AuthUserAuthenticatorsVarPopFields = {
|
||||
__typename?: 'authUserAuthenticators_var_pop_fields';
|
||||
counter?: Maybe<Scalars['Float']>;
|
||||
};
|
||||
|
||||
/** order by var_pop() on columns of table "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsVarPopOrderBy = {
|
||||
counter?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
/** aggregate var_samp on columns */
|
||||
export type AuthUserAuthenticatorsVarSampFields = {
|
||||
__typename?: 'authUserAuthenticators_var_samp_fields';
|
||||
counter?: Maybe<Scalars['Float']>;
|
||||
};
|
||||
|
||||
/** order by var_samp() on columns of table "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsVarSampOrderBy = {
|
||||
counter?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
/** aggregate variance on columns */
|
||||
export type AuthUserAuthenticatorsVarianceFields = {
|
||||
__typename?: 'authUserAuthenticators_variance_fields';
|
||||
counter?: Maybe<Scalars['Float']>;
|
||||
};
|
||||
|
||||
/** order by variance() on columns of table "auth.user_authenticators" */
|
||||
export type AuthUserAuthenticatorsVarianceOrderBy = {
|
||||
counter?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
/** Boolean expression to compare columns of type "bigint". All fields are combined with logical 'AND'. */
|
||||
export type BigintComparisonExp = {
|
||||
_eq?: InputMaybe<Scalars['bigint']>;
|
||||
_gt?: InputMaybe<Scalars['bigint']>;
|
||||
_gte?: InputMaybe<Scalars['bigint']>;
|
||||
_in?: InputMaybe<Array<Scalars['bigint']>>;
|
||||
_is_null?: InputMaybe<Scalars['Boolean']>;
|
||||
_lt?: InputMaybe<Scalars['bigint']>;
|
||||
_lte?: InputMaybe<Scalars['bigint']>;
|
||||
_neq?: InputMaybe<Scalars['bigint']>;
|
||||
_nin?: InputMaybe<Array<Scalars['bigint']>>;
|
||||
};
|
||||
|
||||
/** Boolean expression to compare columns of type "citext". All fields are combined with logical 'AND'. */
|
||||
export type CitextComparisonExp = {
|
||||
_eq?: InputMaybe<Scalars['citext']>;
|
||||
_gt?: InputMaybe<Scalars['citext']>;
|
||||
_gte?: InputMaybe<Scalars['citext']>;
|
||||
/** does the column match the given case-insensitive pattern */
|
||||
_ilike?: InputMaybe<Scalars['citext']>;
|
||||
_in?: InputMaybe<Array<Scalars['citext']>>;
|
||||
/** does the column match the given POSIX regular expression, case insensitive */
|
||||
_iregex?: InputMaybe<Scalars['citext']>;
|
||||
_is_null?: InputMaybe<Scalars['Boolean']>;
|
||||
/** does the column match the given pattern */
|
||||
_like?: InputMaybe<Scalars['citext']>;
|
||||
_lt?: InputMaybe<Scalars['citext']>;
|
||||
_lte?: InputMaybe<Scalars['citext']>;
|
||||
_neq?: InputMaybe<Scalars['citext']>;
|
||||
/** does the column NOT match the given case-insensitive pattern */
|
||||
_nilike?: InputMaybe<Scalars['citext']>;
|
||||
_nin?: InputMaybe<Array<Scalars['citext']>>;
|
||||
/** does the column NOT match the given POSIX regular expression, case insensitive */
|
||||
_niregex?: InputMaybe<Scalars['citext']>;
|
||||
/** does the column NOT match the given pattern */
|
||||
_nlike?: InputMaybe<Scalars['citext']>;
|
||||
/** does the column NOT match the given POSIX regular expression, case sensitive */
|
||||
_nregex?: InputMaybe<Scalars['citext']>;
|
||||
/** does the column NOT match the given SQL regular expression */
|
||||
_nsimilar?: InputMaybe<Scalars['citext']>;
|
||||
/** does the column match the given POSIX regular expression, case sensitive */
|
||||
_regex?: InputMaybe<Scalars['citext']>;
|
||||
/** does the column match the given SQL regular expression */
|
||||
_similar?: InputMaybe<Scalars['citext']>;
|
||||
};
|
||||
|
||||
/** columns and relationships of "storage.files" */
|
||||
export type Files = {
|
||||
__typename?: 'files';
|
||||
bucketId: Scalars['String'];
|
||||
createdAt: Scalars['timestamptz'];
|
||||
etag?: Maybe<Scalars['String']>;
|
||||
id: Scalars['uuid'];
|
||||
isUploaded?: Maybe<Scalars['Boolean']>;
|
||||
mimeType?: Maybe<Scalars['String']>;
|
||||
name?: Maybe<Scalars['String']>;
|
||||
size?: Maybe<Scalars['Int']>;
|
||||
updatedAt: Scalars['timestamptz'];
|
||||
uploadedByUserId?: Maybe<Scalars['uuid']>;
|
||||
};
|
||||
|
||||
/** Boolean expression to filter rows from the table "storage.files". All fields are combined with a logical 'AND'. */
|
||||
export type FilesBoolExp = {
|
||||
_and?: InputMaybe<Array<FilesBoolExp>>;
|
||||
_not?: InputMaybe<FilesBoolExp>;
|
||||
_or?: InputMaybe<Array<FilesBoolExp>>;
|
||||
bucketId?: InputMaybe<StringComparisonExp>;
|
||||
createdAt?: InputMaybe<TimestamptzComparisonExp>;
|
||||
etag?: InputMaybe<StringComparisonExp>;
|
||||
id?: InputMaybe<UuidComparisonExp>;
|
||||
isUploaded?: InputMaybe<BooleanComparisonExp>;
|
||||
mimeType?: InputMaybe<StringComparisonExp>;
|
||||
name?: InputMaybe<StringComparisonExp>;
|
||||
size?: InputMaybe<IntComparisonExp>;
|
||||
updatedAt?: InputMaybe<TimestamptzComparisonExp>;
|
||||
uploadedByUserId?: InputMaybe<UuidComparisonExp>;
|
||||
};
|
||||
|
||||
/** unique or primary key constraints on table "storage.files" */
|
||||
export enum FilesConstraint {
|
||||
/** unique or primary key constraint on columns "id" */
|
||||
FilesPkey = 'files_pkey'
|
||||
}
|
||||
|
||||
/** input type for incrementing numeric columns in table "storage.files" */
|
||||
export type FilesIncInput = {
|
||||
size?: InputMaybe<Scalars['Int']>;
|
||||
};
|
||||
|
||||
/** input type for inserting data into table "storage.files" */
|
||||
export type FilesInsertInput = {
|
||||
bucketId?: InputMaybe<Scalars['String']>;
|
||||
createdAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
etag?: InputMaybe<Scalars['String']>;
|
||||
id?: InputMaybe<Scalars['uuid']>;
|
||||
isUploaded?: InputMaybe<Scalars['Boolean']>;
|
||||
mimeType?: InputMaybe<Scalars['String']>;
|
||||
name?: InputMaybe<Scalars['String']>;
|
||||
size?: InputMaybe<Scalars['Int']>;
|
||||
updatedAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
uploadedByUserId?: InputMaybe<Scalars['uuid']>;
|
||||
};
|
||||
|
||||
/** response of any mutation on the table "storage.files" */
|
||||
export type FilesMutationResponse = {
|
||||
__typename?: 'files_mutation_response';
|
||||
/** number of rows affected by the mutation */
|
||||
affected_rows: Scalars['Int'];
|
||||
/** data from the rows affected by the mutation */
|
||||
returning: Array<Files>;
|
||||
};
|
||||
|
||||
/** on_conflict condition type for table "storage.files" */
|
||||
export type FilesOnConflict = {
|
||||
constraint: FilesConstraint;
|
||||
update_columns?: Array<FilesUpdateColumn>;
|
||||
where?: InputMaybe<FilesBoolExp>;
|
||||
};
|
||||
|
||||
/** Ordering options when selecting data from "storage.files". */
|
||||
export type FilesOrderBy = {
|
||||
bucketId?: InputMaybe<OrderBy>;
|
||||
createdAt?: InputMaybe<OrderBy>;
|
||||
etag?: InputMaybe<OrderBy>;
|
||||
id?: InputMaybe<OrderBy>;
|
||||
isUploaded?: InputMaybe<OrderBy>;
|
||||
mimeType?: InputMaybe<OrderBy>;
|
||||
name?: InputMaybe<OrderBy>;
|
||||
size?: InputMaybe<OrderBy>;
|
||||
updatedAt?: InputMaybe<OrderBy>;
|
||||
uploadedByUserId?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
/** primary key columns input for table: files */
|
||||
export type FilesPkColumnsInput = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
/** select columns of table "storage.files" */
|
||||
export enum FilesSelectColumn {
|
||||
/** column name */
|
||||
BucketId = 'bucketId',
|
||||
/** column name */
|
||||
CreatedAt = 'createdAt',
|
||||
/** column name */
|
||||
Etag = 'etag',
|
||||
/** column name */
|
||||
Id = 'id',
|
||||
/** column name */
|
||||
IsUploaded = 'isUploaded',
|
||||
/** column name */
|
||||
MimeType = 'mimeType',
|
||||
/** column name */
|
||||
Name = 'name',
|
||||
/** column name */
|
||||
Size = 'size',
|
||||
/** column name */
|
||||
UpdatedAt = 'updatedAt',
|
||||
/** column name */
|
||||
UploadedByUserId = 'uploadedByUserId'
|
||||
}
|
||||
|
||||
/** input type for updating data in table "storage.files" */
|
||||
export type FilesSetInput = {
|
||||
bucketId?: InputMaybe<Scalars['String']>;
|
||||
createdAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
etag?: InputMaybe<Scalars['String']>;
|
||||
id?: InputMaybe<Scalars['uuid']>;
|
||||
isUploaded?: InputMaybe<Scalars['Boolean']>;
|
||||
mimeType?: InputMaybe<Scalars['String']>;
|
||||
name?: InputMaybe<Scalars['String']>;
|
||||
size?: InputMaybe<Scalars['Int']>;
|
||||
updatedAt?: InputMaybe<Scalars['timestamptz']>;
|
||||
uploadedByUserId?: InputMaybe<Scalars['uuid']>;
|
||||
};
|
||||
|
||||
/** update columns of table "storage.files" */
|
||||
export enum FilesUpdateColumn {
|
||||
/** column name */
|
||||
BucketId = 'bucketId',
|
||||
/** column name */
|
||||
CreatedAt = 'createdAt',
|
||||
/** column name */
|
||||
Etag = 'etag',
|
||||
/** column name */
|
||||
Id = 'id',
|
||||
/** column name */
|
||||
IsUploaded = 'isUploaded',
|
||||
/** column name */
|
||||
MimeType = 'mimeType',
|
||||
/** column name */
|
||||
Name = 'name',
|
||||
/** column name */
|
||||
Size = 'size',
|
||||
/** column name */
|
||||
UpdatedAt = 'updatedAt',
|
||||
/** column name */
|
||||
UploadedByUserId = 'uploadedByUserId'
|
||||
}
|
||||
|
||||
export type FilesUpdates = {
|
||||
/** increments the numeric columns with given value of the filtered values */
|
||||
_inc?: InputMaybe<FilesIncInput>;
|
||||
/** sets the columns of the filtered rows to the given values */
|
||||
_set?: InputMaybe<FilesSetInput>;
|
||||
where: FilesBoolExp;
|
||||
};
|
||||
|
||||
export type JsonbCastExp = {
|
||||
String?: InputMaybe<StringComparisonExp>;
|
||||
};
|
||||
|
||||
/** Boolean expression to compare columns of type "jsonb". All fields are combined with logical 'AND'. */
|
||||
export type JsonbComparisonExp = {
|
||||
_cast?: InputMaybe<JsonbCastExp>;
|
||||
/** is the column contained in the given json value */
|
||||
_contained_in?: InputMaybe<Scalars['jsonb']>;
|
||||
/** does the column contain the given json value at the top level */
|
||||
_contains?: InputMaybe<Scalars['jsonb']>;
|
||||
_eq?: InputMaybe<Scalars['jsonb']>;
|
||||
_gt?: InputMaybe<Scalars['jsonb']>;
|
||||
_gte?: InputMaybe<Scalars['jsonb']>;
|
||||
/** does the string exist as a top-level key in the column */
|
||||
_has_key?: InputMaybe<Scalars['String']>;
|
||||
/** do all of these strings exist as top-level keys in the column */
|
||||
_has_keys_all?: InputMaybe<Array<Scalars['String']>>;
|
||||
/** do any of these strings exist as top-level keys in the column */
|
||||
_has_keys_any?: InputMaybe<Array<Scalars['String']>>;
|
||||
_in?: InputMaybe<Array<Scalars['jsonb']>>;
|
||||
_is_null?: InputMaybe<Scalars['Boolean']>;
|
||||
_lt?: InputMaybe<Scalars['jsonb']>;
|
||||
_lte?: InputMaybe<Scalars['jsonb']>;
|
||||
_neq?: InputMaybe<Scalars['jsonb']>;
|
||||
_nin?: InputMaybe<Array<Scalars['jsonb']>>;
|
||||
};
|
||||
|
||||
/** mutation root */
|
||||
export type MutationRoot = {
|
||||
__typename?: 'mutation_root';
|
||||
/** delete single row from the table: "auth.user_authenticators" */
|
||||
deleteAuthUserAuthenticator?: Maybe<AuthUserAuthenticators>;
|
||||
/** delete data from the table: "auth.user_authenticators" */
|
||||
deleteAuthUserAuthenticators?: Maybe<AuthUserAuthenticatorsMutationResponse>;
|
||||
/** delete single row from the table: "storage.files" */
|
||||
deleteFile?: Maybe<Files>;
|
||||
/** delete data from the table: "storage.files" */
|
||||
deleteFiles?: Maybe<FilesMutationResponse>;
|
||||
/** delete single row from the table: "todos" */
|
||||
deleteTodo?: Maybe<Todos>;
|
||||
/** delete data from the table: "todos" */
|
||||
deleteTodos?: Maybe<TodosMutationResponse>;
|
||||
/** insert a single row into the table: "storage.files" */
|
||||
insertFile?: Maybe<Files>;
|
||||
/** insert data into the table: "storage.files" */
|
||||
insertFiles?: Maybe<FilesMutationResponse>;
|
||||
/** insert a single row into the table: "todos" */
|
||||
insertTodo?: Maybe<Todos>;
|
||||
/** insert data into the table: "todos" */
|
||||
insertTodos?: Maybe<TodosMutationResponse>;
|
||||
/** update single row of the table: "storage.files" */
|
||||
updateFile?: Maybe<Files>;
|
||||
/** update data of the table: "storage.files" */
|
||||
updateFiles?: Maybe<FilesMutationResponse>;
|
||||
/** update single row of the table: "todos" */
|
||||
updateTodo?: Maybe<Todos>;
|
||||
/** update data of the table: "todos" */
|
||||
updateTodos?: Maybe<TodosMutationResponse>;
|
||||
/** update multiples rows of table: "storage.files" */
|
||||
update_files_many?: Maybe<Array<Maybe<FilesMutationResponse>>>;
|
||||
/** update multiples rows of table: "todos" */
|
||||
update_todos_many?: Maybe<Array<Maybe<TodosMutationResponse>>>;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type MutationRootDeleteAuthUserAuthenticatorArgs = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type MutationRootDeleteAuthUserAuthenticatorsArgs = {
|
||||
where: AuthUserAuthenticatorsBoolExp;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type MutationRootDeleteFileArgs = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type MutationRootDeleteFilesArgs = {
|
||||
where: FilesBoolExp;
|
||||
};
|
||||
|
||||
|
||||
@@ -77,6 +606,20 @@ export type MutationRootDeleteTodosArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type MutationRootInsertFileArgs = {
|
||||
object: FilesInsertInput;
|
||||
on_conflict?: InputMaybe<FilesOnConflict>;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type MutationRootInsertFilesArgs = {
|
||||
objects: Array<FilesInsertInput>;
|
||||
on_conflict?: InputMaybe<FilesOnConflict>;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type MutationRootInsertTodoArgs = {
|
||||
object: TodosInsertInput;
|
||||
@@ -91,6 +634,22 @@ export type MutationRootInsertTodosArgs = {
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type MutationRootUpdateFileArgs = {
|
||||
_inc?: InputMaybe<FilesIncInput>;
|
||||
_set?: InputMaybe<FilesSetInput>;
|
||||
pk_columns: FilesPkColumnsInput;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type MutationRootUpdateFilesArgs = {
|
||||
_inc?: InputMaybe<FilesIncInput>;
|
||||
_set?: InputMaybe<FilesSetInput>;
|
||||
where: FilesBoolExp;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type MutationRootUpdateTodoArgs = {
|
||||
_set?: InputMaybe<TodosSetInput>;
|
||||
@@ -104,6 +663,18 @@ export type MutationRootUpdateTodosArgs = {
|
||||
where: TodosBoolExp;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type MutationRootUpdateFilesManyArgs = {
|
||||
updates: Array<FilesUpdates>;
|
||||
};
|
||||
|
||||
|
||||
/** mutation root */
|
||||
export type MutationRootUpdateTodosManyArgs = {
|
||||
updates: Array<TodosUpdates>;
|
||||
};
|
||||
|
||||
/** column ordering options */
|
||||
export enum OrderBy {
|
||||
/** in ascending order, nulls last */
|
||||
@@ -122,12 +693,63 @@ export enum OrderBy {
|
||||
|
||||
export type QueryRoot = {
|
||||
__typename?: 'query_root';
|
||||
/** fetch data from the table: "auth.user_authenticators" using primary key columns */
|
||||
authUserAuthenticator?: Maybe<AuthUserAuthenticators>;
|
||||
/** fetch data from the table: "auth.user_authenticators" */
|
||||
authUserAuthenticators: Array<AuthUserAuthenticators>;
|
||||
/** fetch aggregated fields from the table: "auth.user_authenticators" */
|
||||
authUserAuthenticatorsAggregate: AuthUserAuthenticatorsAggregate;
|
||||
/** fetch data from the table: "storage.files" using primary key columns */
|
||||
file?: Maybe<Files>;
|
||||
/** fetch data from the table: "storage.files" */
|
||||
files: Array<Files>;
|
||||
/** fetch data from the table: "todos" using primary key columns */
|
||||
todo?: Maybe<Todos>;
|
||||
/** fetch data from the table: "todos" */
|
||||
todos: Array<Todos>;
|
||||
/** fetch aggregated fields from the table: "todos" */
|
||||
todosAggregate: TodosAggregate;
|
||||
/** fetch data from the table: "auth.users" using primary key columns */
|
||||
user?: Maybe<Users>;
|
||||
/** fetch data from the table: "auth.users" */
|
||||
users: Array<Users>;
|
||||
};
|
||||
|
||||
|
||||
export type QueryRootAuthUserAuthenticatorArgs = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryRootAuthUserAuthenticatorsArgs = {
|
||||
distinct_on?: InputMaybe<Array<AuthUserAuthenticatorsSelectColumn>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<AuthUserAuthenticatorsOrderBy>>;
|
||||
where?: InputMaybe<AuthUserAuthenticatorsBoolExp>;
|
||||
};
|
||||
|
||||
|
||||
export type QueryRootAuthUserAuthenticatorsAggregateArgs = {
|
||||
distinct_on?: InputMaybe<Array<AuthUserAuthenticatorsSelectColumn>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<AuthUserAuthenticatorsOrderBy>>;
|
||||
where?: InputMaybe<AuthUserAuthenticatorsBoolExp>;
|
||||
};
|
||||
|
||||
|
||||
export type QueryRootFileArgs = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryRootFilesArgs = {
|
||||
distinct_on?: InputMaybe<Array<FilesSelectColumn>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<FilesOrderBy>>;
|
||||
where?: InputMaybe<FilesBoolExp>;
|
||||
};
|
||||
|
||||
|
||||
@@ -153,14 +775,79 @@ export type QueryRootTodosAggregateArgs = {
|
||||
where?: InputMaybe<TodosBoolExp>;
|
||||
};
|
||||
|
||||
|
||||
export type QueryRootUserArgs = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
|
||||
export type QueryRootUsersArgs = {
|
||||
distinct_on?: InputMaybe<Array<UsersSelectColumn>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<UsersOrderBy>>;
|
||||
where?: InputMaybe<UsersBoolExp>;
|
||||
};
|
||||
|
||||
export type SubscriptionRoot = {
|
||||
__typename?: 'subscription_root';
|
||||
/** fetch data from the table: "auth.user_authenticators" using primary key columns */
|
||||
authUserAuthenticator?: Maybe<AuthUserAuthenticators>;
|
||||
/** fetch data from the table: "auth.user_authenticators" */
|
||||
authUserAuthenticators: Array<AuthUserAuthenticators>;
|
||||
/** fetch aggregated fields from the table: "auth.user_authenticators" */
|
||||
authUserAuthenticatorsAggregate: AuthUserAuthenticatorsAggregate;
|
||||
/** fetch data from the table: "storage.files" using primary key columns */
|
||||
file?: Maybe<Files>;
|
||||
/** fetch data from the table: "storage.files" */
|
||||
files: Array<Files>;
|
||||
/** fetch data from the table: "todos" using primary key columns */
|
||||
todo?: Maybe<Todos>;
|
||||
/** fetch data from the table: "todos" */
|
||||
todos: Array<Todos>;
|
||||
/** fetch aggregated fields from the table: "todos" */
|
||||
todosAggregate: TodosAggregate;
|
||||
/** fetch data from the table: "auth.users" using primary key columns */
|
||||
user?: Maybe<Users>;
|
||||
/** fetch data from the table: "auth.users" */
|
||||
users: Array<Users>;
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionRootAuthUserAuthenticatorArgs = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionRootAuthUserAuthenticatorsArgs = {
|
||||
distinct_on?: InputMaybe<Array<AuthUserAuthenticatorsSelectColumn>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<AuthUserAuthenticatorsOrderBy>>;
|
||||
where?: InputMaybe<AuthUserAuthenticatorsBoolExp>;
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionRootAuthUserAuthenticatorsAggregateArgs = {
|
||||
distinct_on?: InputMaybe<Array<AuthUserAuthenticatorsSelectColumn>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<AuthUserAuthenticatorsOrderBy>>;
|
||||
where?: InputMaybe<AuthUserAuthenticatorsBoolExp>;
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionRootFileArgs = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionRootFilesArgs = {
|
||||
distinct_on?: InputMaybe<Array<FilesSelectColumn>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<FilesOrderBy>>;
|
||||
where?: InputMaybe<FilesBoolExp>;
|
||||
};
|
||||
|
||||
|
||||
@@ -186,6 +873,20 @@ export type SubscriptionRootTodosAggregateArgs = {
|
||||
where?: InputMaybe<TodosBoolExp>;
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionRootUserArgs = {
|
||||
id: Scalars['uuid'];
|
||||
};
|
||||
|
||||
|
||||
export type SubscriptionRootUsersArgs = {
|
||||
distinct_on?: InputMaybe<Array<UsersSelectColumn>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<UsersOrderBy>>;
|
||||
where?: InputMaybe<UsersBoolExp>;
|
||||
};
|
||||
|
||||
/** Boolean expression to compare columns of type "timestamptz". All fields are combined with logical 'AND'. */
|
||||
export type TimestamptzComparisonExp = {
|
||||
_eq?: InputMaybe<Scalars['timestamptz']>;
|
||||
@@ -206,6 +907,8 @@ export type Todos = {
|
||||
createdAt: Scalars['timestamptz'];
|
||||
id: Scalars['uuid'];
|
||||
updatedAt: Scalars['timestamptz'];
|
||||
/** An object relationship */
|
||||
user: Users;
|
||||
userId: Scalars['uuid'];
|
||||
};
|
||||
|
||||
@@ -240,12 +943,13 @@ export type TodosBoolExp = {
|
||||
createdAt?: InputMaybe<TimestamptzComparisonExp>;
|
||||
id?: InputMaybe<UuidComparisonExp>;
|
||||
updatedAt?: InputMaybe<TimestamptzComparisonExp>;
|
||||
user?: InputMaybe<UsersBoolExp>;
|
||||
userId?: InputMaybe<UuidComparisonExp>;
|
||||
};
|
||||
|
||||
/** unique or primary key constraints on table "todos" */
|
||||
export enum TodosConstraint {
|
||||
/** unique or primary key constraint */
|
||||
/** unique or primary key constraint on columns "id" */
|
||||
TodosPkey = 'todos_pkey'
|
||||
}
|
||||
|
||||
@@ -297,6 +1001,7 @@ export type TodosOrderBy = {
|
||||
createdAt?: InputMaybe<OrderBy>;
|
||||
id?: InputMaybe<OrderBy>;
|
||||
updatedAt?: InputMaybe<OrderBy>;
|
||||
user?: InputMaybe<UsersOrderBy>;
|
||||
userId?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
@@ -330,6 +1035,159 @@ export enum TodosUpdateColumn {
|
||||
Contents = 'contents'
|
||||
}
|
||||
|
||||
export type TodosUpdates = {
|
||||
/** sets the columns of the filtered rows to the given values */
|
||||
_set?: InputMaybe<TodosSetInput>;
|
||||
where: TodosBoolExp;
|
||||
};
|
||||
|
||||
/** User account information. Don't modify its structure as Hasura Auth relies on it to function properly. */
|
||||
export type Users = {
|
||||
__typename?: 'users';
|
||||
activeMfaType?: Maybe<Scalars['String']>;
|
||||
/** An array relationship */
|
||||
authenticators: Array<AuthUserAuthenticators>;
|
||||
/** An aggregate relationship */
|
||||
authenticators_aggregate: AuthUserAuthenticatorsAggregate;
|
||||
avatarUrl: Scalars['String'];
|
||||
createdAt: Scalars['timestamptz'];
|
||||
currentChallenge?: Maybe<Scalars['String']>;
|
||||
defaultRole: Scalars['String'];
|
||||
disabled: Scalars['Boolean'];
|
||||
displayName: Scalars['String'];
|
||||
email?: Maybe<Scalars['citext']>;
|
||||
emailVerified: Scalars['Boolean'];
|
||||
id: Scalars['uuid'];
|
||||
isAnonymous: Scalars['Boolean'];
|
||||
lastSeen?: Maybe<Scalars['timestamptz']>;
|
||||
locale: Scalars['String'];
|
||||
metadata?: Maybe<Scalars['jsonb']>;
|
||||
otpHash?: Maybe<Scalars['String']>;
|
||||
otpMethodLastUsed?: Maybe<Scalars['String']>;
|
||||
phoneNumber?: Maybe<Scalars['String']>;
|
||||
phoneNumberVerified: Scalars['Boolean'];
|
||||
updatedAt: Scalars['timestamptz'];
|
||||
};
|
||||
|
||||
|
||||
/** User account information. Don't modify its structure as Hasura Auth relies on it to function properly. */
|
||||
export type UsersAuthenticatorsArgs = {
|
||||
distinct_on?: InputMaybe<Array<AuthUserAuthenticatorsSelectColumn>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<AuthUserAuthenticatorsOrderBy>>;
|
||||
where?: InputMaybe<AuthUserAuthenticatorsBoolExp>;
|
||||
};
|
||||
|
||||
|
||||
/** User account information. Don't modify its structure as Hasura Auth relies on it to function properly. */
|
||||
export type UsersAuthenticatorsAggregateArgs = {
|
||||
distinct_on?: InputMaybe<Array<AuthUserAuthenticatorsSelectColumn>>;
|
||||
limit?: InputMaybe<Scalars['Int']>;
|
||||
offset?: InputMaybe<Scalars['Int']>;
|
||||
order_by?: InputMaybe<Array<AuthUserAuthenticatorsOrderBy>>;
|
||||
where?: InputMaybe<AuthUserAuthenticatorsBoolExp>;
|
||||
};
|
||||
|
||||
|
||||
/** User account information. Don't modify its structure as Hasura Auth relies on it to function properly. */
|
||||
export type UsersMetadataArgs = {
|
||||
path?: InputMaybe<Scalars['String']>;
|
||||
};
|
||||
|
||||
/** Boolean expression to filter rows from the table "auth.users". All fields are combined with a logical 'AND'. */
|
||||
export type UsersBoolExp = {
|
||||
_and?: InputMaybe<Array<UsersBoolExp>>;
|
||||
_not?: InputMaybe<UsersBoolExp>;
|
||||
_or?: InputMaybe<Array<UsersBoolExp>>;
|
||||
activeMfaType?: InputMaybe<StringComparisonExp>;
|
||||
authenticators?: InputMaybe<AuthUserAuthenticatorsBoolExp>;
|
||||
avatarUrl?: InputMaybe<StringComparisonExp>;
|
||||
createdAt?: InputMaybe<TimestamptzComparisonExp>;
|
||||
currentChallenge?: InputMaybe<StringComparisonExp>;
|
||||
defaultRole?: InputMaybe<StringComparisonExp>;
|
||||
disabled?: InputMaybe<BooleanComparisonExp>;
|
||||
displayName?: InputMaybe<StringComparisonExp>;
|
||||
email?: InputMaybe<CitextComparisonExp>;
|
||||
emailVerified?: InputMaybe<BooleanComparisonExp>;
|
||||
id?: InputMaybe<UuidComparisonExp>;
|
||||
isAnonymous?: InputMaybe<BooleanComparisonExp>;
|
||||
lastSeen?: InputMaybe<TimestamptzComparisonExp>;
|
||||
locale?: InputMaybe<StringComparisonExp>;
|
||||
metadata?: InputMaybe<JsonbComparisonExp>;
|
||||
otpHash?: InputMaybe<StringComparisonExp>;
|
||||
otpMethodLastUsed?: InputMaybe<StringComparisonExp>;
|
||||
phoneNumber?: InputMaybe<StringComparisonExp>;
|
||||
phoneNumberVerified?: InputMaybe<BooleanComparisonExp>;
|
||||
updatedAt?: InputMaybe<TimestamptzComparisonExp>;
|
||||
};
|
||||
|
||||
/** Ordering options when selecting data from "auth.users". */
|
||||
export type UsersOrderBy = {
|
||||
activeMfaType?: InputMaybe<OrderBy>;
|
||||
authenticators_aggregate?: InputMaybe<AuthUserAuthenticatorsAggregateOrderBy>;
|
||||
avatarUrl?: InputMaybe<OrderBy>;
|
||||
createdAt?: InputMaybe<OrderBy>;
|
||||
currentChallenge?: InputMaybe<OrderBy>;
|
||||
defaultRole?: InputMaybe<OrderBy>;
|
||||
disabled?: InputMaybe<OrderBy>;
|
||||
displayName?: InputMaybe<OrderBy>;
|
||||
email?: InputMaybe<OrderBy>;
|
||||
emailVerified?: InputMaybe<OrderBy>;
|
||||
id?: InputMaybe<OrderBy>;
|
||||
isAnonymous?: InputMaybe<OrderBy>;
|
||||
lastSeen?: InputMaybe<OrderBy>;
|
||||
locale?: InputMaybe<OrderBy>;
|
||||
metadata?: InputMaybe<OrderBy>;
|
||||
otpHash?: InputMaybe<OrderBy>;
|
||||
otpMethodLastUsed?: InputMaybe<OrderBy>;
|
||||
phoneNumber?: InputMaybe<OrderBy>;
|
||||
phoneNumberVerified?: InputMaybe<OrderBy>;
|
||||
updatedAt?: InputMaybe<OrderBy>;
|
||||
};
|
||||
|
||||
/** select columns of table "auth.users" */
|
||||
export enum UsersSelectColumn {
|
||||
/** column name */
|
||||
ActiveMfaType = 'activeMfaType',
|
||||
/** column name */
|
||||
AvatarUrl = 'avatarUrl',
|
||||
/** column name */
|
||||
CreatedAt = 'createdAt',
|
||||
/** column name */
|
||||
CurrentChallenge = 'currentChallenge',
|
||||
/** column name */
|
||||
DefaultRole = 'defaultRole',
|
||||
/** column name */
|
||||
Disabled = 'disabled',
|
||||
/** column name */
|
||||
DisplayName = 'displayName',
|
||||
/** column name */
|
||||
Email = 'email',
|
||||
/** column name */
|
||||
EmailVerified = 'emailVerified',
|
||||
/** column name */
|
||||
Id = 'id',
|
||||
/** column name */
|
||||
IsAnonymous = 'isAnonymous',
|
||||
/** column name */
|
||||
LastSeen = 'lastSeen',
|
||||
/** column name */
|
||||
Locale = 'locale',
|
||||
/** column name */
|
||||
Metadata = 'metadata',
|
||||
/** column name */
|
||||
OtpHash = 'otpHash',
|
||||
/** column name */
|
||||
OtpMethodLastUsed = 'otpMethodLastUsed',
|
||||
/** column name */
|
||||
PhoneNumber = 'phoneNumber',
|
||||
/** column name */
|
||||
PhoneNumberVerified = 'phoneNumberVerified',
|
||||
/** column name */
|
||||
UpdatedAt = 'updatedAt'
|
||||
}
|
||||
|
||||
/** Boolean expression to compare columns of type "uuid". All fields are combined with logical 'AND'. */
|
||||
export type UuidComparisonExp = {
|
||||
_eq?: InputMaybe<Scalars['uuid']>;
|
||||
@@ -346,11 +1204,27 @@ export type UuidComparisonExp = {
|
||||
export type TodoListQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type TodoListQuery = { __typename?: 'query_root', todos: Array<{ __typename?: 'todos', id: any, contents: string }> };
|
||||
export type TodoListQuery = { __typename?: 'query_root', todos: Array<{ __typename?: 'todos', id: string, contents: string }> };
|
||||
|
||||
export type AddItemMutationVariables = Exact<{
|
||||
contents: Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type AddItemMutation = { __typename?: 'mutation_root', insertTodo?: { __typename?: 'todos', id: any } | null };
|
||||
export type AddItemMutation = { __typename?: 'mutation_root', insertTodo?: { __typename?: 'todos', id: string, contents: string } | null };
|
||||
|
||||
export type NewTodoFragment = { __typename?: 'todos', id: string, contents: string };
|
||||
|
||||
export type SecurityKeysQueryVariables = Exact<{
|
||||
userId: Scalars['uuid'];
|
||||
}>;
|
||||
|
||||
|
||||
export type SecurityKeysQuery = { __typename?: 'query_root', authUserAuthenticators: Array<{ __typename?: 'authUserAuthenticators', id: string, nickname?: string | null }> };
|
||||
|
||||
export type RemoveSecurityKeyMutationVariables = Exact<{
|
||||
id: Scalars['uuid'];
|
||||
}>;
|
||||
|
||||
|
||||
export type RemoveSecurityKeyMutation = { __typename?: 'mutation_root', deleteAuthUserAuthenticator?: { __typename?: 'authUserAuthenticators', id: string } | null };
|
||||
|
||||
@@ -5,6 +5,7 @@ import { useHasuraClaims, useNhostClient, useUserData } from '@nhost/react'
|
||||
import { ChangeEmail } from './change-email'
|
||||
import { ChangePassword } from './change-password'
|
||||
import { Mfa } from './mfa'
|
||||
import { SecurityKeys } from './security-keys'
|
||||
|
||||
export const ProfilePage: React.FC = () => {
|
||||
const claims = useHasuraClaims()
|
||||
@@ -13,6 +14,7 @@ export const ProfilePage: React.FC = () => {
|
||||
return (
|
||||
<Container>
|
||||
<Title>Profile page</Title>
|
||||
<SecurityKeys />
|
||||
<Mfa />
|
||||
<ChangeEmail />
|
||||
<ChangePassword />
|
||||
|
||||
96
examples/react-apollo/src/profile/security-keys.tsx
Normal file
96
examples/react-apollo/src/profile/security-keys.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import { useState } from 'react'
|
||||
import { FaMinus } from 'react-icons/fa'
|
||||
import { RemoveSecurityKeyMutation, SecurityKeysQuery } from 'src/generated'
|
||||
|
||||
import { gql, useMutation } from '@apollo/client'
|
||||
import { ActionIcon, Button, Card, SimpleGrid, Table, TextInput, Title } from '@mantine/core'
|
||||
import { useInputState } from '@mantine/hooks'
|
||||
import { useAddSecurityKey, useUserId } from '@nhost/react'
|
||||
import { useAuthQuery } from '@nhost/react-apollo'
|
||||
|
||||
const SECURITY_KEYS_LIST = gql`
|
||||
query securityKeys($userId: uuid!) {
|
||||
authUserAuthenticators(where: { userId: { _eq: $userId } }) {
|
||||
id
|
||||
nickname
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const REMOVE_SECURITY_KEY = gql`
|
||||
mutation removeSecurityKey($id: uuid!) {
|
||||
deleteAuthUserAuthenticator(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const SecurityKeys: React.FC = () => {
|
||||
const { add } = useAddSecurityKey()
|
||||
const userId = useUserId()
|
||||
const [nickname, setNickname] = useInputState('')
|
||||
const [list, setList] = useState<{ id: string; nickname?: string | null }[]>([])
|
||||
useAuthQuery<SecurityKeysQuery>(SECURITY_KEYS_LIST, {
|
||||
variables: { userId },
|
||||
onCompleted: ({ authUserAuthenticators }) => {
|
||||
if (authUserAuthenticators) {
|
||||
setList(authUserAuthenticators || [])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const addKey = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault()
|
||||
const { key, error } = await add(nickname)
|
||||
if (error) {
|
||||
console.log(error)
|
||||
} else {
|
||||
setNickname('')
|
||||
}
|
||||
if (key) {
|
||||
setList([...list, key])
|
||||
}
|
||||
}
|
||||
const [removeKey] = useMutation<RemoveSecurityKeyMutation>(REMOVE_SECURITY_KEY, {
|
||||
onCompleted: ({ deleteAuthUserAuthenticator }) => {
|
||||
if (deleteAuthUserAuthenticator?.id) {
|
||||
setList(list.filter((item) => item.id !== deleteAuthUserAuthenticator.id))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<Card shadow="sm" p="lg" m="sm">
|
||||
<Title>Security keys</Title>
|
||||
<Table style={{ width: '100%', maxWidth: '100%' }}>
|
||||
<colgroup>
|
||||
<col />
|
||||
<col width="20%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
{list.map(({ id, nickname }) => (
|
||||
<tr key={id}>
|
||||
<td>{nickname || id}</td>
|
||||
<td>
|
||||
<ActionIcon onClick={() => removeKey({ variables: { id } })} color="red">
|
||||
<FaMinus />
|
||||
</ActionIcon>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
<form onSubmit={addKey}>
|
||||
<SimpleGrid cols={2}>
|
||||
<TextInput
|
||||
autoFocus
|
||||
value={nickname}
|
||||
onChange={setNickname}
|
||||
placeholder="Nickname for the device (optional)"
|
||||
/>
|
||||
<Button type="submit">Add a new device</Button>
|
||||
</SimpleGrid>
|
||||
</form>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -3,11 +3,12 @@ import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { Button, Modal, TextInput } from '@mantine/core'
|
||||
import { showNotification } from '@mantine/notifications'
|
||||
import { useSignInEmailPassword } from '@nhost/react'
|
||||
import { useSignInEmailPassword, useSignInSecurityKeyEmail } from '@nhost/react'
|
||||
|
||||
import AuthLink from '../components/AuthLink'
|
||||
|
||||
export const EmailPassword: React.FC = () => {
|
||||
const { signInSecurityKeyEmail } = useSignInSecurityKeyEmail()
|
||||
const [email, setEmail] = useState('')
|
||||
const [password, setPassword] = useState('')
|
||||
const [otp, setOtp] = useState('')
|
||||
@@ -90,7 +91,20 @@ export const EmailPassword: React.FC = () => {
|
||||
size="lg"
|
||||
style={{ marginBottom: '0.5em' }}
|
||||
/>
|
||||
|
||||
<Button
|
||||
fullWidth
|
||||
onClick={() => {
|
||||
signInSecurityKeyEmail(email)
|
||||
.then((res) => {
|
||||
console.log(res)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log('bummer', err)
|
||||
})
|
||||
}}
|
||||
>
|
||||
SecurityKey
|
||||
</Button>
|
||||
<Button fullWidth onClick={signIn}>
|
||||
Sign in
|
||||
</Button>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Divider } from '@mantine/core'
|
||||
|
||||
import AuthLink from '../components/AuthLink'
|
||||
import EmailPasswordlessForm from '../components/SignUpServerlessForm'
|
||||
import PasswordlessForm from '../components/SignInPasswordlessForm'
|
||||
|
||||
export const EmailPasswordless: React.FC = () => {
|
||||
return (
|
||||
<>
|
||||
<EmailPasswordlessForm />
|
||||
<PasswordlessForm />
|
||||
<Divider />
|
||||
<AuthLink link="/sign-up" variant="white">
|
||||
← Other Login Options
|
||||
|
||||
@@ -17,7 +17,7 @@ const Index: React.FC = () => (
|
||||
<OAuthLinks />
|
||||
<Divider my="sm" />
|
||||
<AuthLink leftIcon={<FaLock />} variant="outline" link="/sign-in/email-passwordless">
|
||||
Continue with passwordless email
|
||||
Continue passwordless
|
||||
</AuthLink>
|
||||
<AuthLink variant="subtle" link="/sign-in/email-password">
|
||||
Continue with email + password
|
||||
|
||||
62
examples/react-apollo/src/sign-in/webauthn.tsx
Normal file
62
examples/react-apollo/src/sign-in/webauthn.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import { useState } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
import { Button, Modal, TextInput } from '@mantine/core'
|
||||
import { showNotification } from '@mantine/notifications'
|
||||
import { useSignInSecurityKeyEmail } from '@nhost/react'
|
||||
|
||||
import AuthLink from '../components/AuthLink'
|
||||
|
||||
export const EmailPassword: React.FC = () => {
|
||||
const { signInSecurityKeyEmail } = useSignInSecurityKeyEmail()
|
||||
const [email, setEmail] = useState('')
|
||||
const navigate = useNavigate()
|
||||
const [emailVerificationToggle, setEmailVerificationToggle] = useState(false)
|
||||
|
||||
const signIn = async () => {
|
||||
const result = await signInSecurityKeyEmail(email)
|
||||
if (result.isError) {
|
||||
showNotification({
|
||||
color: 'red',
|
||||
title: 'Error',
|
||||
message: result.error?.message
|
||||
})
|
||||
} else if (result.needsEmailVerification) {
|
||||
setEmailVerificationToggle(true)
|
||||
} else if (result.isSuccess) {
|
||||
navigate('/', { replace: true })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
title="Awaiting email verification"
|
||||
transition="fade"
|
||||
centered
|
||||
transitionDuration={600}
|
||||
opened={emailVerificationToggle}
|
||||
onClose={() => {
|
||||
setEmailVerificationToggle(false)
|
||||
}}
|
||||
>
|
||||
You need to verify your email first. Please check your mailbox and follow the confirmation
|
||||
link to complete the registration.
|
||||
</Modal>
|
||||
<TextInput
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="Email Address"
|
||||
size="lg"
|
||||
autoFocus
|
||||
style={{ marginBottom: '0.5em' }}
|
||||
/>
|
||||
<Button fullWidth onClick={signIn}>
|
||||
Sign in
|
||||
</Button>
|
||||
<AuthLink link="/sign-in/forgot-password" variant="white">
|
||||
Forgot password?
|
||||
</AuthLink>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Divider } from '@mantine/core'
|
||||
|
||||
import AuthLink from '../components/AuthLink'
|
||||
import EmailPasswordlessForm from '../components/SignUpServerlessForm'
|
||||
import EmailPasswordlessForm from '../components/SignUpPasswordlessForm'
|
||||
|
||||
export const EmailPasswordless: React.FC = () => {
|
||||
return (
|
||||
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.10.0
|
||||
image: nhost/hasura-auth:0.11.0
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.4
|
||||
auth:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Docker image versions used in the cloud
|
||||
hasura: v2.10.1
|
||||
auth: 0.10.0
|
||||
auth: 0.11.0
|
||||
storage: 0.2.4
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/apollo
|
||||
|
||||
## 0.5.29
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- @nhost/nhost-js@1.4.12
|
||||
|
||||
## 0.5.28
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/apollo",
|
||||
"version": "0.5.28",
|
||||
"version": "0.5.29",
|
||||
"description": "Nhost Apollo Client library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @nhost/core
|
||||
|
||||
## 0.7.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 9eb78e06: Add `workos` social provider
|
||||
|
||||
## 0.7.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/core",
|
||||
"version": "0.7.6",
|
||||
"version": "0.7.7",
|
||||
"description": "Nhost core client library",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
@@ -45,11 +45,11 @@
|
||||
"scripts": {
|
||||
"dev": "vite build --config ../../config/vite.lib.dev.config.js",
|
||||
"build": "run-p build:lib build:umd",
|
||||
"build:lib": "vite build --config ../../config/vite.lib.config.js",
|
||||
"build:lib": "vite build",
|
||||
"build:umd": "vite build --config ../../config/vite.lib.umd.config.js",
|
||||
"test": "vitest run --config ../../config/vite.lib.config.js",
|
||||
"test:watch": "vitest --config ../../config/vite.lib.config.js",
|
||||
"test:coverage": "vitest run --coverage --config ../../config/vite.lib.config.js",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:coverage": "vitest run",
|
||||
"prettier": "prettier --check src/",
|
||||
"prettier:fix": "prettier --write src/",
|
||||
"lint": "eslint . --ext .ts,.tsx",
|
||||
@@ -58,12 +58,14 @@
|
||||
"verify:fix": "run-p prettier:fix lint:fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@simplewebauthn/browser": "^6.0.0",
|
||||
"axios": "^0.27.2",
|
||||
"js-cookie": "^3.0.1",
|
||||
"xstate": "^4.32.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@simplewebauthn/typescript-types": "^6.0.0",
|
||||
"@types/js-cookie": "^3.0.2",
|
||||
"msw": "^0.39.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
export const NETWORK_ERROR_CODE = 0
|
||||
export const OTHER_ERROR_CODE = 1
|
||||
export const VALIDATION_ERROR_CODE = 10
|
||||
export const STATE_ERROR_CODE = 20
|
||||
|
||||
@@ -8,6 +9,31 @@ export type ErrorPayload = {
|
||||
message: string
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Adds a standard error payload to any JS Error, or convert a standard error payload into a JS Error.
|
||||
* Allows xstate to use `throw` instead of `Promise.reject` to propagate errors.
|
||||
* See https://github.com/statelyai/xstate/issues/3037
|
||||
*/
|
||||
export class CodifiedError extends Error {
|
||||
error: ErrorPayload
|
||||
constructor(original: Error | ErrorPayload) {
|
||||
super(original.message)
|
||||
Error.captureStackTrace(this, this.constructor)
|
||||
if (original instanceof Error) {
|
||||
this.name = original.name
|
||||
this.error = {
|
||||
error: original.name,
|
||||
status: OTHER_ERROR_CODE,
|
||||
message: original.message
|
||||
}
|
||||
} else {
|
||||
this.name = original.error
|
||||
this.error = original
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type ValidationErrorPayload = ErrorPayload & { status: typeof VALIDATION_ERROR_CODE }
|
||||
|
||||
// TODO share with hasura-auth
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios, { AxiosError } from 'axios'
|
||||
|
||||
import { NETWORK_ERROR_CODE } from './errors'
|
||||
import { ErrorPayload, NETWORK_ERROR_CODE } from './errors'
|
||||
|
||||
export const nhostApiClient = (backendUrl: string) => {
|
||||
const client = axios.create({ baseURL: backendUrl })
|
||||
@@ -8,7 +8,7 @@ export const nhostApiClient = (backendUrl: string) => {
|
||||
client.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error: AxiosError<{ message: string; error?: string; statusCode?: number }>) =>
|
||||
Promise.reject({
|
||||
Promise.reject<{ error: ErrorPayload }>({
|
||||
error: {
|
||||
message:
|
||||
error.response?.data?.message ??
|
||||
|
||||
@@ -4,6 +4,7 @@ export type AuthEvents =
|
||||
| { type: 'SESSION_UPDATE'; data: { session: NhostSession } }
|
||||
| { type: 'TRY_TOKEN'; token: string }
|
||||
| { type: 'SIGNIN_ANONYMOUS' }
|
||||
| { type: 'SIGNIN_SECURITY_KEY_EMAIL'; email?: string }
|
||||
| { type: 'SIGNIN_PASSWORD'; email?: string; password?: string }
|
||||
| {
|
||||
type: 'PASSWORDLESS_EMAIL'
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import type { AxiosRequestConfig } from 'axios'
|
||||
import { assign, createMachine, send } from 'xstate'
|
||||
|
||||
import { startAuthentication } from '@simplewebauthn/browser'
|
||||
import type {
|
||||
AuthenticationCredentialJSON,
|
||||
PublicKeyCredentialRequestOptionsJSON
|
||||
} from '@simplewebauthn/typescript-types'
|
||||
|
||||
import {
|
||||
NHOST_JWT_EXPIRES_AT_KEY,
|
||||
NHOST_REFRESH_TOKEN_KEY,
|
||||
@@ -8,6 +14,7 @@ import {
|
||||
TOKEN_REFRESH_MARGIN
|
||||
} from '../constants'
|
||||
import {
|
||||
CodifiedError,
|
||||
INVALID_EMAIL_ERROR,
|
||||
INVALID_MFA_TICKET_ERROR,
|
||||
INVALID_PASSWORD_ERROR,
|
||||
@@ -61,12 +68,12 @@ type AuthServices = {
|
||||
passwordlessEmail: { data: PasswordlessEmailResponse | DeanonymizeResponse }
|
||||
signInAnonymous: { data: SignInAnonymousResponse }
|
||||
signInMfaTotp: { data: SignInMfaTotpResponse }
|
||||
signInSecurityKeyEmail: { data: SignInResponse }
|
||||
refreshToken: { data: NhostSessionResponse }
|
||||
signout: { data: SignOutResponse }
|
||||
signUpEmailPassword: { data: SignUpResponse }
|
||||
importRefreshToken: { data: NhostSessionResponse }
|
||||
}
|
||||
// TODO actions typings
|
||||
|
||||
export const createAuthMachine = ({
|
||||
backendUrl,
|
||||
@@ -158,6 +165,7 @@ export const createAuthMachine = ({
|
||||
on: {
|
||||
SIGNIN_PASSWORD: 'authenticating.password',
|
||||
SIGNIN_ANONYMOUS: 'authenticating.anonymous',
|
||||
SIGNIN_SECURITY_KEY_EMAIL: 'authenticating.securityKeyEmail',
|
||||
SIGNIN_MFA_TOTP: 'authenticating.mfa.totp'
|
||||
}
|
||||
},
|
||||
@@ -225,6 +233,29 @@ export const createAuthMachine = ({
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
securityKeyEmail: {
|
||||
invoke: {
|
||||
src: 'signInSecurityKeyEmail',
|
||||
id: 'authenticateUserWithSecurityKey',
|
||||
onDone: {
|
||||
actions: ['saveSession', 'reportTokenChanged'],
|
||||
target: '#nhost.authentication.signedIn'
|
||||
},
|
||||
onError: [
|
||||
{
|
||||
cond: 'unverified',
|
||||
target: [
|
||||
'#nhost.authentication.signedOut',
|
||||
'#nhost.registration.incomplete.needsEmailVerification'
|
||||
]
|
||||
},
|
||||
{
|
||||
actions: 'saveAuthenticationError',
|
||||
target: '#nhost.authentication.signedOut.failed'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -683,6 +714,22 @@ export const createAuthMachine = ({
|
||||
otp: data.otp
|
||||
})
|
||||
},
|
||||
signInSecurityKeyEmail: async (_, { email }) => {
|
||||
if (!isValidEmail(email)) {
|
||||
throw new CodifiedError(INVALID_EMAIL_ERROR)
|
||||
}
|
||||
const options = await postRequest<PublicKeyCredentialRequestOptionsJSON>(
|
||||
'/signin/webauthn',
|
||||
{ email }
|
||||
)
|
||||
let credential: AuthenticationCredentialJSON
|
||||
try {
|
||||
credential = await startAuthentication(options)
|
||||
} catch (e) {
|
||||
throw new CodifiedError(e as Error)
|
||||
}
|
||||
return postRequest<SignInResponse>('/signin/webauthn/verify', { email, credential })
|
||||
},
|
||||
refreshToken: async (ctx, event) => {
|
||||
const refreshToken = event.type === 'TRY_TOKEN' ? event.token : ctx.refreshToken.value
|
||||
const session = await postRequest<RefreshSessionResponse>('/token', {
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface Typegen0 {
|
||||
| 'done.invoke.authenticateUserWithPassword'
|
||||
| 'done.invoke.authenticateAnonymously'
|
||||
| 'done.invoke.signInMfaTotp'
|
||||
| 'done.invoke.authenticateUserWithSecurityKey'
|
||||
| 'done.invoke.refreshToken'
|
||||
| 'done.invoke.authenticateWithToken'
|
||||
| 'done.invoke.signUpEmailPassword'
|
||||
@@ -20,6 +21,7 @@ export interface Typegen0 {
|
||||
| 'done.invoke.authenticateUserWithPassword'
|
||||
| 'done.invoke.authenticateAnonymously'
|
||||
| 'done.invoke.signInMfaTotp'
|
||||
| 'done.invoke.authenticateUserWithSecurityKey'
|
||||
| 'done.invoke.refreshToken'
|
||||
| 'done.invoke.authenticateWithToken'
|
||||
| 'done.invoke.signUpEmailPassword'
|
||||
@@ -30,6 +32,7 @@ export interface Typegen0 {
|
||||
| 'error.platform.authenticateUserWithPassword'
|
||||
| 'error.platform.authenticateAnonymously'
|
||||
| 'error.platform.signInMfaTotp'
|
||||
| 'error.platform.authenticateUserWithSecurityKey'
|
||||
| 'error.platform.authenticateWithToken'
|
||||
saveMfaTicket: 'done.invoke.authenticateUserWithPassword'
|
||||
saveRefreshAttempt: 'error.platform.refreshToken'
|
||||
@@ -49,6 +52,7 @@ export interface Typegen0 {
|
||||
| 'done.invoke.authenticateUserWithPassword'
|
||||
| 'done.invoke.authenticateAnonymously'
|
||||
| 'done.invoke.signInMfaTotp'
|
||||
| 'done.invoke.authenticateUserWithSecurityKey'
|
||||
| 'done.invoke.authenticateWithToken'
|
||||
| 'done.invoke.signUpEmailPassword'
|
||||
| 'done.invoke.passwordlessSmsOtp'
|
||||
@@ -59,6 +63,7 @@ export interface Typegen0 {
|
||||
reportSignedOut:
|
||||
| 'error.platform.importRefreshToken'
|
||||
| 'error.platform.authenticateUserWithPassword'
|
||||
| 'error.platform.authenticateUserWithSecurityKey'
|
||||
| 'done.invoke.signUpEmailPassword'
|
||||
| 'done.invoke.passwordlessEmail'
|
||||
| 'done.invoke.passwordlessSms'
|
||||
@@ -71,6 +76,7 @@ export interface Typegen0 {
|
||||
| 'done.invoke.authenticateUserWithPassword'
|
||||
| 'done.invoke.authenticateAnonymously'
|
||||
| 'done.invoke.signInMfaTotp'
|
||||
| 'done.invoke.authenticateUserWithSecurityKey'
|
||||
| 'done.invoke.authenticateWithToken'
|
||||
| 'done.invoke.signUpEmailPassword'
|
||||
| 'done.invoke.passwordlessSmsOtp'
|
||||
@@ -81,6 +87,7 @@ export interface Typegen0 {
|
||||
| 'done.invoke.authenticateUserWithPassword'
|
||||
| 'done.invoke.authenticateAnonymously'
|
||||
| 'done.invoke.signInMfaTotp'
|
||||
| 'done.invoke.authenticateUserWithSecurityKey'
|
||||
| 'done.invoke.authenticateWithToken'
|
||||
| 'done.invoke.signUpEmailPassword'
|
||||
| 'done.invoke.passwordlessSmsOtp'
|
||||
@@ -91,6 +98,7 @@ export interface Typegen0 {
|
||||
| 'done.invoke.authenticateUserWithPassword'
|
||||
| 'done.invoke.authenticateAnonymously'
|
||||
| 'done.invoke.signInMfaTotp'
|
||||
| 'done.invoke.authenticateUserWithSecurityKey'
|
||||
| 'done.invoke.authenticateWithToken'
|
||||
| 'done.invoke.signUpEmailPassword'
|
||||
| 'done.invoke.passwordlessSmsOtp'
|
||||
@@ -116,6 +124,11 @@ export interface Typegen0 {
|
||||
data: unknown
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.'
|
||||
}
|
||||
'done.invoke.authenticateUserWithSecurityKey': {
|
||||
type: 'done.invoke.authenticateUserWithSecurityKey'
|
||||
data: unknown
|
||||
__tip: 'See the XState TS docs to learn how to strongly type this.'
|
||||
}
|
||||
'done.invoke.refreshToken': {
|
||||
type: 'done.invoke.refreshToken'
|
||||
data: unknown
|
||||
@@ -151,6 +164,10 @@ export interface Typegen0 {
|
||||
data: unknown
|
||||
}
|
||||
'error.platform.signInMfaTotp': { type: 'error.platform.signInMfaTotp'; data: unknown }
|
||||
'error.platform.authenticateUserWithSecurityKey': {
|
||||
type: 'error.platform.authenticateUserWithSecurityKey'
|
||||
data: unknown
|
||||
}
|
||||
'error.platform.authenticateWithToken': {
|
||||
type: 'error.platform.authenticateWithToken'
|
||||
data: unknown
|
||||
@@ -192,6 +209,7 @@ export interface Typegen0 {
|
||||
signInPassword: 'done.invoke.authenticateUserWithPassword'
|
||||
signInAnonymous: 'done.invoke.authenticateAnonymously'
|
||||
signInMfaTotp: 'done.invoke.signInMfaTotp'
|
||||
signInSecurityKeyEmail: 'done.invoke.authenticateUserWithSecurityKey'
|
||||
refreshToken: 'done.invoke.refreshToken' | 'done.invoke.authenticateWithToken'
|
||||
signUpEmailPassword: 'done.invoke.signUpEmailPassword'
|
||||
passwordlessEmail: 'done.invoke.passwordlessEmail'
|
||||
@@ -208,6 +226,7 @@ export interface Typegen0 {
|
||||
importRefreshToken: 'xstate.init'
|
||||
signInPassword: 'SIGNIN_PASSWORD'
|
||||
signInAnonymous: 'SIGNIN_ANONYMOUS'
|
||||
signInSecurityKeyEmail: 'SIGNIN_SECURITY_KEY_EMAIL'
|
||||
signInMfaTotp: 'SIGNIN_MFA_TOTP'
|
||||
signout: 'SIGNOUT'
|
||||
refreshToken: '' | 'TRY_TOKEN'
|
||||
@@ -220,7 +239,10 @@ export interface Typegen0 {
|
||||
hasSession: 'SESSION_UPDATE' | 'done.invoke.signUpEmailPassword'
|
||||
isSignedIn: '' | 'error.platform.authenticateWithToken'
|
||||
hasMfaTicket: 'done.invoke.authenticateUserWithPassword'
|
||||
unverified: 'error.platform.authenticateUserWithPassword' | 'error.platform.signUpEmailPassword'
|
||||
unverified:
|
||||
| 'error.platform.authenticateUserWithPassword'
|
||||
| 'error.platform.authenticateUserWithSecurityKey'
|
||||
| 'error.platform.signUpEmailPassword'
|
||||
noToken: ''
|
||||
isAutoRefreshDisabled: ''
|
||||
hasRefreshToken: ''
|
||||
@@ -243,6 +265,7 @@ export interface Typegen0 {
|
||||
| 'authentication.authenticating.anonymous'
|
||||
| 'authentication.authenticating.mfa'
|
||||
| 'authentication.authenticating.mfa.totp'
|
||||
| 'authentication.authenticating.securityKeyEmail'
|
||||
| 'authentication.signedIn'
|
||||
| 'authentication.signedIn.refreshTimer'
|
||||
| 'authentication.signedIn.refreshTimer.disabled'
|
||||
@@ -281,7 +304,12 @@ export interface Typegen0 {
|
||||
| 'needsMfa'
|
||||
| 'failed'
|
||||
| 'signingOut'
|
||||
authenticating?: 'password' | 'anonymous' | 'mfa' | { mfa?: 'totp' }
|
||||
authenticating?:
|
||||
| 'password'
|
||||
| 'anonymous'
|
||||
| 'mfa'
|
||||
| 'securityKeyEmail'
|
||||
| { mfa?: 'totp' }
|
||||
signedIn?:
|
||||
| 'refreshTimer'
|
||||
| {
|
||||
|
||||
54
packages/core/src/promises/addSecurityKey.ts
Normal file
54
packages/core/src/promises/addSecurityKey.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { startRegistration } from '@simplewebauthn/browser'
|
||||
import {
|
||||
PublicKeyCredentialCreationOptionsJSON,
|
||||
RegistrationCredentialJSON
|
||||
} from '@simplewebauthn/typescript-types'
|
||||
|
||||
import { AuthClient } from '../client'
|
||||
import { CodifiedError, ErrorPayload } from '../errors'
|
||||
import { nhostApiClient } from '../hasura-auth'
|
||||
import { SecurityKey } from '../types'
|
||||
|
||||
import { ActionErrorState, ActionLoadingState, ActionSuccessState } from './types'
|
||||
export interface AddSecurityKeyHandlerResult extends ActionErrorState, ActionSuccessState {
|
||||
key?: SecurityKey
|
||||
}
|
||||
|
||||
export interface AddSecurityKeyState extends AddSecurityKeyHandlerResult, ActionLoadingState {}
|
||||
|
||||
export const addSecurityKeyPromise = async (
|
||||
{ backendUrl, interpreter }: AuthClient,
|
||||
nickname?: string
|
||||
): Promise<AddSecurityKeyHandlerResult> => {
|
||||
const api = nhostApiClient(backendUrl)
|
||||
try {
|
||||
const { data: options } = await api.post<PublicKeyCredentialCreationOptionsJSON>(
|
||||
'/user/webauthn/add',
|
||||
{},
|
||||
{
|
||||
headers: {
|
||||
authorization: `Bearer ${interpreter?.state.context.accessToken.value}`
|
||||
}
|
||||
}
|
||||
)
|
||||
let credential: RegistrationCredentialJSON
|
||||
try {
|
||||
credential = await startRegistration(options)
|
||||
} catch (e) {
|
||||
throw new CodifiedError(e as Error)
|
||||
}
|
||||
const { data: key } = await api.post<SecurityKey>(
|
||||
'/user/webauthn/verify',
|
||||
{ credential, nickname },
|
||||
{
|
||||
headers: {
|
||||
authorization: `Bearer ${interpreter?.state.context.accessToken.value}`
|
||||
}
|
||||
}
|
||||
)
|
||||
return { key, isError: false, error: null, isSuccess: true }
|
||||
} catch (e) {
|
||||
const { error } = e as { error: ErrorPayload }
|
||||
return { isError: true, error, isSuccess: false }
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './addSecurityKey'
|
||||
export * from './changeEmail'
|
||||
export * from './changePassword'
|
||||
export * from './mfa'
|
||||
@@ -7,6 +8,7 @@ export * from './signInAnonymous'
|
||||
export * from './signInEmailPassword'
|
||||
export * from './signInEmailPasswordless'
|
||||
export * from './signInMfaTotp'
|
||||
export * from './signInSecurityKeyEmail'
|
||||
export * from './signInSmsPasswordless'
|
||||
export * from './signInSmsPasswordlessOtp'
|
||||
export * from './signOut'
|
||||
|
||||
66
packages/core/src/promises/signInSecurityKeyEmail.ts
Normal file
66
packages/core/src/promises/signInSecurityKeyEmail.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { USER_ALREADY_SIGNED_IN } from '../errors'
|
||||
import { AuthInterpreter } from '../types'
|
||||
|
||||
import {
|
||||
ActionLoadingState,
|
||||
NeedsEmailVerificationState,
|
||||
SessionActionHandlerResult
|
||||
} from './types'
|
||||
|
||||
export interface SignInSecurityKeyPasswordlessHandlerResult
|
||||
extends SessionActionHandlerResult,
|
||||
NeedsEmailVerificationState {}
|
||||
|
||||
export interface SignInSecurityKeyPasswordlessState
|
||||
extends SignInSecurityKeyPasswordlessHandlerResult,
|
||||
ActionLoadingState {}
|
||||
|
||||
export const signInSecurityKeyEmailPromise = (interpreter: AuthInterpreter, email: string) =>
|
||||
new Promise<SignInSecurityKeyPasswordlessHandlerResult>((resolve) => {
|
||||
const { changed, context } = interpreter.send({ type: 'SIGNIN_SECURITY_KEY_EMAIL', email })
|
||||
if (!changed) {
|
||||
return resolve({
|
||||
accessToken: context.accessToken.value,
|
||||
error: USER_ALREADY_SIGNED_IN,
|
||||
isError: true,
|
||||
isSuccess: false,
|
||||
needsEmailVerification: false,
|
||||
user: context.user
|
||||
})
|
||||
}
|
||||
interpreter.onTransition((state) => {
|
||||
if (
|
||||
state.matches({
|
||||
authentication: { signedOut: 'noErrors' },
|
||||
registration: { incomplete: 'needsEmailVerification' }
|
||||
})
|
||||
) {
|
||||
resolve({
|
||||
accessToken: null,
|
||||
error: null,
|
||||
isError: false,
|
||||
isSuccess: false,
|
||||
needsEmailVerification: true,
|
||||
user: null
|
||||
})
|
||||
} else if (state.matches({ authentication: { signedOut: 'failed' } })) {
|
||||
resolve({
|
||||
accessToken: null,
|
||||
error: state.context.errors.authentication || null,
|
||||
isError: true,
|
||||
isSuccess: false,
|
||||
needsEmailVerification: false,
|
||||
user: null
|
||||
})
|
||||
} else if (state.matches({ authentication: 'signedIn' })) {
|
||||
resolve({
|
||||
accessToken: state.context.accessToken.value,
|
||||
error: null,
|
||||
isError: false,
|
||||
isSuccess: true,
|
||||
needsEmailVerification: false,
|
||||
user: state.context.user
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -87,7 +87,14 @@ export interface DeanonymizeOptions extends RegistrationOptions {
|
||||
email?: string
|
||||
password?: string
|
||||
}
|
||||
export interface ProviderOptions extends RegistrationOptions, RedirectOption {}
|
||||
|
||||
export interface CommonProviderOptions extends RegistrationOptions, RedirectOption {}
|
||||
export interface WorkOsOptions extends CommonProviderOptions {
|
||||
connection?: string
|
||||
organization?: string
|
||||
provider?: string
|
||||
}
|
||||
export interface ProviderOptions extends CommonProviderOptions, WorkOsOptions {}
|
||||
|
||||
// TODO share with hasura-auth
|
||||
/** User information */
|
||||
@@ -152,6 +159,7 @@ export type Provider =
|
||||
| 'bitbucket'
|
||||
| 'discord'
|
||||
| 'twitch'
|
||||
| 'workos'
|
||||
|
||||
// TODO share with hasura-auth
|
||||
export interface JWTHasuraClaims {
|
||||
@@ -266,3 +274,11 @@ export type PasswordlessSmsOtpResponse = NhostSessionResponse
|
||||
|
||||
/** payload from hasura-auth endpoint /signin/mfa/totp */
|
||||
export type SignInMfaTotpResponse = NhostSessionResponse
|
||||
|
||||
/** Data of a WebAuthn security key */
|
||||
export interface SecurityKey {
|
||||
/** Unique indentifier of the security key */
|
||||
id: string
|
||||
/** Human-readable nickname fof the security key */
|
||||
nickname?: string
|
||||
}
|
||||
|
||||
@@ -11,3 +11,4 @@ export * from './sendVerificationEmailHandlers'
|
||||
export * from './signOutHandlers'
|
||||
export * from './signUpHandlers'
|
||||
export * from './anonymousHandlers'
|
||||
export * from './securityKeySignInHandlers'
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import faker from '@faker-js/faker'
|
||||
import { PublicKeyCredentialRequestOptionsJSON } from '@simplewebauthn/typescript-types'
|
||||
import { rest } from 'msw'
|
||||
import { Mfa, NhostSession } from '../../../src/types'
|
||||
import { BASE_URL } from '../config'
|
||||
import fakeUser from '../mocks/user'
|
||||
|
||||
/**
|
||||
* Request handler for MSW to mock a successful sign in request when using the email and password
|
||||
* sign in method.
|
||||
*/
|
||||
export const correctEmailSecurityKeyHandler = rest.post(
|
||||
`${BASE_URL}/signin/webauthn`,
|
||||
(_req, res, ctx) => {
|
||||
return res(
|
||||
ctx.json<PublicKeyCredentialRequestOptionsJSON>({
|
||||
challenge: faker.datatype.string(30)
|
||||
})
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Request handler for MSW to mock a successful sign in verification when using the email and password
|
||||
* sign in method.
|
||||
*/
|
||||
export const correctSecurityKeyVerifyHandler = rest.post(
|
||||
`${BASE_URL}/signin/webauthn/verify`,
|
||||
(_req, res, ctx) => {
|
||||
return res(
|
||||
ctx.json<{ mfa: Mfa | null; session: NhostSession | null }>({
|
||||
session: {
|
||||
user: fakeUser,
|
||||
accessTokenExpiresIn: 900,
|
||||
accessToken: faker.datatype.string(40),
|
||||
refreshToken: faker.datatype.uuid()
|
||||
},
|
||||
mfa: null
|
||||
})
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Request handler for MSW to mock an incorrect sign in verification when using the email and password
|
||||
* sign in method.
|
||||
*/
|
||||
export const incorrectSecurityKeyVerifyHandler = rest.post(
|
||||
`${BASE_URL}/signin/webauthn/verify`,
|
||||
(_req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(401),
|
||||
ctx.json({
|
||||
status: 401,
|
||||
error: 'invalid-webauthn-authenticator',
|
||||
message: 'Invalid WebAuthn authenticator'
|
||||
})
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Request handler for MSW to mock a network error when trying to sign in using the email + security key
|
||||
* sign in method.
|
||||
*/
|
||||
export const emailSecurityKeyNetworkErrorHandler = rest.post(
|
||||
`${BASE_URL}/signin/webauthn`,
|
||||
(_req, res) => {
|
||||
return res.networkError('Network error')
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Request handler for MSW to mock an internal server error when trying to sign in using the email + security key
|
||||
* sign in method.
|
||||
*/
|
||||
export const emailSecurityKeyInternalErrorHandler = rest.post(
|
||||
`${BASE_URL}/signin/webauthn`,
|
||||
(_req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(500),
|
||||
ctx.json({ status: 500, error: 'internal-error', message: 'Internal error' })
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Request handler for MSW to mock an unsuccessful sign (user not found) in request using the security key + email
|
||||
* sign in method.
|
||||
*/
|
||||
export const userNotFoundSecurityKeyHandler = rest.post(
|
||||
`${BASE_URL}/signin/webauthn`,
|
||||
(_req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(400),
|
||||
ctx.json({
|
||||
status: 400,
|
||||
error: 'user-not-found',
|
||||
message: 'No user found'
|
||||
})
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Request handler for MSW to mock an unsuccessful sign in request using the security key + email
|
||||
* sign in method. Useful if you'd like to mock a scenario where the user provided an
|
||||
* signed in with an unverified account.
|
||||
*/
|
||||
export const unverifiedEmailSecurityKeyErrorHandler = rest.post(
|
||||
`${BASE_URL}/signin/webauthn`,
|
||||
(_req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(401),
|
||||
ctx.json({
|
||||
status: 401,
|
||||
error: 'unverified-email',
|
||||
message: 'Email needs verification'
|
||||
})
|
||||
)
|
||||
}
|
||||
)
|
||||
213
packages/core/tests/securityKeyEmailSignIn.test.ts
Normal file
213
packages/core/tests/securityKeyEmailSignIn.test.ts
Normal file
@@ -0,0 +1,213 @@
|
||||
import faker from '@faker-js/faker'
|
||||
import { interpret } from 'xstate'
|
||||
import { afterAll, afterEach, beforeAll, beforeEach, test, vi } from 'vitest'
|
||||
import { AuthenticationCredentialJSON } from '@simplewebauthn/typescript-types'
|
||||
import { waitFor } from 'xstate/lib/waitFor'
|
||||
|
||||
import { createAuthMachine } from '../src/machines'
|
||||
|
||||
import { BASE_URL } from './helpers/config'
|
||||
import server from './helpers/server'
|
||||
import CustomClientStorage from './helpers/storage'
|
||||
import {
|
||||
emailSecurityKeyNetworkErrorHandler,
|
||||
unverifiedEmailSecurityKeyErrorHandler,
|
||||
userNotFoundSecurityKeyHandler,
|
||||
authTokenNetworkErrorHandler,
|
||||
correctEmailSecurityKeyHandler,
|
||||
correctSecurityKeyVerifyHandler,
|
||||
incorrectSecurityKeyVerifyHandler
|
||||
} from './helpers/handlers'
|
||||
import fakeUser from './helpers/mocks/user'
|
||||
|
||||
const customStorage = new CustomClientStorage(new Map())
|
||||
|
||||
const authMachine = createAuthMachine({
|
||||
backendUrl: BASE_URL,
|
||||
clientUrl: 'http://localhost:3000',
|
||||
clientStorage: customStorage,
|
||||
clientStorageType: 'custom',
|
||||
refreshIntervalTime: 1
|
||||
})
|
||||
|
||||
const mockAuthenticator = () => {
|
||||
vi.mock('@simplewebauthn/browser', () => {
|
||||
return {
|
||||
default: {},
|
||||
startAuthentication: vi.fn(async (): Promise<AuthenticationCredentialJSON> => {
|
||||
return {
|
||||
id: faker.datatype.uuid(),
|
||||
rawId: faker.datatype.uuid(),
|
||||
response: {
|
||||
authenticatorData: faker.datatype.string(30),
|
||||
clientDataJSON: faker.datatype.string(30),
|
||||
signature: faker.datatype.string(30)
|
||||
},
|
||||
type: 'public-key',
|
||||
clientExtensionResults: {}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
const authService = interpret(authMachine)
|
||||
|
||||
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }))
|
||||
afterAll(() => server.close())
|
||||
|
||||
beforeEach(() => {
|
||||
authService.start()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
authService.stop()
|
||||
customStorage.clear()
|
||||
server.resetHandlers()
|
||||
})
|
||||
|
||||
test(`should fail if network is unavailable`, async () => {
|
||||
server.use(emailSecurityKeyNetworkErrorHandler, authTokenNetworkErrorHandler)
|
||||
|
||||
authService.send({
|
||||
type: 'SIGNIN_SECURITY_KEY_EMAIL',
|
||||
email: faker.internet.email()
|
||||
})
|
||||
|
||||
const state = await waitFor(authService, (s) => s.matches('authentication.signedOut.failed'))
|
||||
|
||||
expect(state.context.errors).toMatchInlineSnapshot(`
|
||||
{
|
||||
"authentication": {
|
||||
"error": "OK",
|
||||
"message": "Network Error",
|
||||
"status": 200,
|
||||
},
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
test(`should fail if server returns an error`, async () => {
|
||||
server.use(emailSecurityKeyNetworkErrorHandler, authTokenNetworkErrorHandler)
|
||||
|
||||
authService.send({
|
||||
type: 'SIGNIN_SECURITY_KEY_EMAIL',
|
||||
email: faker.internet.email()
|
||||
})
|
||||
|
||||
const state = await waitFor(authService, (s) => s.matches('authentication.signedOut.failed'))
|
||||
|
||||
expect(state.context.errors).toMatchInlineSnapshot(`
|
||||
{
|
||||
"authentication": {
|
||||
"error": "OK",
|
||||
"message": "Network Error",
|
||||
"status": 200,
|
||||
},
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
test(`should fail if email is incorrectly formatted`, async () => {
|
||||
authService.send({
|
||||
type: 'SIGNIN_SECURITY_KEY_EMAIL',
|
||||
email: faker.internet.userName()
|
||||
})
|
||||
|
||||
const emailErrorSignInState = await waitFor(authService, (s) =>
|
||||
s.matches('authentication.signedOut.failed')
|
||||
)
|
||||
expect(emailErrorSignInState.context.errors).toMatchInlineSnapshot(`
|
||||
{
|
||||
"authentication": {
|
||||
"error": "invalid-email",
|
||||
"message": "Email is incorrectly formatted",
|
||||
"status": 10,
|
||||
},
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
test(`should fail if server does not find the user`, async () => {
|
||||
server.use(userNotFoundSecurityKeyHandler)
|
||||
|
||||
authService.send({
|
||||
type: 'SIGNIN_SECURITY_KEY_EMAIL',
|
||||
email: faker.internet.email()
|
||||
})
|
||||
|
||||
const state = await waitFor(authService, (state) =>
|
||||
state.matches('authentication.signedOut.failed')
|
||||
)
|
||||
|
||||
expect(state.context.errors).toMatchInlineSnapshot(`
|
||||
{
|
||||
"authentication": {
|
||||
"error": "user-not-found",
|
||||
"message": "No user found",
|
||||
"status": 400,
|
||||
},
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
test(`should fail if user email needs verification`, async () => {
|
||||
server.use(unverifiedEmailSecurityKeyErrorHandler)
|
||||
|
||||
authService.send({
|
||||
type: 'SIGNIN_SECURITY_KEY_EMAIL',
|
||||
email: faker.internet.email()
|
||||
})
|
||||
|
||||
const state = await waitFor(authService, (state) =>
|
||||
state.matches('authentication.signedOut.failed')
|
||||
)
|
||||
|
||||
expect(state.context.errors).toMatchInlineSnapshot(`
|
||||
{
|
||||
"authentication": {
|
||||
"error": "unverified-email",
|
||||
"message": "Email needs verification",
|
||||
"status": 401,
|
||||
},
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
test(`should fail if the user secret key is incorrect`, async () => {
|
||||
server.use(correctEmailSecurityKeyHandler, incorrectSecurityKeyVerifyHandler)
|
||||
mockAuthenticator()
|
||||
authService.send({
|
||||
type: 'SIGNIN_SECURITY_KEY_EMAIL',
|
||||
email: faker.internet.email()
|
||||
})
|
||||
|
||||
const state = await waitFor(authService, (state) =>
|
||||
state.matches('authentication.signedOut.failed')
|
||||
)
|
||||
|
||||
expect(state.context.errors).toMatchInlineSnapshot(`
|
||||
{
|
||||
"authentication": {
|
||||
"error": "invalid-webauthn-authenticator",
|
||||
"message": "Invalid WebAuthn authenticator",
|
||||
"status": 401,
|
||||
},
|
||||
}
|
||||
`)
|
||||
})
|
||||
|
||||
test(`should succeed if correct credentials are provided`, async () => {
|
||||
server.use(correctEmailSecurityKeyHandler, correctSecurityKeyVerifyHandler)
|
||||
mockAuthenticator()
|
||||
|
||||
authService.send({
|
||||
type: 'SIGNIN_SECURITY_KEY_EMAIL',
|
||||
email: faker.internet.email()
|
||||
})
|
||||
|
||||
const state = await waitFor(authService, (state) =>
|
||||
state.matches({ authentication: { signedIn: { refreshTimer: { running: 'pending' } } } })
|
||||
)
|
||||
|
||||
expect(state.context.user).not.toBeNull()
|
||||
})
|
||||
15
packages/core/vite.config.js
Normal file
15
packages/core/vite.config.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
import baseConfig from '../../config/vite.lib.config'
|
||||
|
||||
export default defineConfig({
|
||||
...baseConfig,
|
||||
build: {
|
||||
...baseConfig.build,
|
||||
rollupOptions: {
|
||||
...baseConfig.build.rollupOptions,
|
||||
external: (id) =>
|
||||
id !== '@simplewebauthn/browser' && baseConfig.build.rollupOptions.external(id)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,5 +1,12 @@
|
||||
# @nhost/hasura-auth-js
|
||||
|
||||
## 1.4.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [9eb78e06]
|
||||
- @nhost/core@0.7.7
|
||||
|
||||
## 1.4.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -5,7 +5,7 @@ services:
|
||||
environment:
|
||||
hasura_graphql_enable_remote_schema_permissions: false
|
||||
auth:
|
||||
image: nhost/hasura-auth:0.10.0
|
||||
image: nhost/hasura-auth:0.11.0
|
||||
storage:
|
||||
image: nhost/hasura-storage:0.2.4
|
||||
auth:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@nhost/hasura-auth-js",
|
||||
"version": "1.4.2",
|
||||
"version": "1.4.3",
|
||||
"description": "Hasura-auth client",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
|
||||
@@ -2,6 +2,7 @@ import jwt_decode from 'jwt-decode'
|
||||
import { interpret } from 'xstate'
|
||||
|
||||
import {
|
||||
addSecurityKeyPromise,
|
||||
AuthClient,
|
||||
AuthInterpreter,
|
||||
changeEmailPromise,
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
DeanonymizeResponse,
|
||||
EMAIL_NEEDS_VERIFICATION,
|
||||
encodeQueryParameters,
|
||||
ErrorPayload,
|
||||
INVALID_REFRESH_TOKEN,
|
||||
JWTClaims,
|
||||
JWTHasuraClaims,
|
||||
@@ -23,6 +25,7 @@ import {
|
||||
resetPasswordPromise,
|
||||
ResetPasswordResponse,
|
||||
rewriteRedirectTo,
|
||||
SecurityKey,
|
||||
sendVerificationEmailPromise,
|
||||
SendVerificationEmailResponse,
|
||||
signInAnonymousPromise,
|
||||
@@ -30,6 +33,7 @@ import {
|
||||
signInEmailPasswordPromise,
|
||||
signInMfaTotpPromise,
|
||||
SignInResponse,
|
||||
signInSecurityKeyEmailPromise,
|
||||
signInSmsPasswordlessOtpPromise,
|
||||
signInSmsPasswordlessPromise,
|
||||
signOutPromise,
|
||||
@@ -176,6 +180,14 @@ export class HasuraAuthClient {
|
||||
return { ...getAuthenticationResult(res), mfa: null }
|
||||
}
|
||||
|
||||
if ('email' in params && 'securityKey' in params) {
|
||||
if (params.securityKey !== true) {
|
||||
throw Error('securityKey must be true')
|
||||
}
|
||||
const res = await signInSecurityKeyEmailPromise(interpreter, params.email)
|
||||
return { ...getAuthenticationResult(res), mfa: null }
|
||||
}
|
||||
|
||||
// * Passwordless Email (magic link)
|
||||
if ('email' in params) {
|
||||
const { email, options } = params
|
||||
@@ -348,6 +360,19 @@ export class HasuraAuthClient {
|
||||
throw Error(`Unknown deanonymization method`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Use `nhost.auth.addSecurityKey to add a security key to the user, using the WebAuthn API.
|
||||
* @param nickname optional human-readable nickname for the security key
|
||||
*
|
||||
* @docs https://docs.nhost.io/reference/javascript/auth/add-security-key
|
||||
*/
|
||||
async addSecurityKey(
|
||||
nickname?: string
|
||||
): Promise<{ error: ErrorPayload | null; key?: SecurityKey }> {
|
||||
const { error, key } = await addSecurityKeyPromise(this._client, nickname)
|
||||
return { error, key }
|
||||
}
|
||||
|
||||
/**
|
||||
* Use `nhost.auth.onTokenChanged` to add a custom function that runs every time the access or refresh token is changed.
|
||||
*
|
||||
|
||||
@@ -34,7 +34,8 @@ export const getAuthenticationResult = ({
|
||||
}
|
||||
if (user && accessToken) {
|
||||
return {
|
||||
session: { accessToken, accessTokenExpiresIn: 0, refreshToken: 'TODO', user },
|
||||
// TODO either return the refresh token or remove it from the session type
|
||||
session: { accessToken, accessTokenExpiresIn: 0, refreshToken: '', user },
|
||||
error: null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import {
|
||||
AuthClient,
|
||||
AuthOptions,
|
||||
CommonProviderOptions,
|
||||
NhostSession,
|
||||
PasswordlessOptions,
|
||||
Provider,
|
||||
ProviderOptions,
|
||||
RedirectOption,
|
||||
SignUpOptions,
|
||||
StorageGetter,
|
||||
StorageSetter,
|
||||
User
|
||||
User,
|
||||
WorkOsOptions
|
||||
} from '@nhost/core'
|
||||
export type { AuthClient, Provider, StorageGetter, StorageSetter, User }
|
||||
export interface NhostAuthConstructorParams extends AuthOptions {
|
||||
@@ -42,6 +43,11 @@ export interface SignInPasswordlessEmailParams {
|
||||
options?: PasswordlessOptions
|
||||
}
|
||||
|
||||
export interface SignInPasswordlessSecurityKeyParams {
|
||||
email: string
|
||||
securityKey: true
|
||||
}
|
||||
|
||||
export interface SignInPasswordlessSmsParams {
|
||||
phoneNumber: string
|
||||
options?: PasswordlessOptions
|
||||
@@ -51,15 +57,16 @@ export interface SignInPasswordlessSmsOtpParams {
|
||||
phoneNumber: string
|
||||
otp: string
|
||||
}
|
||||
export interface SignInWithProviderOptions {
|
||||
provider: Provider
|
||||
options?: ProviderOptions
|
||||
}
|
||||
|
||||
export type SignInWithProviderOptions =
|
||||
| { provider: Exclude<Provider, 'workos'>; options?: CommonProviderOptions }
|
||||
| { provider: 'workos'; options?: WorkOsOptions }
|
||||
|
||||
export type SignInParams =
|
||||
| SignInEmailPasswordParams
|
||||
| SignInEmailPasswordOtpParams
|
||||
| SignInPasswordlessEmailParams
|
||||
| SignInPasswordlessSecurityKeyParams
|
||||
| SignInPasswordlessSmsOtpParams
|
||||
| SignInPasswordlessSmsParams
|
||||
| SignInWithProviderOptions
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user