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

Authentication with multiple JWT tokens from the same browser session #387

Closed
damijank opened this issue Nov 2, 2020 · 14 comments · Fixed by #655
Closed

Authentication with multiple JWT tokens from the same browser session #387

damijank opened this issue Nov 2, 2020 · 14 comments · Fixed by #655
Labels
pinned The issue must not be marked as stale

Comments

@damijank
Copy link

damijank commented Nov 2, 2020

I have a question about serving multi-tenant / multi-client apps.

We have a setup like this one:

  • a single multi-tenant API Platform backend (on paas.org)
  • a single Mercure hub, which is on the same domain as PaaS
  • a multi-client-enabled CMS (on tenant.org domain)
    • the CMS is allowed to administrate all the clients for their tenant
  • multiple clients (websites) are on their own domains, too (client-one.org, client-two.org...)

For reference:
in this setup the tenants (our customers) are parents of clients (our customers have multiple websites, mobile apps, etc.).

When working just with a single client, we ran into the problem that the client and the hub are not on the same second-level domain. I've seen #17 and the polyfill solution is not what we would want since that falls back to plain ol' HTTP and as such is not as "native" and performant as the real native EventSource.
We could/should fix this with the PaaS setting the mercureAuthentication cookie at the time of response to OAuth, and since the hub is on the same domain this should work... almost, I'm not going into detail with OAuth scopes. But still, even that is not the whole story. 🙂

When switching to a second client in the CMS, it has to fetch a different JWT from PaaS (which would reset the cookie), then the CMS opens a new EventSource connection to the hub with the new JWT.
With the solution when the PaaS sets the mercure cookie, we here loose the EventSource connection for the first client.
If we exposed the header to JS, it still wouldn't work because JS can not set a cookie for another domain (it's not a subdomain, so a Domain attribute isn't suitable either).

We want to enable the customer to have multiple tabs with CMS open in the same browser session, each administering it's own client. With cookie-based mercure authentication we can not have each tab receiving it's own SSEs because the cookie can only be set to a single value.

I've found this #150 and I understand the goal of securing the JWT. But in the end, with HTTPS the URI itself is encrypted in transport. I'd suggest to add a query parameter as a possibility for mercure authentication. The current limitation is restrictive enough so we're not able to accomplish what we've aimed for.

I'm open to any other suggestions which would resolve this problem, without the need to transport the JWT in a query parameter. Please advise!

@damijank
Copy link
Author

For now, I've made a workaround via a reverse-proxy configuration in my nginx

location /.well-known/mercure {

    ...

    # TODO: Reverse-proxy workaround for: https://github.com/dunglas/mercure/issues/387
    set $fw_cookie "";
    if ($arg_mercureauthorization) {
        set $fw_cookie mercureAuthorization=$arg_mercureauthorization;
    }
    proxy_set_header Cookie $fw_cookie;
}

@divine
Copy link
Contributor

divine commented Dec 5, 2020

@dunglas 👋,

Given a lot of issues with event-source-polyfill, is there any possibility that this feature* might come with v0.11?

* Defining JWT key in URL parameters. It's still secure and tampered data can be easily detected. The only limitation is increased URL size.

Thanks!

@dunglas
Copy link
Owner

dunglas commented Jan 8, 2021

I'm not against supporting this feature in the hub. But I'm not sure if it should be in the spec. Maybe should we make supporting this feature only as a "MAY" in the spec?

@dunglas
Copy link
Owner

dunglas commented Jan 17, 2021

I suggest to add support for query parameters in spec. We can adapt the wording of the OAuth spec: https://tools.ietf.org/html/rfc6750#section-2.3

@divine
Copy link
Contributor

divine commented Jan 18, 2021

I suggest to add support for query parameters in spec. We can adapt the wording of the OAuth spec: https://tools.ietf.org/html/rfc6750#section-2.3

@dunglas 👋, that would be really great!

Thanks!

@damijank
Copy link
Author

@dunglas nice! I'm looking forward for an update.
Thanks!

@stale
Copy link

stale bot commented Mar 20, 2021

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 wontfix This will not be worked on label Mar 20, 2021
@stale stale bot closed this as completed Mar 27, 2021
@dunglas dunglas added pinned The issue must not be marked as stale and removed wontfix This will not be worked on labels Apr 29, 2021
@dunglas dunglas reopened this Apr 29, 2021
@dunglas
Copy link
Owner

dunglas commented Sep 17, 2021

I just opened a PR to add support for this to the spec, could you review please? #562

@damijank
Copy link
Author

damijank commented Sep 20, 2021

Seems legit, as you have proposed, the hubs *MAY* support the query parameters method.

Also, I've had an idea while reviewing these changes: How about alowing the subscriber to specify which cookie is to be used for authentication? I'm gonna quote a portion of the initial comment:

We want to enable the customer to have multiple tabs with CMS open in the same browser session, each administering it's own client. With cookie-based mercure authentication we can not have each tab receiving it's own SSEs because the cookie can only be set to a single value.

This is only true if only one specific cookie is used for authentication of subscribers.

If the subscriber could tell the hub to read the JWS from any specific cookie at the time the connection is established, via a query parameter (i.e. cookie-name, just like for the publishers), then having different clients in one browser session would be possible by using a unique cookie for each of them. Resolving the authentication cookies' name would be up to the implementor. One solution, in our case, could be naming the cookie as the client's name or UUID (or some other unique clear-text hash per client of sorts).

The above specific cookie "method" would preserve the security level you were pursuing from the beginning while still adding the multi-client feature. I'm sorry I did not think of this earlier.

I'm not saying you should completely remove the query authentication method. It is really a convenient way to implement it, and the *MAY* and not recommended parts are emphasized enough not to encourage its usage.

@divine
Copy link
Contributor

divine commented Sep 20, 2021

Hello,

Query parameter authentication was requested (by me as well) only because of the lack of native support for authentication headers in SSE.

That's why we're using event-source-polyfill which has some random issues (timeouts and etc) and it's not native overall.

How about alowing the subscriber to specify which cookie is to be used for authentication?

Do you have any example specs that do the same thing?

For me specifying cookie name in query parameters doesn't make sense. It will make things messy and what I have seen in all eco systems is that cookie names are configured once and never touched again.

Never thought that cookie names might become a dynamic one...

Thanks!

@damijank
Copy link
Author

Hey,

no, I have no specs nor implementations of such behaviour. As said, i've had the idea just when I was reading through the PR where a cookie-name parameter (although in the Link header) has been added for publishers.

Agreed, it would be a highly uncommon practice to do so. But nevertheless, functionality would be there and it would preserve the security (i.e. not expose the JWS in the URL).

@netz0
Copy link

netz0 commented Mar 8, 2023

@damijank Did you find a solution to this? It seems from the docs that passing the authentication on the URL query does work now:
https://mercure.rocks/spec#authorization

But as others said, no ideal security wise since the token will be logged in requests.

One solution, assuming you are controlling the domains, is creating a subdomain in the DNS for those names that point to your hub. Cookies in the subdomain should be supported.

@netz0
Copy link

netz0 commented Mar 8, 2023

For now, I've made a workaround via a reverse-proxy configuration in my nginx

location /.well-known/mercure {

    ...

    # TODO: Reverse-proxy workaround for: https://github.com/dunglas/mercure/issues/387
    set $fw_cookie "";
    if ($arg_mercureauthorization) {
        set $fw_cookie mercureAuthorization=$arg_mercureauthorization;
    }
    proxy_set_header Cookie $fw_cookie;
}

Also looking to run it as reverse proxy, in the docs it says that authorization is only supported using HTTPS, does that apply as well using a reverse proxy? The proxy would handle the HTTPS and certificate connection, are you still running the hub with HTTPS internally if that is the case?

@damijank
Copy link
Author

I have in the meantime moved away from the project for which this feature has been requested. But this was the (a) solution to our use case... so yeah, I found it even before asking for the feature... 🙂

As the hub has implemented the support for query parameter authorization, I have notified my successor there... but did not follow up with them if it was implemented though.

One solution, assuming you are controlling the domains, is creating a subdomain in the DNS for those names that point to your hub. Cookies in the subdomain should be supported.

If one would add a separate subdomain for every client, then this would be okay... but that was not the case in the project, an arbitrary number of clients can be added, programmatically, so we needed a dynamic solution.

Also looking to run it as reverse proxy, in the docs it says that authorization is only supported using HTTPS, does that apply as well using a reverse proxy? The proxy would handle the HTTPS and certificate connection, are you still running the hub with HTTPS internally if that is the case?

Yes, it applies. As you said yourself, the hub requires a direct HTTPS connection when authorization is used. The reverse proxy only "moves" the token from the query parameter to the cookie. Then the request is forwarded to the hub via HTTPS.

If you have a similar scenario to that what I have described (a single hub domain, manage multiple clients from a single browser session), and you would really want to protect your JWT, you could do some "black magic" like I described in a comment above

  • generate a uniquely identifying cookie name for each client (i.e. ma_client1, ma_client2, etc...)
  • store the distinct JWTs for each client in a secure cookie with it's respective name
  • from the browser send the cookie name in some query parameter (all cookies will be sent along, too)
  • make the reverse proxy move the corresponding token from that cookie to the mercureAuthorization cookie (or even the Authorization header)

It could be something like

location /.well-known/mercure {
    ...
    # More secure Reverse-proxy workaround for: https://github.com/dunglas/mercure/issues/387
    set $fw_cookie "";
    if ($arg_cookieNameToUse) {
        set $pattern "~*$arg_cookieNameToUse=(?<tokenFromCookie>[^;]+)";
        map $http_cookie $token {
            $pattern "$tokenFromCookie";
        }
        set $fw_cookie mercureAuthorization=$token;
    }
    proxy_set_header Cookie $fw_cookie;
}

I did not test this, it may not work as it is, I wrote from top of my head. I don't know if we can use a variable for the map pattern. You may find a more elegant solution, but this kinda demonstrates it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pinned The issue must not be marked as stale
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants