This is a proof of concept for having multiple separated services, which each one is protected by a different Oauth2-proxy instance using the same Oauth2 configuration.
You need to have installed in the machine that will run it.
- Docker compose
- Go. Used v1.20, but is should work with a previous one.
- A machine with a public IP and with the port 80 and 443 accessible from the internet, and administrative access rights (e.g. sudo). We needs those ports because it retrieves certificates for the domains from Let's Encrypt.
You need to have access to:
- A Google Cloud account with right to create Oauth2 credentials for applications.
- A domain name with access to its DNS management to create A registers. You can use a sub-domain.
NOTE this proof of concept uses Google Cloud Oauth2 provider, you should be able any of the providers accepted by Oauth2-proxy, however, you will have to change the docker-compose.yml file.
-
Create the A registers for the base (sub-)domain that you are going to use setting the public IP of the machine that will run this. Create one A register for the base (sub-)domain. Create one A register for the following sub-domain of the base (sub-)domain:
functional. For examplefunctional.my-base-domain.test, etc. -
Create a Google Oauth2 credentials for applications. You can follow the Oauth2-proxy documentation.
You have to add the 2 domains to the Authorized Javascript origins (e.g.
https:://my-base-domain.test,https://functional.my-base-domain.test).You also have to add 2 domains to the Authorized redirect URIs with the path
/oauth2/callback(e.g.https://my-base-domain.test/oauth2/callback,https://functional.my-base-domain.test/oauth2/callback). -
Take note of the client ID and client secret of the Google Oauth2 credentials.
-
Fill the values of environment variables in the
envfile.IFRAIXEDES_EMAIL_DOMAINenvironment variable sets the e-mail domain that you're service authorize, if you want any, use*.
In one terminal session run with administrative rights (e.g. sudo) from the same folder where you
have cloned this sources docker compose --env-file env up.
In another terminal run IFRAIXEDES_BASE_DOMAIN=my-base-domain.test go run ..
At this point you can navigate to the service that shows the list of services to access which is
https://my-base-domain.test. Oauth2-proxy will present you the login screen and if it successfully
you'll be redirected to the "selector" service.
Click for accessing the "functional" service. You can access without having to log in again if you
navigate to its specific URL https://functional.my-base-domain.test.
If you want to see if you can access to one of the services without having a valid authentication
session, logout navigating to https://my-base-domain.test/oauth2/sign_out and try to access to the
functional service https://functional.my-base-domain.test. You'll see that the Oauth2-proxy will
present you the login screen; if you login successfully you'll be able to access to the other
services without having to login again as before.
I wanted to have a bunch of services:
- Only accessible after an Oauth2 login.
- Running in different regions, but served under the same base (sub-)domain.
- Each service with it's Oauth2-proxy, for not having a single one and having to peer networks and no single point of failure for the authentication service.
- All of them accessible with the same Oauth2 credentials.
- Users must not have to login again once they have a successful service in one of them, although I wanted mostly to access through the "selector" service.
Let's show that in a diagram
sequenceDiagram
actor not authenticated user
actor authenticated user
participant Oauth2-proxy selector service
participant Google Oauth2
participant Selector service
participant Oauth2-proxy functional service
participant Functional service
not authenticated user->>Oauth2-proxy selector service: https://my-base-domain.test
Oauth2-proxy selector service->>Google Oauth2: Redirect
activate Google Oauth2
Google Oauth2->>not authenticated user: ask for credentials
not authenticated user->>Google Oauth2: send credentials
alt invalid credentials
Google Oauth2->>Oauth2-proxy selector service: login failed
Oauth2-proxy selector service->>not authenticated user: permission denied
else valid credentials
Google Oauth2->>Oauth2-proxy selector service: login successful
Oauth2-proxy selector service->>authenticated user: send provider credentials & redirect
authenticated user->>Oauth2-proxy selector service: request callback with provider credentials
Oauth2-proxy selector service->>authenticated user: send service credentials & redirect
end
deactivate Google Oauth2
authenticated user->>Oauth2-proxy selector service: request with service credentials
Oauth2-proxy selector service->>Selector service: redirect
authenticated user->>Selector service: select functional service
authenticated user->>Oauth2-proxy functional service: request with service credentials
Oauth2-proxy functional service->>Functional service: forward request
Functional service->>Oauth2-proxy functional service: send content
Oauth2-proxy functional service->>authenticated user: forward response
In this proof of concept the "selector" service sends a cookie to the client so the client is automatic redirected to the "functional" service once the accessed to the "functional" service through it. The concept was to emulate a "selector" service with several "functional" services and when one is chosen, the user is always redirected to that one unless that they explicitly want to select another one. This cookie isn't a requirement.
NOTE that this proof of concept only specifies one "functional" service for simplicity, but you can crate as many "functional" services with their own Oauth2-proxy as you need.
When I was doing this proof of concept, I realized:
- Oauth2-proxy
--cookie-samesitecannot be set tostrictbecause, otherwise, Google Oauth2 doesn't work. - Oauth2-proxy
--cookie-namecan be used to use the__Secure-prefix (e.g.--cookie-name=__Secure-Oauth2), but not with the__Host-prefix because we need to set the domain to the cookie to make the single login to work. See cookie prefixes. - All your services ("selector" and "functional") must be hosted under the same base (sub-)domain. This is needed because otherwise the authentication cookies won't be send across the different services because of the browser security restrictions.
This proof-of-concept doesn't contain the configurations needed for "Restrict auth to specific Google groups on your domain. (optional)", however, I used it for tinkering with that set-up and I highlight a few things that I consider that are good to bear in mind despite some of them may be obvious for someone:
- You need to pass the following arguments or their corresponding environment variables:
google-admin-email,google-service-account-json, andgoogle-group(this one multiple times if you're designing more than one group). - NOTE at the time that I set this up, the corresponding environment variable for the
google-groupargument should be according to the documentationOAUTH2_PROXY_GOOGLE_GROUPS(trailingS), however, it'sOAUTH2_PROXY_GOOGLE_GROUP(no trailingS). See oauth2-proxy/oauth2-proxy#2201 - Passing multiple values, when using arguments, you must pass them multiple times, when using
environment variables, you have to separate the values with commas (i.e.
,). - The proxy sends the Google groups that are configured and the user belongs to through the header
X-Forwarded-Groupsseparated with commas when there is more than one. For example:- Proxy has configured the groups
managers@example.test,engineers@example.test, andemployees@example.test. - The user belongs to
engineers@example.testandemployees@example.test. - Proxy sends to the upstream services
X-Forwared-Groups: engineers@example.test,employees@example.test.
- Proxy has configured the groups
- The proxy denies the access to users who don't belong to any of the configured groups.