Skip to content

Merge pull request #32 from sw-yx/v0.2.2 #32

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,16 @@ yarn add react-netlify-identity
- `updateUser(fields: object)`: see [updateUser @ gotrue-js](https://github.com/netlify/gotrue-js#update-a-user)
- `getFreshJWT()`
- `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
- `recoverAccount(remember?: boolean)`: verifies and consumes the recovery token caught by `runRoutes`, sets user on success
- `param: TokenParam`
- a token exposing Netlify tokens a dev has to implement the actions for; namely `invite`, `recovery`, `email_change` and `access_denied`
- **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)
- **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
- 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
- `verifyToken()`
- consumes & verifies TokenParam based on the type and tries to retrieve a valid user object
- devs duty to show password field to afterwards call `signupUser(user.email, newPassword)`

```tsx
import React from 'react';
Expand Down
67 changes: 46 additions & 21 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ const errors = {
tokenMissingOrInvalid: 'either no token found or invalid for this purpose',
};

type MaybeUserPromise = Promise<User | undefined>;

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 */
Expand All @@ -54,29 +52,33 @@ export type ReactNetlifyIdentityAPI = {
password: string,
data: Object,
directLogin: boolean
) => MaybeUserPromise;
) => Promise<User | undefined>;
loginUser: (
email: string,
password: string,
remember?: boolean
) => MaybeUserPromise;
logoutUser: () => MaybeUserPromise;
) => Promise<User | undefined>;
logoutUser: () => Promise<User | undefined>;
requestPasswordRecovery: (email: string) => Promise<void>;
recoverAccount: (remember?: boolean) => MaybeUserPromise;
updateUser: (fields: object) => MaybeUserPromise;
getFreshJWT: () => Promise<string>;
recoverAccount: (remember?: boolean) => Promise<User | undefined>;
updateUser: (fields: object) => Promise<User | undefined>;
getFreshJWT: () => Promise<string> | undefined;
authedFetch: {
get: (endpoint: string, obj?: {}) => Promise<any>;
post: (endpoint: string, obj?: {}) => Promise<any>;
put: (endpoint: string, obj?: {}) => Promise<any>;
delete: (endpoint: string, obj?: {}) => Promise<any>;
get: (endpoint: string, obj?: RequestInit) => Promise<any>;
post: (endpoint: string, obj?: RequestInit) => Promise<any>;
put: (endpoint: string, obj?: RequestInit) => Promise<any>;
delete: (endpoint: string, obj?: RequestInit) => Promise<any>;
};
_goTrueInstance: GoTrue;
_url: string;
loginProvider: (provider: Provider) => void;
acceptInviteExternalUrl: (provider: Provider) => string;
acceptInviteExternalUrl: (
provider: Provider,
autoRedirect: boolean
) => string | undefined;
settings: Settings;
param: TokenParam;
verifyToken: () => Promise<User | undefined>;
};

const [_useIdentityContext, _IdentityCtxProvider] = createCtx<
Expand Down Expand Up @@ -179,20 +181,42 @@ export function useNetlifyIdentity(
* @see https://github.com/netlify/gotrue-js/blob/master/src/index.js#L92
*/
const acceptInviteExternalUrl = useCallback(
(provider: Provider) => {
(provider: Provider, autoRedirect: boolean = true) => {
if (!param.token || param.type !== 'invite') {
throw new Error(errors.tokenMissingOrInvalid);
console.error(errors.tokenMissingOrInvalid);
return;
}

const url = goTrueInstance.acceptInviteExternalUrl(provider, param.token);
// clean up consumed token
setParam(defaultParam);

if (autoRedirect) {
window.location.href = url;
return;
}

return url;
},
[goTrueInstance, param]
);

/**
* @see https://github.com/netlify/gotrue-js/blob/master/src/index.js#L123
*/
const verifyToken = useCallback(() => {
if (!param.type || !param.token) {
return Promise.reject(errors.tokenMissingOrInvalid);
}

return goTrueInstance.verify(param.type, param.token).then(user => {
// cleanup consumed token
setParam(defaultParam);

return user;
});
}, [goTrueInstance, param]);

/******* email auth */
/**
* @see https://github.com/netlify/gotrue-js/blob/master/src/index.js#L50
Expand Down Expand Up @@ -237,7 +261,7 @@ export function useNetlifyIdentity(
const recoverAccount = useCallback(
(remember?: boolean) => {
if (!param.token || param.type !== 'recovery') {
throw new Error(errors.tokenMissingOrInvalid);
return Promise.reject(errors.tokenMissingOrInvalid);
}

return goTrueInstance
Expand All @@ -259,7 +283,7 @@ export function useNetlifyIdentity(
const updateUser = useCallback(
(fields: object) => {
if (!user) {
throw new Error(errors.noUserFound);
return Promise.reject(errors.noUserFound);
}

return user!
Expand All @@ -274,7 +298,7 @@ export function useNetlifyIdentity(
*/
const getFreshJWT = useCallback(() => {
if (!user) {
throw new Error(errors.noUserFound);
return Promise.reject(errors.noUserFound);
}

return user.jwt();
Expand All @@ -285,18 +309,18 @@ export function useNetlifyIdentity(
*/
const logoutUser = useCallback(() => {
if (!user) {
throw new Error(errors.noUserFound);
return Promise.reject(errors.noUserFound);
}

return user.logout().then(() => _setUser(undefined));
}, [user]);

const genericAuthedFetch = (method: string) => (
const genericAuthedFetch = (method: RequestInit['method']) => (
endpoint: string,
options: RequestInit = {}
) => {
if (!user?.token?.access_token) {
throw new Error(errors.noUserTokenFound);
return Promise.reject(errors.noUserTokenFound);
}

const defaultObj = {
Expand Down Expand Up @@ -341,6 +365,7 @@ export function useNetlifyIdentity(
acceptInviteExternalUrl,
settings,
param,
verifyToken,
};
}

Expand Down
12 changes: 9 additions & 3 deletions src/runRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,15 @@ export function runRoutes(

const hash = document.location.hash.replace(hashReplace, '');

// todo: maybe replace with history.replaceState to completely clear the url?
// currently keeps #
document.location.hash = '';
try {
history.pushState(
'',
document.title,
window.location.pathname + window.location.search
);
} catch (_) {
window.location.href.substr(0, window.location.href.indexOf('#'));
}

// earliest possible bail on any match
if (hash.match(errorRoute)) {
Expand Down