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

Fix capture and handle expired token #4956

Merged
merged 5 commits into from Jul 11, 2022

Conversation

MUzairS15
Copy link
Contributor

@MUzairS15 MUzairS15 commented Jan 18, 2022

Signed-off-by: MUzairS15 muzair.shaikh810@gmail.com

Description

This PR fixes #3935

Notes for Reviewers
This PR also addresses #3919

Signed commits

  • Yes, I signed my commits.
1.mov

@github-actions github-actions bot added the component/ui User Interface label Jan 18, 2022
@codecov-commenter
Copy link

codecov-commenter commented Jan 18, 2022

Codecov Report

Merging #4956 (8b7c671) into master (40216c6) will decrease coverage by 0.00%.
The diff coverage is 0.00%.

@@            Coverage Diff            @@
##           master   #4956      +/-   ##
=========================================
- Coverage    6.02%   6.01%   -0.01%     
=========================================
  Files         107     107              
  Lines        9850    9851       +1     
=========================================
  Hits          593     593              
- Misses       9100    9101       +1     
  Partials      157     157              
Flag Coverage Δ
gointegrationtests 6.01% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
handlers/middlewares.go 0.00% <0.00%> (ø)
handlers/provider_handler.go 0.00% <ø> (ø)
router/server.go 0.00% <0.00%> (ø)

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 ff73237...8b7c671. Read the comment docs.

@MUzairS15
Copy link
Contributor Author

MUzairS15 commented Jan 18, 2022

I am not able to see the full coverage report.
Can anyone guide me to increase coverage?

Copy link
Contributor

@bariqhibat bariqhibat left a comment

Choose a reason for hiding this comment

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

A comment! Thanks for the contribution! @MUzairS15

@@ -156,19 +208,27 @@ function PerformanceProfile({ updateProgress, enqueueSnackbar, closeSnackbar })

function handleError(msg) {
return function (error) {
updateProgress({ showProgress : false });
if (error.source.errors[0].message != undefined) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we do a type check here? And additionally use optional chaining

@leecalcote
Copy link
Member

[Grover] "Cookie Monster, come out and play."

Copy link
Member

@leecalcote leecalcote left a comment

Choose a reason for hiding this comment

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

@MUzairS15, thanks for this PR!

This PR was discussed on today's Meshery meeting (we missed you ;) ). A couple comments were given such as "Can the cookie be used to determine session state / token expiry?", "How is this going to be applied to each UI / endpoint? It could be easy to miss an endpoint.", "Can this be achieved in NextJS with one central configuration?", "The golang middleware attached to each endpoint verifies token expiration and can redirect the user. This would be a central approach, too."

@MUzairS15
Copy link
Contributor Author

"The golang middleware attached to each endpoint verifies token expiration and can redirect the user. This would be a central approach, too."

Current implementation redirects user to login but it fails.
Screenshot 2022-01-20 at 8 53 01 AM
Similar to #3919
Even if, we happen to solve this, without any alert user will be redirected.

"Can the cookie be used to determine session state/token expiry?"

In the UI itself? Yes, we can do this.I thought about this but later resolved to rely on the server as in AuthMiddleware we were doing the same,

"How is this going to be applied to each UI / endpoint? It could be easy to miss an endpoint."

In data-fetch.js const dataFetch = (url, options = {}, successFn, errorFn) => {, we can handle the error here itself, (this will secure all the endpoint making a query),

Pls correct me if I am wrong.

@MUzairS15
Copy link
Contributor Author

MUzairS15 commented Jan 20, 2022

@leecalcote Can you suggest, should I handle error or add check for whether token is still valid or not in const dataFetch = (url, options = {}, successFn, errorFn) => {
OR I am getting things wrong
(Now, I regret not being able to join the meeting🥲, things have been clear)

@Abhishek-kumar09
Copy link
Member

Current implementation redirects user to login but it fails.

Are you referring to the current implementation (on master branch)?
It seems like the login request can't be sent directly from the server to the cloud, just by clients like mesheryctl or UI which is the required behaviour. Youy should try for UI redirection to login page, instead. Is it what you are doing now??

@Abhishek-kumar09
Copy link
Member

t, should I handle error or add check for whether token is still valid or not in const dataFetch = (url, options = {}, successFn, errorFn) => {

Token validation can only be done on the server/cloud. Rather you can check for the cookie key (token) be present on all requests that includes credential(also called cookies).

@MUzairS15
Copy link
Contributor Author

Current implementation redirects user to login but it fails.

Are you referring to the current implementation (on master branch)? It seems like the login request can't be sent directly from the server to the cloud, just by clients like mesheryctl or UI which is the required behaviour. Youy should try for UI redirection to login page, instead. Is it what you are doing now??

Yes, on master branch (server code, redirect fails, see screenshot above). And I have added logic to redirect from UI.
Based on #4956 (review), concern is to apply to each endpoint. So, if I instead of checking for errors in individual query call, what if I move this to dataFetch?

@MUzairS15
Copy link
Contributor Author

t, should I handle error or add check for whether token is still valid or not in const dataFetch = (url, options = {}, successFn, errorFn) => {

Token validation can only be done on the server/cloud. Rather you can check for the cookie key (token) be present on all requests that includes credential(also called cookies).

When token expires, cookie is not cleared, so cookie (token) will be present.

@Abhishek-kumar09
Copy link
Member

Abhishek-kumar09 commented Jan 21, 2022

Based on #4956 (review), concern is to apply to each endpoint. So, if I instead of checking for errors in individual query call, what if I move this to dataFetch?

Yeah that's what you should do, intercept every request that passes cookie to server. Do it in data fetch itself.

When token expires, cookie is not cleared, so cookie (token) will be present.

That's a good thought, but token is set to session expire policy, i.e, it will not expire frequently and only in two conditions:

  1. The user closes the browser (session is lost and so is the token)
  2. Token has timeout for 1 day or probably more (@leecalcote could confirm).

SO the case is extremely rare, meanwhile in case it is not correct (i.e., the auth token expired) the server will automatically redirect to the provider UI.

@Utkarsh-pro do you remember the token expiry time?

@MUzairS15
Copy link
Contributor Author

SO the case is extremely rare, meanwhile in case it is not correct (i.e., the auth token expired) the server will automatically redirect to the provider UI.

I tried with an expired token but was not redirected to provider UI.

@Abhishek-kumar09
Copy link
Member

Okay let's have a check at two levels:

  1. the client level for auth token present which is just one liner.
  2. the server for auth token expiry, and redirection to the provider UI instead of handling the login request itself.

What do you say @MUzairS15 ? What are the things needed for the 2 to be done, if you have any idea.

@MUzairS15
Copy link
Contributor Author

Okay let's have a check at two levels:

  1. the client level for auth token present which is just one liner.
  2. the server for auth token expiry, and redirection to the provider UI instead of handling the login request itself.

What do you say @MUzairS15 ? What are the things needed for the 2 to be done, if you have any idea.

In server AuthMiddleware checks for session validity, I can use the error to handle the redirect. But, then it will not be graceful handling

Irrespective of the page, users should universally see the same message - a modal informing the user that they their session has expired and that they will be directed to login in 3... 2... 1...

Copy link
Member

@Abhishek-kumar09 Abhishek-kumar09 left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @MUzairS15
So what you have done on the server is:

  1. check for token cookie being present
  2. If not present redirect to provider UI
  3. else marshal the cookie token and authenticate the user.

Just one concern, WHy you have added graphql in between? Is there any specific usecase?
I think this is not touching any graphql domain. Do we?

provider.Logout(w, req)
}

graphQlError := []Body{{"expired"}}
Copy link
Member

Choose a reason for hiding this comment

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

What is this doing?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, we are not touching graphQL, (just for sake of the name graphQlError) I will change the name.
I am sending `{
"errors": {[
"error" : "expired"
]}

}
` as json to get it when we do a query when token becomes invalid.

Copy link
Member

Choose a reason for hiding this comment

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

I think giving more verbose error will help, like auth token expired

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -56,7 +65,26 @@ func (h *Handler) AuthMiddleware(next http.Handler) http.Handler {
// }
// return
if provider.GetProviderType() == models.RemoteProviderType {
provider.Logout(w, req)
if err.Error() == "http: named cookie not present" {
Copy link
Member

Choose a reason for hiding this comment

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

use contains function for this. https://pkg.go.dev/strings#Contains

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Comment on lines 98 to 105
func (h *Handler) validateAuth(provider models.Provider, req *http.Request) (bool, error) {
err := provider.GetSession(req)
if err == nil {
// logrus.Debugf("session: %v", sess)
return true
return true, err
}
// logrus.Errorf("session invalid, error: %v", err)
return false
return false, err
Copy link
Member

Choose a reason for hiding this comment

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

You can delete the unusable comments like seen here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, will remove.

Comment on lines 366 to 380
<GenericModal
open={open}
handleClose={handleClose}
Content={
<Box sx = {style}>
<Typography id="transition-modal-title" variant="h6" component="h2" className={classes.modal}>
<span className = {classes.span}>&#9432;</span>Session Expired
</Typography>
{ counter && <Typography id="transition-modal-description" className = {classes.modalText} >
Your session has expired.
You will now be redirected to login... {counter}
</Typography>
}
</Box>
} />
Copy link
Member

Choose a reason for hiding this comment

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

Please extract this dialog and it's style in a separate component. They have been used twice on different pages.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

);
},
autoHideDuration : 8000, });
if (error.source.errors[0].message != undefined) {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (error.source.errors[0].message != undefined) {
if (error?.source?.errors?.[0]?.message) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

Comment on lines 123 to 128
useEffect(() => {
const timer = setTimeout(() => {
setCounter(counter - 1)
}, 1000)
return () => clearTimeout(timer)
})
Copy link
Member

Choose a reason for hiding this comment

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

The useEffect Body runs on every state change which is not desired. Actually this modal can be placed in it's own functional component for the purpose of re-usability and then this implementation will look good.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@@ -56,7 +65,26 @@ func (h *Handler) AuthMiddleware(next http.Handler) http.Handler {
// }
// return
if provider.GetProviderType() == models.RemoteProviderType {
provider.Logout(w, req)
if err.Error() == "http: named cookie not present" {
provider.Logout(w, req)
Copy link
Member

Choose a reason for hiding this comment

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

Earlier we had the problem for wildcard response set,

Are we solving it now?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is solved with the proposed changes, as we will not redirect from the server in case of an invalid session, instead query will fail, and UI will handle it by alerting the user.

@MUzairS15 MUzairS15 force-pushed the feature/MUzairS/3935 branch 2 times, most recently from bafa989 to ab905ed Compare January 25, 2022 18:12
open={open}
onClose={handleClose}
>
<Box sx={style}>
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
<Box sx={style}>
<Box className={classes.box}>

Comment on lines 73 to 80
authError := []Body{{"auth-token expired"}}
errors := Error{authError}
jsonMsg, err := json.Marshal(errors)
if err != nil {
logrus.Errorf("Error marshaling response")
http.Error(w, "unable to authenticate user", http.StatusInternalServerError)
return
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
authError := []Body{{"auth-token expired"}}
errors := Error{authError}
jsonMsg, err := json.Marshal(errors)
if err != nil {
logrus.Errorf("Error marshaling response")
http.Error(w, "unable to authenticate user", http.StatusInternalServerError)
return
}

Signed-off-by: MUzairS15 <muzair.shaikh810@gmail.com>
@github-actions github-actions bot removed the component/ui User Interface label Jan 26, 2022
Copy link
Member

@Abhishek-kumar09 Abhishek-kumar09 left a comment

Choose a reason for hiding this comment

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

After 30 mins long iteration, we got the root of the problem.

SO the /user/login is redirecting to the meshery cloud for the user login, but since the redirection is from the server the cloud takes server on localhost:9081 as client and further failing because server can't act as a client and this is the reason behind the cors error.

Cross origin request from the server is denied on the cloud for the Login and that's how it should behave. You can't [instantiate] a login from the server.

Now since we are redirecting to the provider UI now, the actual instantiation of login can be done through react-client.

Cookie Policy:

  1. The cookie expiration policy is set to session, i.e., the cookie invalidates on the new session.
  2. It has its own expiry time more than 24 hours in case the session is not closed,

We are touching both the cases and all the cases the user can open meshery and cannot use any meshery service without being authenticated.

There can be a need of slight UX improvemnt in terms of the first load with invalid token sent to meshery and being validated, but that's a seperate concern and is out of the scope of this PR.

I think this PR can be merged, and you can see the changes with your eyes, the all functions being working the way they should.

UI improvements (minor flash between meshery homepage to going to provider login page (only once)) can be done seprately.

@Abhishek-kumar09
Copy link
Member

// @leecalcote

@leecalcote
Copy link
Member

@MUzairS15, it'd be great for you to quick a quick demo of this work on a Meshery Development Meeting. There's one today in 30 minutes, if you're free. Details here: https://meet.layer5.io

@stale
Copy link

stale bot commented Mar 28, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the issue/stale Issue has not had any activity for an extended period of time label Mar 28, 2022
@leecalcote
Copy link
Member

@sudo-NithishKarthik what do you think? #4956 (review)

@stale stale bot removed the issue/stale Issue has not had any activity for an extended period of time label Mar 29, 2022
@MUzairS15
Copy link
Contributor Author

@sudo-NithishKarthik ping

@stale
Copy link

stale bot commented Jun 15, 2022

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@leecalcote
Copy link
Member

@MUzairS15 will you put this on the agenda for next week's Meshery Dev meeting?

@MUzairS15
Copy link
Contributor Author

Yes @leecalcote

@leecalcote
Copy link
Member

Discussed again today. The current server-side redirect is good and the return of the user-facing modal is also desirable.

Signed-off-by: MUzairS15 <muzair.shaikh810@gmail.com>
Signed-off-by: MUzairS15 <muzair.shaikh810@gmail.com>
@@ -24,7 +23,6 @@ func (h *Handler) ProviderHandler(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w, &http.Cookie{
Name: h.config.ProviderCookieName,
Value: p.Name(),
Expires: time.Now().Add(h.config.ProviderCookieDuration),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changes meshery-provider cookie as Session cookie.

Copy link
Member

Choose a reason for hiding this comment

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

✔️

@MUzairS15 MUzairS15 changed the title Fix capture and handle expired token in performance page Fix capture and handle expired token Jul 11, 2022
Signed-off-by: MUzairS15 <muzair.shaikh810@gmail.com>
@leecalcote leecalcote merged commit 54a7706 into meshery:master Jul 11, 2022
@MUzairS15 MUzairS15 added kind/enhancement Improvement in current feature language/go Issues or pull requests that use Golang security Issues or pull requests that address a security vulnerability labels Jul 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component/ui User Interface kind/enhancement Improvement in current feature language/go Issues or pull requests that use Golang security Issues or pull requests that address a security vulnerability
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[UI] Capture and Handle Expired Token
5 participants