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: Initial Login flow #42

Merged
merged 38 commits into from
Jan 21, 2022
Merged

feat: Initial Login flow #42

merged 38 commits into from
Jan 21, 2022

Conversation

bryphe-coder
Copy link
Contributor

@bryphe-coder bryphe-coder commented Jan 20, 2022

This just implements a basic sign-in flow, using the new endpoints in #29 :
2022-01-20 12 35 30

This brings over several dependencies that are necessary:

  • formik
  • yep

Ports over some v1 code to bootstrap it:

  • FormTextField
  • PasswordField
  • CoderIcon

TODO:

  • Add spinner to Sign In button, for better feedback
  • Force redirect to /login if not authenticated
  • Add full screen spinner when getting initial user info
  • Add test for PasswordField
  • Add test for SignInForm
  • Add test for AuthenticatedRouter
  • Pivot to link-style flow
  • Switch the /user API to use SWR-

Fixes #37
Fixes #43

This does not implement it navbar integration (importantly - there is no way to sign out yet, unless you manually delete your session_token). I'll do that in the next PR - figured this was big enough to get reviewed.

@bryphe-coder bryphe-coder self-assigned this Jan 20, 2022
@codecov
Copy link

codecov bot commented Jan 20, 2022

Codecov Report

Merging #42 (bf2a9af) into main (7b9347b) will increase coverage by 6.39%.
The diff coverage is 79.47%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main      #42      +/-   ##
==========================================
+ Coverage   64.70%   71.10%   +6.39%     
==========================================
  Files          19       59      +40     
  Lines         153     2308    +2155     
  Branches        9       30      +21     
==========================================
+ Hits           99     1641    +1542     
- Misses         50      521     +471     
- Partials        4      146     +142     
Flag Coverage Δ
unittest-go-macos-latest 66.25% <ø> (?)
unittest-go-ubuntu-latest 70.10% <ø> (?)
unittest-go-windows-latest 66.04% <ø> (?)
unittest-js 74.91% <79.47%> (+10.20%) ⬆️
Impacted Files Coverage Δ
site/components/Page/Footer.tsx 100.00% <ø> (ø)
site/components/SignIn/index.tsx 0.00% <0.00%> (ø)
site/pages/_app.tsx 0.00% <0.00%> (ø)
site/pages/index.tsx 0.00% <0.00%> (ø)
site/pages/login.tsx 0.00% <0.00%> (ø)
site/components/Form/PasswordField.tsx 86.66% <86.66%> (ø)
site/contexts/UserContext.tsx 93.33% <93.33%> (ø)
site/components/Form/FormTextField.tsx 94.28% <94.28%> (ø)
site/components/Button/LoadingButton.tsx 100.00% <100.00%> (ø)
site/components/Button/index.ts 100.00% <100.00%> (+100.00%) ⬆️
... and 36 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 7b9347b...bf2a9af. Read the comment docs.

site/components/Form/types.ts Outdated Show resolved Hide resolved

type PasswordFieldProps = Omit<TextFieldProps, "InputProps" | "type">

