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: Add Sign-out functionality #46

Merged
merged 16 commits into from
Jan 25, 2022
Merged

Conversation

bryphe-coder
Copy link
Contributor

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

#37 implemented the Sign-in flow, but there wasn't a Sign-out flow as part of that PR (aside from letting the cookie expire... or manually deleting the cookie...), which is obviously not ideal.

This PR implements a basic sign-out flow, along with a very simple user dropdown:
2022-01-21 18 09 14

Bringing in a few pruned down components for the <UserDropdown /> to integrate into the <NavBar />.

In addition, this also implements a simple back-end API for /logout which just clears the session token.

TODO:

@codecov
Copy link

codecov bot commented Jan 22, 2022

Codecov Report

Merging #46 (00df3d9) into main (a44056c) will increase coverage by 0.30%.
The diff coverage is 84.44%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main      #46      +/-   ##
==========================================
+ Coverage   71.89%   72.20%   +0.30%     
==========================================
  Files          64       71       +7     
  Lines        2711     2795      +84     
  Branches       30       36       +6     
==========================================
+ Hits         1949     2018      +69     
- Misses        592      605      +13     
- Partials      170      172       +2     
Flag Coverage Δ
unittest-go-macos-latest 67.30% <85.71%> (-0.14%) ⬇️
unittest-go-ubuntu-latest 70.58% <85.71%> (+0.08%) ⬆️
unittest-go-windows-latest 67.17% <85.71%> (+0.03%) ⬆️
unittest-js 76.55% <84.05%> (+1.63%) ⬆️
Impacted Files Coverage Δ
site/pages/index.tsx 0.00% <0.00%> (ø)
site/contexts/UserContext.tsx 77.27% <55.55%> (-16.07%) ⬇️
codersdk/users.go 57.14% <66.66%> (+1.58%) ⬆️
site/components/User/UserProfileCard.tsx 75.00% <75.00%> (ø)
site/components/User/UserAvatar.tsx 87.50% <87.50%> (ø)
site/components/Navbar/UserDropdown.tsx 88.00% <88.00%> (ø)
coderd/coderd.go 89.13% <100.00%> (+0.24%) ⬆️
coderd/users.go 63.24% <100.00%> (+2.32%) ⬆️
site/components/Icons/Logout.tsx 100.00% <100.00%> (ø)
site/components/Icons/index.ts 100.00% <100.00%> (ø)
... and 6 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 a44056c...00df3d9. Read the comment docs.

@bryphe-coder bryphe-coder marked this pull request as ready for review January 22, 2022 02:55
@bryphe-coder bryphe-coder self-assigned this Jan 22, 2022
coderd/users.go Outdated
@@ -199,6 +199,20 @@ func (users *users) loginWithPassword(rw http.ResponseWriter, r *http.Request) {
})
}

// Clear the user's session cookie
func logout(rw http.ResponseWriter, r *http.Request) {
Copy link
Member

Choose a reason for hiding this comment

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

Minor nit: We should add the users strict to contain this operation. In an ideal world, the API key is invalidated too. Might be a good issue!

Copy link
Member

Choose a reason for hiding this comment

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

A test for this would be good too!

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 fixed the receiver name in: af001cb

And had a go at adding a test in: 52d0136

One thing I wasn't sure about was the right level-of-abstraction for the test. I thought about adding codersdk functions for:

  • Logout
  • ClearSessionTokens

but I realized when I started writing those that it actually doesn't test the functionality of sending-a-cookie-that-clears-a-session. But on the flip side, the test I wrote is different than the other tests (it constructs a request and validates the cookies returned are correct) - whereas the other test files call against codersdk. Curious to get your thoughts!

Copy link
Member

Choose a reason for hiding this comment

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

Hmm. I'd add a call for Logout, even if it doesn't function as expected right now.

It seems odd to have no deauthenticate functionality inside of the codersdk.

I'm also fine with you adding a comment for that, since logout isn't a critical path for most anyways!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Gave adding this a try here: 4dcd1fd

I added a Logout as the deauth version of LoginWithPassword, and ClearSessionTokens as the opposite of SetSessionToken (although ClearSessionTokens is currently clearing everything). Since the Client doesn't expose any internals about what cookies are around - the test is kinda awkward. It'd be better if it verified the state of cookies

bryphe-coder added a commit that referenced this pull request Jan 22, 2022
While I was testing the Sign-out functionality in #46 , I found a bug on login. 

Sometimes, intermittently, the login wouldn't 'take' even though the login POST was successful and the subsequent GET to `/api/v2/user` was successful - the user would still be brought to the `/login` screen a second time:

![2022-01-21 19 52 31](https://user-images.githubusercontent.com/88213859/150623831-7ce33fca-97d5-4ea8-8301-9149def1ee40.gif)

^ I log-in, and then the login screen just resets, and logging in a second time works 🤔 

I tracked this down and found it is a new race condition that I inadvertently introduced when I migrated to SWR in #46. Because SWR caches responses based on the API path - we have to invalidate the cache for `/api/v2/user` when the user logins, otherwise we'll continue to serve the user-not-found error until SWR decides to retry. 

However, we weren't waiting for that cache-invalidation to go through - so there was the chance that an `/api/v2/user` request was still in-flight while we were redirecting after a successful login. If the request made it back prior to the redirect - login would work on the first try. If the request took longer - SWR would serve the stale, erroneous user from the cache.

Luckily the fix is simple - `mutate` in the SWR API returns a promise, so we can just wait for that to go through before redirecting. Unfortunately, though, this is tricky to exercise in a unit test because with the SWR model, the login logic is spread between `_app.tsx`, `UserContext.tsx`, and `SignInForm.tsx`, and the race condition involves an integration of all of those places. 

I found that there is a lint rule that can help protect us from making this mistake - [`typescript-eslint/no-floating-promises`](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-floating-promises.md) (it's a lint rule we wanted to turn on in `coder/m`, too, actually - just a lot of work to clean up the code for it!), so I turned it on as part of this PR, and it caught a couple extra places to check.
Comment on lines 24 to 27
err := client.SetSessionToken("test-session")
require.NoError(t, err, "Setting a session token should be successful")

err = client.ClearSessionToken()
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 test is a little awkward because it's not really verifying anything, except that no errors occurr with SetSessionToken and ClearSessionToken.

The Client at the moment doesn't expose a way to look at the internal state of cookies, which is probably by design! But it would help to be able to poke inside and see if setting the session token actually did something, and likewise, if clearing the session token actually did stuff too.

@@ -47,6 +47,24 @@ func (c *Client) SetSessionToken(token string) error {
return nil
}

// ClearSessionToken clears tokens and authentication context in the current cline.t
func (c *Client) ClearSessionToken() error {
Copy link
Member

Choose a reason for hiding this comment

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

Could we just make SetSessionToken do this with an empty string? Then we don't need another func!

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, this isn't used anywhere - I was thinking of having pairs of Login/Logout and SetSessionToken/ClearSessionToken but totally fine not to have that. Removed in aa84624

@bryphe-coder bryphe-coder merged commit 69d88b4 into main Jan 25, 2022
@bryphe-coder bryphe-coder deleted the bryphe/feat/user-profile-card branch January 25, 2022 01:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants