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

Support for multiple frontends with the same backend #1352

Open
alahiff opened this issue Aug 1, 2024 · 16 comments
Open

Support for multiple frontends with the same backend #1352

alahiff opened this issue Aug 1, 2024 · 16 comments

Comments

@alahiff
Copy link

alahiff commented Aug 1, 2024

Summary

We would like to have multiple frontends sharing a single backend, using Keycloak for authentication. Each frontend would be for a different experiment and require different configuration (hence the reason for multiple frontends).

Current Behaviour

I tried removing OIDC_SUCCESS_URL from the backend config since there can be multiple possible URLs, depending on the frontend being used. I was expecting the code introduced by #603 to ensure this works correctly, but the backend gives:

[Nest] 1  - 08/01/2024, 12:40:05 PM   ERROR "Internal server error"
[Nest] 1  - 08/01/2024, 12:40:05 PM   ERROR TypeError [ERR_INVALID_URL]: Invalid URL

Adding some console.log statements into the backend it seems that request in getRequest in https://github.com/SciCatProject/scicat-backend-next/blob/master/src/auth/guards/oidc.guard.ts does contain the correct value of referer in the header. However, after authentication with Keycloak the backend sets referer to undefined (in oidc.guard.ts), so the information about referer obtained in the previous step was lost.

Expected Behaviour

When OIDC_SUCCESS_URL is not defined in the backend config, after succesful authentication users would get redirected back to the frontend they had originally come from (due to the referer header).

Details

I am using SciCatLive 2.8.0.

@bpedersen2
Copy link
Contributor

I think the better approach would be to split the auth in two parts instead (which is a very typical OIDC flow):

  • a frontend login, using a "public" keycloak client
  • the backend just receives the token from the frontend and use a confidental client.

I did not try it yet, but my feeling that passing a valid token should already work, so the alternate clients just need to do the oidc-login themself ( there are suitable libs out there for most frameworks).

Otherwise we open up for referer-spoofing attacks.

@alahiff
Copy link
Author

alahiff commented Aug 2, 2024

Thanks for the reply. That's pretty much what I was planning to do, with a completely custom frontend. I just need to work out how to actually create the users in the backend in this case (i.e. so there are appropriate documents in the UserIdentity collection).

I was also hoping it might be possible to run multiple instances of the official (unmodified) SciCat frontend instead as an option, but if not, that's not a problem.

@nitrosx
Copy link
Contributor

nitrosx commented Aug 5, 2024

You can configure multiple FE with a single BE. Each user working on the FE will get its own token.

Could you elaborate more on what you mean by "create users in the BE"?

I'm really curious to see a proof of concept of the solution that you two (@alahiff and @bpedersen2) mention in the post above

@alahiff
Copy link
Author

alahiff commented Aug 5, 2024

You can multiple FE with a single BE. Each user working on the FE will get its own token.

How do you do this? The backend has the configuration OIDC_SUCCESS_URL, which means that after authentication via Keyclock users will always be redirected back to a single FE, rather than the FE they originally tried to login from. Maybe there's something simple I'm missing :-)

@nitrosx
Copy link
Contributor

nitrosx commented Aug 5, 2024

@alahiff my mistake. Apologies for the confusion.
You are correct. I answered before I reviewed completely your and @bpedersen2 answers.
We need some code refactoring before we can do that.

@alahiff
Copy link
Author

alahiff commented Aug 5, 2024

Could you elaborate more on what you mean by "create users in the BE"?

If the FE is integrated with Keycloak (rather than the BE) then after successful authentication the frontend could create a token which could then be provided when querying the backend. I was thinking that the BE wouldn't even need to be integrated with OIDC, but of course would need to know the same secret as the FE in order to validate the tokens. The existing JWT auth in the backend could be used.

Looking at an existing token created by the BE, it contains _id and id (identical values) which refer to an existing user in MongoDB. So the problem with the above is that somehow the FE needs to get the id of the user identity from the BE, and to trigger creation of a new identity when a user logs in for the first time.

