From c38e006de31e3787c7f0ac5ba0dff4dc8c088cf8 Mon Sep 17 00:00:00 2001 From: Gerrit Alex Date: Thu, 16 Jan 2020 00:50:11 +0100 Subject: [PATCH 1/7] fix(TokenParam): fixed type --- src/token.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/token.ts b/src/token.ts index 79212f3..cee9409 100644 --- a/src/token.ts +++ b/src/token.ts @@ -1,8 +1,8 @@ export type TokenParam = { token: string | undefined; type: 'invite' | 'recovery' | 'email_change' | undefined; - error?: 'access_denied'; - status?: 403; + error: 'access_denied' | undefined; + status: 403 | undefined; }; export const defaultParam: TokenParam = { From da99a22b39e2b0e93a2ef6e310e63dc4e9b6bbfa Mon Sep 17 00:00:00 2001 From: Gerrit Alex Date: Thu, 16 Jan 2020 00:50:51 +0100 Subject: [PATCH 2/7] fix(recoverAccount): remove unecessary type --- src/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.tsx b/src/index.tsx index 95e08ad..983dff2 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -218,7 +218,7 @@ export function useNetlifyIdentity( ); const recoverAccount = useCallback( - (remember?: boolean | undefined) => { + (remember?: boolean) => { if (!param.token || param.type !== 'recovery') { throw new Error(errors.tokenMissingOrInvalid); } From 1f5f6b1dff088dd9015581542de4be85243e94f3 Mon Sep 17 00:00:00 2001 From: Gerrit Alex Date: Thu, 16 Jan 2020 00:58:55 +0100 Subject: [PATCH 3/7] fix(_setUser): export custom setter instead of useState to support manual onAuthChange --- src/index.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 983dff2..c852d15 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,11 +4,9 @@ import React, { useEffect, createContext, useContext, + useCallback, // types - Dispatch, - SetStateAction, ReactNode, - useCallback, } from 'react'; import GoTrue, { @@ -48,7 +46,7 @@ type MaybeUserPromise = Promise; export type ReactNetlifyIdentityAPI = { user: User | undefined; /** not meant for normal use! you should mostly use one of the other exported methods to update the user instance */ - setUser: Dispatch>; + setUser: (_user: GoTrueUser | undefined) => GoTrueUser | undefined; isConfirmedUser: boolean; isLoggedIn: boolean; signupUser: ( @@ -294,7 +292,7 @@ export function useNetlifyIdentity( return { user, /** not meant for normal use! you should mostly use one of the other exported methods to update the user instance */ - setUser, + setUser: _setUser, isConfirmedUser: !!(user && user.confirmed_at), isLoggedIn: !!user, signupUser, From 226aea39809ba08ff282acf74ece24c518e8b2a4 Mon Sep 17 00:00:00 2001 From: Gerrit Alex Date: Thu, 16 Jan 2020 01:11:03 +0100 Subject: [PATCH 4/7] fix(recoverAccount): also clean up potentially rejected token --- src/index.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index c852d15..529403e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -221,11 +221,15 @@ export function useNetlifyIdentity( throw new Error(errors.tokenMissingOrInvalid); } - return goTrueInstance.recover(param.token, remember).then(user => { - // clean up consumed token - setParam(defaultParam); - return _setUser(user); - }); + return goTrueInstance + .recover(param.token, remember) + .then(user => { + return _setUser(user); + }) + .finally(() => { + // clean up consumed token + setParam(defaultParam); + }); }, [goTrueInstance, _setUser, param] ); From abdcdada960849d7059affcfeaee35c9b8036d0f Mon Sep 17 00:00:00 2001 From: Gerrit Alex Date: Thu, 16 Jan 2020 01:23:31 +0100 Subject: [PATCH 5/7] fix(readme): update to latest changes --- README.md | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index f716cf7..6884adc 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,12 @@ Three demos: - [example with Reach Router](https://github.com/sw-yx/react-netlify-identity/tree/master/examples/example-reach-router) with [a demo hosted here](https://react-netlify-identity.netlify.com) and [deployed logs here](https://app.netlify.com/sites/react-netlify-identity/deploys) - [example with React Router](https://github.com/sw-yx/react-netlify-identity/tree/master/examples/example-react-router) with [a demo hosted here](https://react-netlify-identity-example.netlify.com) - **This library is not officially maintained by Netlify.** This is written by swyx for his own use (and others with like minds 😎) and will be maintained as a personal project unless formally adopted by Netlify. See below for official alternatives. ## Blogposts - [Add Netlify Identity Authentication to any React App in 5 minutes with React Context, Hooks and Suspense](https://dev.to/swyx/add-netlify-identity-authentication-to-any-react-app-in-5-minutes-with-react-context-hooks-and-suspense-5gci) - ## List of Alternatives **Lowest level JS Library**: If you want to use the official Javascript bindings to GoTrue, Netlify's underlying Identity service written in Go, use https://github.com/netlify/gotrue-js @@ -51,13 +49,19 @@ yarn add react-netlify-identity - `isConfirmedUser: boolean`: if they have confirmed their email - `isLoggedIn: boolean`: if the user is logged in - `signupUser(email: string, password: string, data: Object)` -- `loginUser(email: string, password: string, remember: Boolean)` - we default the `remember` term to `true` since you'll usually want to remember the session in localStorage. set it to false if you need to +- `loginUser(email: string, password: string, remember: boolean = true)` - we default the `remember` term to `true` since you'll usually want to remember the session in localStorage. set it to false if you need to - `logoutUser()` - `requestPasswordRecovery(email: string)` -- `recoverAccount(token: string, remember?: boolean | undefined)` - `updateUser(fields: { data: object })` - `getFreshJWT()` -- `authedFetch(endpoint: string, obj = {})` (a thin axios-like wrapper over `fetch` that has the user's JWT attached, for convenience pinging Netlify Functions with Netlify Identity) +- `authedFetch(endpoint: string, obj: RequestInit = {})` a thin axios-like wrapper over `fetch` that has the user's JWT attached, for convenience pinging Netlify Functions with Netlify Identity +- `param: TokenParam` + - a token exposing Netlify tokens a dev has to implement the actions for; namely `invite`, `recovery`, `email_change` and `access_denied` + - for further reference, please check the [type definition](https://github.com/sw-yx/react-netlify-identity/tree/master/src/token.ts) + - implementation for a Recovery Example below + - **Important:** tokens this package exposes no methods for are automatically handled and will not be passed down - see [runRoutes implementation](https://github.com/sw-yx/react-netlify-identity/master/src/runRoutes.tsx) + - if you don't want this behaviour (added [here](https://github.com/sw-yx/react-netlify-identity/issues/12) in v.0.1.8), pass `runRoutes={false}` to the exposed hook +- `recoverAccount(remember?: boolean)`: ```tsx import React from 'react'; @@ -89,9 +93,11 @@ function Login() { const { loginUser, signupUser } = useIdentityContext(); const formRef = React.useRef(); const [msg, setMsg] = React.useState(''); + const signup = () => { const email = formRef.current.email.value; const password = formRef.current.password.value; + signupUser(email, password) .then(user => { console.log('Success! Signed up', user); @@ -99,6 +105,7 @@ function Login() { }) .catch(err => console.error(err) || setMsg('Error: ' + err.message)); }; + return (
+
+ +

+ How to handle a Recovery Action (since v0.2) +

+
+ +Of course you can alternatively inline this logic into app. + +```tsx +import { useIdentityContext } from 'react-netlify-identity'; +import { + BrowserRouter as Router, + Switch, + Route, + useLocation, + useHistory, +} from 'react-router-dom'; + +export default function App() { + const { isLoggedIn } = useIdentityContext(); + + return ( + + + + {isLoggedIn ? ( + <> + + } /> + + ) : ( + <> + + + + {/* etc */} + + } /> + + )} + + + ); +} + +function CatchNetlifyRecoveryNullComponent() { + const { + param: { token, type }, + } = useIdentityContext(); + const { replace } = useHistory(); + const { pathname } = useLocation(); + + // important to check for the current pathname here because else you land + // in a infinite loop + if (token && type === 'recovery' && pathname === '/') { + replace(`/recovery`, { token }); + } + + return null; +} + +function RecoveyPage() { + const { + location: { state }, + } = useHistory(); + // this state _might_ not be needed, it was needed in my specific implementation + const [token] = useState(state?.token); + + return null; // do something with the token +} +``` + +
+ ## Lower level API: `useNetlifyIdentity` If you'd like to handle your own context yourself, you can use this library as a hook as well: @@ -201,8 +283,6 @@ function useNetlifyIdentity( ): ReactNetlifyIdentityAPI; ``` -the library watches for and handles confirmation routes by default. If you don't like this, pass `enableRunRoutes: false`. This was added here https://github.com/sw-yx/react-netlify-identity/issues/12 in v0.1.8 - ## License MIT © [sw-yx](https://github.com/sw-yx) From d71eac9ee43e5e9ccbaba0b82eab882a0986f24e Mon Sep 17 00:00:00 2001 From: Gerrit Alex Date: Thu, 16 Jan 2020 01:29:35 +0100 Subject: [PATCH 6/7] chore(version): bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 643a4cf..11662f6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-netlify-identity", - "version": "0.2.0", + "version": "0.2.1", "description": "use netlify identity with react", "author": "sw-yx", "license": "MIT", From 8e305934d6a3801cd556ea1edd6b0fc1f7316e13 Mon Sep 17 00:00:00 2001 From: Gerrit Alex Date: Thu, 16 Jan 2020 01:51:36 +0100 Subject: [PATCH 7/7] fix(readme): finish docs --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6884adc..d5f3feb 100644 --- a/README.md +++ b/README.md @@ -57,11 +57,11 @@ yarn add react-netlify-identity - `authedFetch(endpoint: string, obj: RequestInit = {})` a thin axios-like wrapper over `fetch` that has the user's JWT attached, for convenience pinging Netlify Functions with Netlify Identity - `param: TokenParam` - a token exposing Netlify tokens a dev has to implement the actions for; namely `invite`, `recovery`, `email_change` and `access_denied` - - for further reference, please check the [type definition](https://github.com/sw-yx/react-netlify-identity/tree/master/src/token.ts) - - implementation for a Recovery Example below - **Important:** tokens this package exposes no methods for are automatically handled and will not be passed down - see [runRoutes implementation](https://github.com/sw-yx/react-netlify-identity/master/src/runRoutes.tsx) - if you don't want this behaviour (added [here](https://github.com/sw-yx/react-netlify-identity/issues/12) in v.0.1.8), pass `runRoutes={false}` to the exposed hook -- `recoverAccount(remember?: boolean)`: + - for further reference, please check the [type definition](https://github.com/sw-yx/react-netlify-identity/tree/master/src/token.ts) + - an example implementation for a Recovery process can be found below +- `recoverAccount(remember?: boolean)`: verifies and consumes the recovery token caught by `runRoutes`, sets user on success ```tsx import React from 'react'; @@ -258,14 +258,14 @@ function CatchNetlifyRecoveryNullComponent() { return null; } -function RecoveyPage() { +function RecoveryPage() { const { location: { state }, } = useHistory(); // this state _might_ not be needed, it was needed in my specific implementation const [token] = useState(state?.token); - return null; // do something with the token + return null; // set new password in a form and call updateUser } ```