export const PasswordField: React.FC<PasswordFieldProps> = ({ variant = "outlined", ...rest }) => {
Copy link
Member

Choose a reason for hiding this comment

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

Why is this called PasswordField, but the other named FormTextField?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is brought in mostly as-is from v1, but there's actually a has-a relationship here.

FormTextField contains either:

  • TextField
  • PasswordField

In the v1 coder/m codebase - we used the prefix Form* to denote that the controller is meant to be bound to a formik form. That means that it exposes a couple of properties:

  • form: a formik form
  • formFieldName: the field name to bind too

With that, the Form* components handle binding to Formik:

  • onChange (calls into formik)
  • onBlur (calls into formik)
  • error (sets appropriate property on control based on form state + field)
  • helperText (set to error text if there is an error, otherwise helper text if provided)
  • id (set to form field name)

So essentially FormTextField is a convenience component that helps bind to formik - instead of having to bind to 5 different props to get proper form behavior (and potentially doing it inconsistently) we only have to bind to two props - form and formFieldName, and get common behavior across our forms.

And it happens that FormTextField can either use a TextField or PasswordField under the hood, depending on the isPassword prop. TextField and PasswordField don't know anything at all about formik - they're just 'dumb' input components.

In the v1/coder/m codebase, we actually have a bunch of these Form* components, including:

  • FormCheckbox
  • FormRadioGroup
  • FormField
    ...and we've talked about a few others, like FormToggleSwitch

Hope that helps clarify - let me know if you have other questions

Copy link
Member

Choose a reason for hiding this comment

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

Ahh this is very helpful context. Thanks Bryan!

* CoderIcon represents the cloud with brackets Coder brand icon. It does not
* contain additional aspects, like the word 'Coder'.
*/
export const CoderIcon = (props: SvgIconProps): JSX.Element => (
Copy link
Member

Choose a reason for hiding this comment

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

I know we didn't in the other repo, but it might be wise to consider a default export idiom for this codebase.

CoderIcon.tsx should default export CoderIcon from my POV.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is actually kind of contentious in the TypeScript world - there's a couple of quirks with default exports.

  • The refactoring tools in VSCode / TypeScript language server don't work as well with default exports
  • In some compilation modes, the default export is kind of awkward (it literally exports a named module 'export', which can cause problems with some tools). NextJS handles this OK, but if we use a tool with a Babel pipeline like storybook, we can sometimes run into weird issues.

There's a bit more info in these links:

That being said, it seems to work fine for our pages/ 😄 And I think the icons is a reasonable use-case for this, I agree with you conceptually that it makes sense for CoderIcon.tsx. We can try it out if you want.

Copy link
Member

Choose a reason for hiding this comment

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

With the context you provided here, I'll trust the direction you're taking here! Let's leave it as-is for now, everything seems to point away from the defaults hahah

import { FullScreenLoader } from "../Loader/FullScreenLoader"
import * as API from "./../../api"

export const AuthenticatedRouter: React.FC = ({ children }) => {
Copy link
Member

Choose a reason for hiding this comment

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

Should we do this in _app.tsx instead? Seems quite standard.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As it is currently implemented - this is a case where it actually wouldn't make sense to include this in _app.tsx - if we did, the /login route would get in an infinite loop. (The _app.tsx would fetch the current user, fail, redirect to login.... which would render _app.tsx first, fail, redirect to login... forever 😄 )

But I think this an example of a case where something like an <AppPage /> can help us (from the comment in the other PR here: #33 (comment)) - every <AppPage /> should include this.

However, if we removed the login.tsx, and just implemented that full flow in _app.tsx - it could work. My concern is that the _app.tsx can grow really fast and it could also add complexity when we add other login flows besides built-in auth (ie, OIDC)

useEffect(() => {
const asyncFn = async () => {
try {
await API.User.current()
Copy link
Member

Choose a reason for hiding this comment

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

This might be a good opportunity to introduce SWC. It gives us a lot of beeeeautiful inherit benefits.

Primarily in periodically refreshing to ensure state is accurate with the backend.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I'm open to exploring it!

Are you OK if we track this outside of the PR? And do you think it is necessary for black triangle, or something we can defer until after?

Opened this issue to track, and I marked it as a MVP for the time being: #43 but let me know if its something you'd prefer to do in this PR (or outside the PR, but for black triangle, and I'll update the milestone)

Copy link
Member

Choose a reason for hiding this comment

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

Well, the reason I recommend we use it is actually for simplicity. Although there's little familiarity, I'm not sure there needs to be: https://github.com/coder/link/blob/main/site/src/contexts/UserContext.tsx#L35

Just makes it really simple, and removes the requirement of abstracting a fetch at all.

This is up to you as to whether we use it or not. It does handle all the errors n' such nicely though, so could be cool!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Cool, I'll take a look as part of this PR then and try it out! Thanks for the link to the usage in link - helpful

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried it out and it actually makes things more complicated (for login, at least). It isn't really meant to POST or take body arguments out of the box.

Here's a GH issue describing setting up a login flow:
vercel/swr#553 (comment)

I'll revisit for some of the other places, though, where might be able to take better advantage of the error handling / refresh behavior.

Copy link
Member

Choose a reason for hiding this comment

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

Ahh I mean mostly for fetching. Much less useful for any other type of request.

@bryphe-coder bryphe-coder marked this pull request as ready for review January 21, 2022 02:06
Copy link
Member

@kylecarbs kylecarbs left a comment

Choose a reason for hiding this comment

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

Awesome!

site/pages/_app.tsx Outdated Show resolved Hide resolved
@bryphe-coder
Copy link
Contributor Author

Thanks for reviewing, @kylecarbs !

@bryphe-coder bryphe-coder merged commit 4183a4e into main Jan 21, 2022
@bryphe-coder bryphe-coder deleted the bryphe/feat/37/sign-in-flow branch January 21, 2022 19:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants