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

ISSUE-365: Destroy JWT tokens on logout #412

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from

Conversation

Ammar-T
Copy link
Contributor

@Ammar-T Ammar-T commented Nov 24, 2022

Details

  • New authentication system which caches JWT tokens via Redis
  • On login, user gets a JWT token which is stored in Redis
  • On logout, token gets removed from Redis and every request checks if the user's token still exists (if it does -> good, if not -> will logout client from UI)

NOTE: For development environments, Redis will have to be running locally. To setup:

  1. Install redis on your machine via https://redis.io/download/
  2. After running usual npm run dev and ng serve, we also need to run redis-server to start the redis server.
  3. Also, remember to run npm install since new packages in this PR.
  4. Update environment variables as follows:
# SCORE_SERVER_ADDRESS=https://score.oise.utoronto.ca
# CKBOARD_SERVER_ADDRESS=https://ck-board.oise.utoronto.ca
# APP_DOMAIN=.oise.utoronto.ca
SCORE_SSO_ENDPOINT=/sso/ckboard
SCORE_LOGOUT_ENDPOINT=/api/logout

Comment out first two since we're in the dev environment.

Closes #365

@JoelWiebe
Copy link
Contributor

@Ammar-T For the production server, it looks like we will need Ed-Commons to set this up: https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-nodejs-get-started

Should I go ahead and make this request?

@JoelWiebe
Copy link
Contributor

@geoffreykwan Would you be able to help us with making the call to log out of SCORE when we log out of CK Board (and vice versa)?

@JoelWiebe
Copy link
Contributor

JoelWiebe commented Nov 29, 2022

@Ammar-T I tested running SCORE locally and using SCORE login for CK Board. After entering my credentials on SCORE, I was navigated to a URL and presented with a blank page (http://localhost:4201/sso/login/bm9uY...)

  • SCORE backend: score-mysql | mbind: Operation not permitted
  • CK Board backend: (node:52182) UnhandledPromiseRejectionWarning: Error: Expected "payload" to be a plain object.
    at validate (/Users/abc/ck-board/backend/node_modules/jsonwebtoken/sign.js:40:11) ... at generateSessionToken (/Users/abc/ck-board/backend/src/utils/auth.ts:230:21)
    at /Users/abc/ck-board/backend/src/utils/auth.ts:223:50
    at Generator.next ()
    at fulfilled (/Users/abc/ck-board/backend/src/utils/auth.ts:5:58)
    at processTicksAndRejections (internal/process/task_queues.js:95:5)
    (node:52182) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)

@geoffreykwan
Copy link
Contributor

To have CK Board sign out of SCORE, you can make a GET request to

http://localhost/api/logout

Where localhost is the local SCORE server.

We might want to add an environment variable to the .env file in CK Board backend to specify the SCORE server address. It could be named something like SCORE_SERVER or SCORE_SERVER_ADDRESS that is set to http://localhost when running locally. Then in backend/src/api/auth.ts in the /logout function we can grab the SCORE server address and make a GET request to ${process.env.SCORE_SERVER_ADDRESS}/api/logout and this should log the user out of SCORE.

To have SCORE sign out of CK Board, is there a CK Board endpoint that SCORE can make a request to, to log out of CK Board?

@JoelWiebe
Copy link
Contributor

Thanks @geoffreykwan, I like that idea of a SCORE_SERVER_ADDRESS environment variable. I suppose we could update the update the SCORE_SSO_ENDPOINT as well to only require the path (e.g., /sso/ckboard) and get the server address from that new variable?

@Ammar-T, I believe you said we had a CK Board endpoint that SCORE could make requests to for logout, right?

@geoffreykwan
Copy link
Contributor

Yea if we have a SCORE_SERVER_ADDRESS environment variable (set to http:/localhost when used locally), we could change the SCORE_SSO_ENDPOINT environment variable to /sso/ckboard and then we would make requests to ${SCORE_SERVER_ADDRESS}${SCORE_SSO_ENDPOINT} for SSO.

@Ammar-T
Copy link
Contributor Author

Ammar-T commented Dec 14, 2022

@geoffreykwan For the SCORE logout endpoint mentioned, is there no additional data that needs to be sent with the request? I'm wondering if anyone hits that endpoint from Postman, for example, who and how would it log them out? Is there no extra data that must be passed in the header/body which would identify the user attempting to logout?

@geoffreykwan
Copy link
Contributor

If you want to log out from Postman, you need to set the SESSION cookie. I think if you make the request to https://score.oise.utoronto.ca/api/logout (or http://localhost/api/logout) from the CK Board client, it would automatically use the SESSION cookie for SCORE. I haven't tested this but I think it should work. If you want to make the request to the SCORE logout endpoint from the CK Board backend, I think you would need to send the SESSION cookie to the backend so that the backend could use it in the logout request. I haven't tested this either.

You can manually get the SCORE SESSION cookie by logging into SCORE and then looking at a request header.

Screenshot 2022-12-14 173605 marked

Here's how to set the SESSION cookie in Postman

  1. Click "Cookies" below the "Send" button
  2. Enter the domain for SCORE (like localhost) and then click "Add domain"
  3. Click "Add Cookie" and paste in the SESSION cookie
  4. Click "Save"
  5. Close the "Cookies" popup
  6. Set the request url to the logout endpoint (like localhost/api/logout)
  7. Click "Send" and it should log the user out. You can verify by going back to your browser and refreshing the page to make sure they are really logged out.

Screenshot 2022-12-14 173652

@Ammar-T
Copy link
Contributor Author

Ammar-T commented Dec 14, 2022

Thanks for the detailed reply! I'll try this out.

@Ammar-T
Copy link
Contributor Author

Ammar-T commented Dec 15, 2022

Figured out how to pass cookies into the backend after having to change a few cors policies, but there's a new problem. Users of CK board never have access to cookies of SCORE, so we're never able to pass in that SESSION cookie to the ck backend.

But, since ckboard and score are both subdomains of the same domain (oise.utoronto.ca), there is a way to share cookies between the 2 (https://stackoverflow.com/a/32355083/13725847). Basically, just specifying the parent domain when setting the cookie - Set-Cookie: SESSION=value; domain=.oise.utoronto.ca.

I tried finding where this was happening in SCORE-API but wasn't successful. Could you point me in the right direction and I can create a PR in SCORE.

@geoffreykwan
Copy link
Contributor

Looks like we only need to set some properties in the application.properties file (when doing this on your local machine you should set it in the application-dockerdev.properties file). The two properties below are what you should add to the properties file. Change the localhost value appropriately when using on the production server (I'll change this on the SCORE production server sometime during user down time and tell you after I've done so).

server.servlet.session.cookie.domain=localhost
server.servlet.session.cookie.path=/

You can see the domain cookie in the j_acegi_security_check request that is made when you log in to SCORE. Look at the Headers > Response Headers > Set-Cookie value.

Screenshot 2022-12-15 114236

@Ammar-T
Copy link
Contributor Author

Ammar-T commented Dec 16, 2022

@JoelWiebe This is ready for re-review, should now work with SCORE login and will also logout users from SCORE if they logout from CK-Board.

@geoffreykwan The endpoint you would need to hit to logout CK Board is /api/auth/logout. In the request header, you will also need to supply the CK board authorization token (access_token in local storage) as something like:

headers: { Authorization: 'Bearer: [access_token]' },

@Ammar-T
Copy link
Contributor Author

Ammar-T commented Dec 17, 2022

Cookie is created now on login which is the same as access_token, which should be accessible on SCORE side. Note: new environment variable added as well # APP_DOMAIN=.oise.utoronto.ca which should be commented out when testing locally.

@JoelWiebe
Copy link
Contributor

@geoffreykwan Would you have time to review this PR as well?

Copy link
Contributor

@JoelWiebe JoelWiebe left a comment

Choose a reason for hiding this comment

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

This appears to work for me.

Tested:

  • login from CK Board also logs in for SCORE
  • logout from CK Board also logs out of SCORE

Great work!

@geoffreykwan
Copy link
Contributor

geoffreykwan commented Dec 19, 2022

I can't seem to access the CK_SESSION cookie. If I look at the cookies on the browser I only see the SESSION cookie (from SCORE) and not the CK_SESSION cookie.

Here is how I'm looking at the cookies

  1. Press F12 to open the developer tool;
  2. Click on the Application tab;
  3. Under the "Storage" section open Cookies node and click on your website address;

On the CK Board backend I've even tried setting the CK_SESSION cookie parameters to be more relaxed like this (I set httpOnly to false and secure to false) and I still can't see it in the browser.

  res.cookie('CK_SESSION', sessionToken.token, {
    httpOnly: false,
    domain: process.env.APP_DOMAIN || 'localhost',
    expires: sessionToken.expiresAt,
    secure: false,
  });

When I sign out of CK Board it does sign me out of SCORE. I'm still trying to get signing out of SCORE to also sign out of CK Board but I need the CK_SESSION value to do that and I can't seem to obtain it.

@geoffreykwan
Copy link
Contributor

I think I figured out how to get the CK_SESSION cookie to save in the browser. In the CK Board frontend, when the sso login request is made to the CK Board backend, we need to provide the withCredentials: true parameter when making the request.

In frontend/src/app/services/user.service.ts

  async ssoLogin(sso: string | null, sig: string | null): Promise<boolean> {
    return this.http
      .get(`auth/sso/login/${sso}/${sig}`, { withCredentials: true })
...

Now I can work on having SCORE make the request to sign out of CK Board.

@Ammar-T
Copy link
Contributor Author

Ammar-T commented Dec 19, 2022

Oh good catch, I'll update this PR.

@geoffreykwan
Copy link
Contributor

I've created a PR on SCORE-API to have SCORE make the request to sign out of CK Board.

@JoelWiebe
Copy link
Contributor

@Ammar-T Are these all of the variables that we need added or updated on our production server?

SCORE_SERVER_ADDRESS=https://score.oise.utoronto.ca
CKBOARD_SERVER_ADDRESS=https://ck-board.oise.utoronto.ca
APP_DOMAIN=.oise.utoronto.ca
SCORE_SSO_ENDPOINT=/sso/ckboard
SCORE_LOGOUT_ENDPOINT=/api/logout

@JoelWiebe
Copy link
Contributor

@Ammar-T Just re-requested approval for the redis cache, once approved we can request the changes to environment variables and upgrade perhaps early next week.

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.

Add logout endpoint to backend
3 participants