I'm not sure if this could be done using the existing user API in the BE, or maybe the existing OIDC integration in the BE could somehow be leveraged, as it already handles creating the user identity:

await this.usersService.createUserIdentity(createUserIdentity);

@bpedersen2
Copy link
Contributor

No, that is not how oidc auth should be used.

The typical flow is:

  • FE requests token from OIDC server with a "public" client (no client secret).
  • On each request to the BE this token is passsed to the BE
  • The backend is a "confidental" client ( with client secret) and can validate this token (and fetch the userinfo).
  • This info is then used to resolve the user and this info can be passed backed to the frontend via a suitable api endpoint.

@nitrosx
Copy link
Contributor

nitrosx commented Aug 6, 2024

I trust @bpedersen2 to provide the correct information.
I think I need to go back and read once more the OIDC documentation.

@alahiff: just to make sure that I understand correctly what you are trying to achieve, can you confirm that my following statement is true:

You would like to be able to deploy a single backend and multiple frontends with different URLs. You would like to be able to authenticate through OIDC from any of the frontend and be able to be redirected back to the correct one.

Thanks

@alahiff
Copy link
Author

alahiff commented Aug 6, 2024

@nitrosx Yes, that statement is true.

@nitrosx
Copy link
Contributor

nitrosx commented Aug 6, 2024

@alahiff thanks for confirming.
I will raise the question with the community at the next collaborators meeting, which is this afternoon.

@alahiff
Copy link
Author

alahiff commented Aug 6, 2024

On each request to the BE this token is passsed to the BE

The current tokens accepted by the backend look like this:

{
  "_id": "66a248704e178fc76d15cd09",
  "username": "oidc-user",
  "email": "[email protected]",
  "authStrategy": "oidc",
  "__v": 0,
  "id": "66a248704e178fc76d15cd09",
  "userId": "66a248704e178fc76d15cd09",
  "iat": 1722945688,
  "exp": 1722949288
}

How does the FE know the id in your step 2? (as it's something generated & stored in the backend).

I suppose one option is use in the tokens the ids from the OIDC provider instead, and modify the backend appropriately.

@nitrosx
Copy link
Contributor

nitrosx commented Aug 6, 2024

@alahiff I just chatted with the collaborators and @minottic pointed me to the following code:
https://github.com/SciCatProject/scicat-backend-next/blob/master/src/auth/auth.controller.ts#L92-L95

According to this section of the code, the url that you are redirected to upon successful login is configured in the backend configuration under the oidc.successUrl.
If you leave that entry empty, it will use the referrer field that is present in the request header.

Let me know if this help and your feedback on how to improve the documentation

@alahiff
Copy link
Author

alahiff commented Aug 6, 2024

@nitrosx That was one of the first things I tried but it doesn't work unfortunately. After authentication with Keycloak referrer is undefined and the backend gives this error:

[Nest] 1  - 08/01/2024, 12:40:05 PM   ERROR "Internal server error"
[Nest] 1  - 08/01/2024, 12:40:05 PM   ERROR TypeError [ERR_INVALID_URL]: Invalid URL

@nitrosx
Copy link
Contributor

nitrosx commented Aug 6, 2024

Thanks for confirming.
I will inquire more with the collaborators that have done it in the past and let you know.<

Now I'm wondering how we can test this use case in CI/CD

@minottic
Copy link
Contributor

minottic commented Aug 7, 2024

the snippet that Max shared tries to get the referer from the BE callback, and, given what you say, the referer is empty there because kc intercepts it and unset it. This is because of the login flow:

FE --> BE (init) --> KC --> BE (callback) --> FE

Given that the referer is inspected in the BE (callback), its value is lost because of the KC step. What one can do, I think, is to inspect the referer in the BE (init) instead and set it as the BE session FE successUrl. I think this can be done by modifying this part of the code, probably the OidcAuthGuard

@minottic
Copy link
Contributor

minottic commented Aug 7, 2024

I agree @bpedersen2's flow is a better implementation of OIDC, closer to PKCE

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

No branches or pull requests

4 participants