Skip to content
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

feat(login): add hook for login + interceptor #22

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

ZiyedB
Copy link
Collaborator

@ZiyedB ZiyedB commented Feb 14, 2021

Still need to do :

  • Refine the login component and the layout
  • Hook this to the api once live & having a way to detect the expiration
  • Potentially hooking for the refresh token
  • Address all the todos in the code
  • Adding unit tests for this

Changes

  • Added the login screen & route
  • Added some based for the authentication interceptor & redirection

Screenshots

Screenshot 2021-02-14 at 17 57 45

Screenshot 2021-02-14 at 19 56 30

Screenshot 2021-02-14 at 19 56 35

src/App.tsx Outdated
import Ipfs from "./ipfs/Ipfs";
function App(): JSX.Element {
// Handle user state
// Ask if we should have this instead directly in a utils instead
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NiiCoder should we extract this to a util ? I wasn't sure as it's using some state ( not sure about how this would be structured)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to have this in utils

</Switch>
</BaseLayout>
</Router>
<UserContext.Provider value={{ isLoggedIn, login, logout }}>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought this would be the best to handle the user state. Open to suggestions, if there is a better way to do it with React

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think saving user information in localStorage is not a safe way. We have to ask the user to save user info("Remember me" checkbox).
And for the states that need to be used in several places, prefer to use redux.

<BaseLayout>
<Switch>
<Route path="/login" component={Login} />
<ProtectedRoute path="/ipfs/:hash" component={Ipfs} />
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering if we couldn't just handle this at a higher level, defining all the routes as protected seems overkill when we only have one route for not logged in ( /login )
But not sure if we can define two routers or instead conditional show one or the other ( if it's a good practice )

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think both are good. But to make code cleaner, prefer to have 2 routers.

</div>
{userContext.isLoggedIn ? (
<div className="navigation-bar__user">
{/* TODO: Need to have this as a small menu showing up */}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This menu would show up all the users path, if later on we add profile, right now this is just for logout on click

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good

@@ -0,0 +1,5 @@
export const apis = {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought it would be good to handle everything in a config folder @NiiCoder
Apis urls, environment variables, etc.

@@ -0,0 +1,3 @@
export const storageNames = {
user: 'pl11',
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just random "paralink11" just so it's not easy to guess when looking

@@ -0,0 +1,72 @@
$elevation-card: 0 0 1px 0 rgba(8, 11, 14, 0.6), 0 16px 16px -1px rgba(8, 11, 14, 0.1);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am gonna create a ticket to define a theme & variables to use accross the app & a global style. Right now this is using a lot of "custom" things. But if we can come up with a global styling ( for inputs, buttons, etc. ) this would make it easier

from: string;
}

const Login = (props: RouteComponentProps<{}, {}, LoginProps>): JSX.Element => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to add more comments but this is a base for now

paralinkApi.interceptors.request.use(
(config: any) => {
// Check for user token
const accessToken = localStorage.getItem(storageNames.user);
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things here where I need your input @NiiCoder

  1. Should we use directly the user context instead ( and store the token in it )?
  2. Should we store the token in the cookies or local storage is fine ? I went for local storage but cookies could work and we could make it 'more' secure in case

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this is what I was saying.

  1. store
  2. cookie

@ZiyedB ZiyedB self-assigned this Feb 14, 2021
@ZiyedB ZiyedB added the help wanted Extra attention is needed label Feb 14, 2021
@ZiyedB
Copy link
Collaborator Author

ZiyedB commented Feb 16, 2021

Screenshot 2021-02-16 at 20 36 40

Added the hook form & validation, it would be useful for other features later on ( like that query builder )

@NiiCoder note I decide to actually keep things as they are for the moment for those reasons :

  • Redux is a bit overkill for now, we are not storing the user information for now and only having a token. The user context should be more than enough. If we were to have more data shared across the app it would be definitely worth adding that.
    I've created this here : https://github.com/ZiyedB/paralink-ui/tree/TEST-WithRedux which is basically this PR with redux. ( a bit of struggle there ) but decided to keep things as they are now to make it less boilerplate. Also probably easier to test with this conf

  • I am also keeping it for now on the local storage for testing. Normally the set cookie would be directly set from server. This is to avoid issues with other browsers ( like Safari ). Once we have the api hooked up would check on that and add it. I like your idea about remember me ( probably something we could do for the refresh token )

@ZiyedB ZiyedB added enhancement New feature or request and removed help wanted Extra attention is needed labels Feb 18, 2021
.finally(() => {
// TODO: login loading should be set to false here
});
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In login, you are using both async/await and promise patterns. Also, you are saying that the function returns Promise<any> which is not true. In the case of async/await It returns Promise<void> in case of Promise it returns void.

I suggest one of the following implementations:
Promise pattern

const login = (data: LoginFormData): void => {
  console.log('data', data);
  paralinkApi
    .get('https://jsonplaceholder.typicode.com/todos')
    .then(() => {
      const token = 'faketokenhere';
      userContext.login(token);
    })
    .catch((err: any) => console.error(err))
    .finally(() => {});
};

Async/await pattern

const login = async (data: LoginFormData): Promise<void> => {
    console.log('data', data);
    try {
      var token = await paralinkApi.get('https://jsonplaceholder.typicode.com/todos')
      token = 'faketokenhere';
      userContext.login(token);
    } catch (e) {
      console.error(e);
    }
  };

Here are some suggestions when to use one or another:
https://levelup.gitconnected.com/async-await-vs-promises-4fe98d11038f

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants