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

Improve security on payment server access token generation using intermediate codes #1696

Open
lmorchard opened this issue Jul 8, 2019 · 7 comments

Comments

Projects
None yet
4 participants
@lmorchard
Copy link
Member

commented Jul 8, 2019

For issue #881, we implemented auth token generation in content server to enable API calls in payment server pages. This works, but is not ideal for many reasons.

An incremental improvement to the security situation would be to supply the payment server web app with a code it can use to fetch an access token. That way, we can enforce various constraints on the transition from content server to payment server - e.g. same IP, etc.

/cc @shane-tomlinson for more erudite details on this.

@6a68

This comment has been minimized.

Copy link
Member

commented Jul 9, 2019

@shane-tomlinson Let's find a time to chat about the right way to implement this. Could this be a spot to use a JWT?

@shane-tomlinson

This comment has been minimized.

Copy link
Member

commented Jul 9, 2019

Could this be a spot to use a JWT?

I don't think so, JWTs are currently only used for access tokens. Here I'd love to use the full prompt=none flow, but that's not ready. An intermediate step that's a bit better than what we have now is to redirect to the payments page with an authorization code instead of an access token. At least authorization codes are single use.

@shane-tomlinson

This comment has been minimized.

Copy link
Member

commented Jul 9, 2019

Could this be a spot to use a JWT?

Though, there is this IETF spec for using JWTs as authorization grants.

@lmorchard

This comment has been minimized.

Copy link
Member Author

commented Jul 10, 2019

A question that occurred to me over in #1694: Short of skipping straight to a full OAuth RP implementation (#1695), is there a way we can implement this issue such that the payment pages can refresh the token?

@shane-tomlinson

This comment has been minimized.

Copy link
Member

commented Jul 11, 2019

A question that occurred to me over in #1694: Short of skipping straight to a full OAuth RP implementation (#1695), is there a way we can implement this issue such that the payment pages can refresh the token?

When generating the authorization code, request "access_type=offline". When trading the code for an access token at /token, a refresh token will also be returned. See

code: validators.authorizationCode.required(),
. The refresh token can then be used to get new access tokens:

@lmorchard

This comment has been minimized.

Copy link
Member Author

commented Jul 11, 2019

Ah, okay - I was thinking we would be using something bespoke and not a more normal authorization code. That probably makes a lot of things more sane.

@6a68

This comment has been minimized.

Copy link
Member

commented Jul 12, 2019

Shane and I chatted about this today, and the way forward isn't totally clear, but it seems like we're leaning towards a cookie shared between payments and content servers, as a secure, least-effort hack until Skyline ships. Shane has a branch (as always :-). I'm not working on this right now, but may pick it up next week.

shane-tomlinson added a commit that referenced this issue Jul 12, 2019

feat(payments): Use PKCE to access the payments page.
This is a bit strange, but here we go.

We aren't doing a traditional OAuth flow here where we start at the RP (payments page)
to set up `state` and PKCE parameters. To do so cleanly would require support for
`prompt=none`, which we do not yet have. See #640, PR #1150.

Instead, we secure the flow and prevent CSRF by relying on cookies.
We set up state and the PKCE parameters on the content server, then send
the params to the content-server backend to store in an HTTP only cookie on
accounts.firefox.com scoped to /payments-pkce.

Still on the content server, we then generate an OAuth code using the
generated state/PKCE params, and redirect to the payments page.

The payments page gets the OAuth code/state out of the URL.
It then fetches the state/PKCE params from the content-server
by making a GET request to /payments-pkce. The content-server
ensures the request comes from the payments domain, reads
the cookie, and then returns the PKCE/state info.

The payments server now has all the stuff it needs to trade
the OAuth code for an access token. It makes the call to the
OAuth server's /token endpoint with the code from the URL,
and the state and PKCE parameters returned from the backend.

A refresh token is also returned enabling fetches of new
access tokens if the current one expires.

issue #1696
issue #1694

shane-tomlinson added a commit that referenced this issue Jul 12, 2019

feat(payments): Use PKCE to access the payments page.
This is a bit strange, but here we go.

We aren't doing a traditional OAuth flow here where we start at the RP (payments page)
to set up `state` and PKCE parameters. To do so cleanly would require support for
`prompt=none`, which we do not yet have. See #640, PR #1150.

Instead, we secure the flow and prevent CSRF by relying on cookies.
We set up state and the PKCE parameters on the content server, then send
the params to the content-server backend to store in an HTTP only cookie on
accounts.firefox.com scoped to /payments-pkce.

Still on the content server, we then generate an OAuth code using the
generated state/PKCE params, and redirect to the payments page.

The payments page gets the OAuth code/state out of the URL.
It then fetches the state/PKCE params from the content-server
by making a GET request to /payments-pkce. The content-server
ensures the request comes from the payments domain, reads
the cookie, and then returns the PKCE/state info.

The payments server now has all the stuff it needs to trade
the OAuth code for an access token. It makes the call to the
OAuth server's /token endpoint with the code from the URL,
and the state and PKCE parameters returned from the backend.

A refresh token is also returned enabling fetches of new
access tokens if the current one expires.

issue #1696
issue #1694
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.