Skip to content
This repository has been archived by the owner on Jan 24, 2019. It is now read-only.

Question: Intended Flow and Configuration for Single Page Apps? #571

Open
blaskovicz opened this issue Mar 25, 2018 · 7 comments
Open

Question: Intended Flow and Configuration for Single Page Apps? #571

blaskovicz opened this issue Mar 25, 2018 · 7 comments

Comments

@blaskovicz
Copy link

Use-Case

I have a single-page react app that needs to authenticate against the Github API, and then, subsequently, use the access_token to access said api. My question: is this an intended use case, and, if so, what should the configuration look like?

Typically, these type of apps can easily authenticate with an implicit-grant; this yields them the access-token, client-side, as part of the final redirect uri which can be parsed and used.

Config and Observations

I set up oauth2_proxy in two ways and observed the following..

  1. config: nginx: proxy_pass everything to oauth2_proxy; oauth2_proxy: set upstream to spa, -set-xauthrequest=true, -pass-access-token=true, -redirect-url=/oauth2/callback.
    observation: this sets the X-Auth-Request-Access-Token head on the request made to the spa, but I would have to configure that app to inject the header back into the response / html. Also, this means all requests to the spa are proxying through the oauth2_proxy

  2. config: nginx: proxy_pass /oauth2/ and /oauth2/auth to oauth2_proxy, / to spa with auth_request set to /oauth2/auth, error_page 401 = /oauth2/sign_in, and add_header set-cookie $auth_cookie ($upstream_http_set_cookie); oauth2_proxy has no upstream, -set-xauthrequest=true, -redirect-url=/oauth2/callback.
    observation: this performs auth requests when the user isn't authenticated, and then ultimately sets the cookie (default _oauth2_proxy) such that it's visable to the spa. The problem is that when the cookie base64 decoded, no access_token is present.

Possible Solutions

If the solution isn't as simple as adjusting my config, I'm able to help with updating the proxy code. Here are a few solutions I can think of.

  • add an option to oauth2_proxy which could allow encoding the access_token, refresh_token, etc into the cookie returned from the /oauth2/callback endpoint
  • add a route to oauth2_proxy for GET /oauth2/token which could return the current json representing the access_token, refresh_token, expires, etc based on the current session.
  • In config option 1, I could have the backend server inject request headers into the response body (cookies, or javascript).
blaskovicz added a commit to blaskovicz/oauth2_proxy that referenced this issue Mar 25, 2018
GET {proxy-prefix}/token will output access_token,
refresh_token, username, email and expires in json
format, if the route is enabled (default: off).

Ref bitly#571
@blaskovicz
Copy link
Author

I started working on the cookie part, then I realized the /oauth2/token route was probably better. Submitted a pull with that feature, #572 .

@ploxiln
Copy link
Contributor

ploxiln commented Mar 25, 2018

It's mostly intended to use oauth2_proxy in front of apps that don't separately do auth, use tokens, etc. Depending on how much your app is doing with the auth token, you might not too far from having your app do the oauth2 itself.

see also #424

@blaskovicz
Copy link
Author

@ploxiln I can't have the single-page app do oauth itself since I would need the client_secret in the javascript to sign the code. Github does not support implicit grants.

@ploxiln
Copy link
Contributor

ploxiln commented Mar 26, 2018

Ah, I didn't fully understand your situation - it actually doesn't have anything to do with whether you your app has one or more pages, but rather with the fact that it's only static file hosting on the server-side, and all logic in client-side JS. So you want oauth2_proxy to be the only dynamic/logic component on the server-side, and want it to send auth info back to the client-side JS.

@blaskovicz
Copy link
Author

Exactly. I just discovered the rd param which is the final leg of the redirect journey and am trying to get an example together now using the code from my branch.

@blaskovicz
Copy link
Author

After a lot of trial and error, I got the oauth2_proxy working and serving an access_token to my single-page-app, yay! All the options below were needed for the proxy. I noticed if pass-access-token and cookie-refresh weren't both set, access_token wouldn't be encoded in the cookie and was empty on GET /oauth2/token (perhaps we should add to docs if the pull is merged, along with my sample config?).

In my docker-compose.yml I launch it via:
oauth2_proxy -http-address=http://:4180 -redirect-url=https://www.carlyzach.com/oauth2/callback -email-domain=* -provider=github -cookie-secure=true -allow-token-request=true -skip-provider-button=true -scope='user:email gist' -pass-access-token=true -cookie-refresh=1h, with oauth2_proxy_cookie_secret, oauth2_proxy_client_id, and oauth2_proxy_client_secret env variables set.

In my app I fetch /oauth2/token once logged in, otherwise I redirect to /oauth2/start?rd=<currentURL>.

In nginx, I have the following:

upstream oauth2_proxy_carlyzach_local {
  server localhost:4180;
}
upstream carlyzach_local {
  server localhost:<static-container-port>;
}
server {
 ...
  location / {
    try_files $uri @proxy;
  }
  location /oauth2/ {
    proxy_pass http://oauth2_proxy_carlyzach_local;
    proxy_set_header Host                    $host;
    proxy_set_header X-Real-IP               $remote_addr;
    proxy_set_header X-Auth-Request-Redirect $request_uri;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
  location @proxy {
    proxy_pass http://carlyzach_local;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
  ...
}

With the above config, I can get the following flow:
0) [javascript] fetch() GET /oauth2/token (401)

  1. [user-initiated] GET /functions/playground
  2. [user-initiated, button click] browser GET /oauth2/start?rd=%2Ffunctions%2Fplayground
  3. [oauth2_proxy initiated redirect] browser GET https://api.github.com/login/oauth/authorize?...
  4. [user-initiated, button click auth redirect] browser GET /oauth2/callback?...
  5. [oauth2_proxy initiated redirect] browser GET /functions/playground
  6. [javascript] fetch() GET /oauth2/token (200, json access_token)

@bhack
Copy link

bhack commented May 27, 2018

Nice! It could be nice to add this officially to the docs

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests

3 participants