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

Feature/multi tenancy #1923

Open
wants to merge 35 commits into
base: master
Choose a base branch
from

Conversation

salmanazmat666
Copy link

@salmanazmat666 salmanazmat666 commented Dec 9, 2022

Description

This PR is only meant to have a discussion around the implementation of multi-tenancy in oauth2 proxy and have comments from the maintainers about if they'll be willing to merge when the feature is fully implemented and tested. I have tested the basic Oauth2 flow with OIDC type provider and it is working as expected.

Motivation and Context

In its current form, oauth2 proxy cannot handle multiple tenants, each with different IdP configurations and requirements. Multi-tenancy is a very common design pattern in SAAS applications and issues have been raised to implement this feature on oauth2 proxy in the past like #1863 and #739. We can be sure that the requirement for this feature will increase in future.

How Has This Been Tested?

This PR is not ready to merge in its current form, and tests are broken right now.

Implementation Details

Following are the key points that'll help understand the implementation

  • Following interface has been introduced, which is responsible for loading a Tenant given its id or *http.Request
type Loader interface {
	Load(req *http.Request) (*Tenant, error)
	LoadById(id string) (*Tenant, error)
}
type Tenant struct {
	Id       string
	Provider providers.Provider
}
  • Instead of using the provider configured in the OAuthProxy struct, now the Tenant struct is loaded in a middleware using the Loader interface and injected into the request context. Later in the flow, the Tenant can be retrieved from the request context wherever needed.
  • One assumption is that the incoming request contains all the information to choose the corresponding tenant. We can have tenantId as part of the host url, query parameters, path or headers.
  • Currently, two implementations have been implemented for Tenant Loader interface.
    • One, that loads the tenants from a config file and doesn't allow to add/remove tenants dynamically. This implementation has following sample configuration
    tenantLoader:
      type: "config"
      config:
        tenantsFile: "tenants.yaml" // this file contains tenants' configuration,
        rules:
          - source: "header"
            header: "Tenantid"
            expr: (^[^\.]*) # matches everything before the first '.', so gets 'www' out from 'www.google.com'
            captureGroup: 1
          - source: "host"
            expr: (^[^\.]*) # matches everything before the first '.', so gets 'www' out from 'www.google.com'
            captureGroup: 1
    
    This implementation of tenant Loader allows you to describe a list of rules based on which we can extract tenant id from the request. Rule configuration is as follows
    type RuleSource string
    
    const (
       SourceHost        RuleSource = "host"
       SourcePath        RuleSource = "path"
       SourceQueryParams RuleSource = "query"
       SourceHeader      RuleSource = "header"
    )
    
    type RuleConfig struct {
      Source       RuleSource // which part of the HTTP request contains the tenant id
      Expr         string     // the regex expression to match and extract tenant id from the source
      CaptureGroup int        // capture group or sub-match that is actually the tenant id
      QueryParam   string     // query parameter in case source is 'query'
      Header       string     // header key in case source is 'header'
    }
    
    • Second that loads a single pre-fixed tenant no matter what id we use in loader.LoadById(string) method or what request we pass to loader.Load(*http.Request) method. This second implementation allows us to use the legacy config.
  • Using the Loader interface we can implement the the functionality that loads/removes the tenants dynamically, without actually touching the actual oauth2 proxy logic.
  • Alpha config feature is used to configure the tenant Loader interface, and everything is backwards compatible so legacy config can be used as well without configuring the tenant Loader. If the legacy config is used, we configure the tenant Loader interface and initialize it using the second implementation described above, in the LegacyOptions.ToOptions() method.

TODOs

  1. Implement new tests and update the broken ones.
  2. Update Documentation.
  3. Implement multi-tenancy in following constructs since they're also tenant specific
    • Email domains and authenticated emails validator
    • Trusted IPs validator
    • Basic Auth validator
    • SkipJwtBearerTokens option
  4. Other things, I might have missed

Final Comments

There is still lots of work to make the oauth2 proxy fully multi-tenant but we can take on things one by one which is possible because we are using alpha config which still allows for breaking changes.

This is really a great work by all the contributors and I want to thank you guys for this. Even if this doesn't get approved we'll be forking and implementing the features privately anyway.

Looking forward to hear back from maintainers. @JoelSpeed @NickMeves

Checklist:

  • My change requires a change to the documentation or CHANGELOG.
  • I have updated the documentation/CHANGELOG accordingly.
  • I have created a feature (non-master) branch for my PR.

@zeeshanshabbir93
Copy link

Hi, @JoelSpeed Are you guys interested in adding this feature to oauth2proxy anytime soon? We plan on using implementation in this PR for our internal use but we have put in extra effort to make it backward compatible & generic as we want to contribute to the community. But if this doesn't look like something that you would want to merge then we can take a private fork & maintain a custom implementation without the overhead of backward compatibility etc.
Looking forward to your response.

@JoelSpeed
Copy link
Member

We are interested in this feature, but, I'm not getting much time for the project lately.

I went travelling for a few months this year and am currently mid way through a house move.

I expect I'll have time to come to this sometime mid to late January once things have settled down in my life a little bit. Apologies for the delays folks, but please do remember, this is a passion project for me, I don't get time day to day for it

Copy link
Member

@JoelSpeed JoelSpeed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should mix tenants and providers up like this, right now, we have a single provider configuration, so the intention from previous work was that you would then have a list of providers, which is kind of what you've done, but you're calling it tenants. I'd rather we stick to a single term than mix these up.

I think this has the ground work needed, but how does it relate to existing PRs that are open? Is this an extension of those? Is it intended to replace them?

@@ -42,7 +47,9 @@ type AlphaOptions struct {
MetricsServer Server `json:"metricsServer,omitempty"`

// Providers is used to configure multiple providers.
Providers Providers `json:"providers,omitempty"`
Providers provideropts.Providers `json:"providers,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why has this been moved into a new package?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

otherwise get import cycles,

package github.com/oauth2-proxy/oauth2-proxy/v7
        imports github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options
        imports github.com/oauth2-proxy/oauth2-proxy/v7/tenant
        imports github.com/oauth2-proxy/oauth2-proxy/v7/tenant/configloader
        imports github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options: import cycle not allowed

basically ConfigLoader (a TenantLoader implementation) config depends on the provider config, which is part of pkg/apis/options package, and the options.Options depend on the TenantLoader config which depends upon the ConfigLoader config.

This is resolved by moving the provider options to a new package.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why has this been moved into a new package?

resolved. moved to the original location.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why has this been moved into a new package?

resolved. moved to the original location.

Providers Providers `json:"providers,omitempty"`
Providers provideropts.Providers `json:"providers,omitempty"`

TenantLoader tenant.LoaderConfiguration `flag:"tenant-loader" cfg:"tenant_loader"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This struct is meant to represent the configuration, please don't add runtime bits to it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are configurations, not runtime structures,

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved. removed the runtime bits from options.

Comment on lines 100 to 106
// in case of legacy options, we initilize single tenant implementation of tenant loader
l.Options.TenantLoader = tenant.LoaderConfiguration{
Type: "single",
Single: &single.Configuration{
Provider: &providers[0],
},
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kind of initialisation shouldn't be happening within the options part of the project

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed,

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved, removed the initialization logic away from the options package.

@@ -1,4 +1,4 @@
package options
package loginurlopts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why has this moved?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

otherwise get import cycles,

package github.com/oauth2-proxy/oauth2-proxy/v7
        imports github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options
        imports github.com/oauth2-proxy/oauth2-proxy/v7/tenant
        imports github.com/oauth2-proxy/oauth2-proxy/v7/tenant/configloader
        imports github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options: import cycle not allowed

basically ConfigLoader (a TenantLoader implementation) config depends on the provider config, which is part of pkg/apis/options package, and the options.Options depend on the TenantLoader config which depends upon the ConfigLoader config.

This is resolved by moving the provider options to a new package.

same thing as above, since provider options depends on loginurlopts,
we can keep the provider opts and loginurlopts in the same package though,

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved, moved the file/s back to its original location.

@@ -40,6 +42,9 @@ type Options struct {

// Not used in the legacy config, name not allowed to match an external key (upstreams)
// TODO(JoelSpeed): Rename when legacy config is removed

TenantLoader tenant.LoaderConfiguration `cfg:",internal"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't add runtime information to structs that are intended for configuration

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the configuration for tenantLoader, not the runtime construct,

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved, remved the runtime bits away from the options package.

@@ -3,14 +3,16 @@ package pagewriter
import (
"fmt"
"net/http"

"github.com/oauth2-proxy/oauth2-proxy/v7/tenant/types"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need another concept, a tenant? Why not extend the provider interface?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

responded here,

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the tenant struct and loader interfaces have been removed. Only a concep of tenant-id remains now. We extract tenant-id from the incoming request. We could have replaced tenant-id as well and only captured provider-id from the request. But in order to make the whole module multi-tenant, we must have the tenant-concept in some form. Since there are other components besides providers that need to be modified to make them multi-tenant. For example, basicAuth validator, email domain validator and SkipJwtBearerTokens option.

Comment on lines -44 to -50
// Provider based session refreshing
RefreshSession func(context.Context, *sessionsapi.SessionState) (bool, error)

// Provider based session validation.
// If the sesssion is older than `RefreshPeriod` but the provider doesn't
// refresh it, we must re-validate using this validation.
ValidateSession func(context.Context, *sessionsapi.SessionState) bool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a good idea 🤔

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would you elaborate please.
I removed this since now we're loading tenant/provider from request context and calling the provider's RefreshSession and ValidateSession methods.

tenant/config.go Outdated
@@ -0,0 +1,12 @@
package tenant
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this package not under pkg like the rest of the code?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will move to under pkg/

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved, moved the new packages under the pkg/ folder

Rules []*RuleConfig
}

// structure that is loaded from the tenant's config file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait a minute, so, once you have your provider configured, this adds a new dimension of tenants within the provider? This is very keycloak esque and doesn't translate well to other providers

Copy link
Author

@salmanazmat666 salmanazmat666 Jan 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rules are what match an incoming request to a tenant/provider. The ConfigLoader configures all the providers and keeps them in memory. Then in a middleware (pkg/middleware/tenantLoader.go), the request is matched to a tenant and then that tenant is injeted into the request context. The rules are there to do the matching. We execute rules one by one untill we find a match.
A rule (type RuleConfig struct) has following structure.

type RuleSource string

const (
   SourceHost        RuleSource = "host"
   SourcePath        RuleSource = "path"
   SourceQueryParams RuleSource = "query"
   SourceHeader      RuleSource = "header"
)

type RuleConfig struct {
  Source       RuleSource // which part of the HTTP request contains the tenant id
  Expr         string     // the regex expression to match and extract tenant id from the source
  CaptureGroup int        // capture group or sub-match that is actually the tenant id
  QueryParam   string     // query parameter in case source is 'query'
  Header       string     // header key in case source is 'header'
}

I think these rules are pretty generic and are not just related to keycloak. We just have to have tenant-id somewhere in the incoming request. That location is specified in the Source field in a rule. Right now, host, path, header, and query are supported but we can expand to include cookies as well.

func New(conf *Configuration) (*loader, error) {
tntsConf := &TenantsConfig{}

err := loadConfigFromFile(conf.TenantsFile, tntsConf)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sharding of config files is just going to make our configuration story harder IMO

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will merge the config files,

Copy link
Author

@salmanazmat666 salmanazmat666 Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolved, only once config file for now.

@salmanazmat666
Copy link
Author

I don't think we should mix tenants and providers up like this, right now, we have a single provider configuration, so the intention from previous work was that you would then have a list of providers, which is kind of what you've done, but you're calling it tenants. I'd rather we stick to a single term than mix these up.

thanks for the comment,
i can remove the "tenant" construct, so now following two new constructs will be introduced,

type TenantMatcher interface{
  GetTenantId(req *http.Request) (tenantId string, err error)
} 

type ProviderLoader interface{
// for now 'providerId' won't be used since each tenant will have only one provider but once PR #1657 is merged 'providerId' will be used
  Load(tenantId string, providerId string) (providers.Provider, error) 
}

so in a middleware, we can use TenantMatcher to get the tenantId out from http.Request, and then ProviderLoader to load the provider and inject it into request Context, so that it can be used everywhere where it is needed. What are your thoughts ?

how does it relate to existing PRs that are open? Is this an extension of those? Is it intended to replace them?

I can't find any open PRs that implement multi-tenancy. #1657 will have conflicts with this PR, but I feel they won't be huge.

@salmanazmat666
Copy link
Author

Hi @JoelSpeed,

I have updated the PR to incorporate your comments. Following changes were made

  1. Removed the TenantLoader interface. Now we have following constructs,
// tenantMatcher is responsible for matching tenants against incoming requests
type Matcher struct {}

// this function extracts tenantId from the request and returns it
func (matcher *Matcher) Match(req *http.Request) string {}

// ProviderLoader is responsible for getting Provider given a tenantId
type Loader interface {
	// id is provider id, which should be same as tenantId
	Load(id string) (providers.Provider, error)
}
  1. Besides above constructs, I added two middlewares, one that extracts tenant-id from incoming request and injects it into request context, second that loads provider and injects it into request context.
  2. Moved the new packages under the pkg/ folder.
  3. There is only one configuration file now, no more configuration sharding.
  4. Added tenant-id as part of SessionState.
  5. Implemented a decorator/wrapper over Sessionstore interface, that inserts tenant-id in the Save(http.ResponseWrite, *http.Request, *SessionState) error function and validates that the tenant-id in the SessionState and the incoming requests same inside the Load(*http.Request) (*SessionsState, error) function.
  6. Some options that were moved to different packages have been restored to original locations,
  7. Cookie names now have hash of tenant-id suffixed to them. So that we log in to the different tenants from the same browser window.

Please let me know your comments.

@axel7083
Copy link
Contributor

Hello @salmanazmat666 and @JoelSpeed,

I am very interesting in this feature, therefore to participate in the effort I opend two PR on the @salmanazmat666 repository.

Hope this can still be integrated.

@axel7083 axel7083 mentioned this pull request Mar 22, 2023
3 tasks
@salmanazmat666
Copy link
Author

Hey @JoelSpeed,

We have added more features into the PR,

  1. fixed unit tests
  2. implemented a new tenantloader, that stores provider configs in postgres and uses redis as a cache to improve on latency, this also lets us add/remove providers while the proxy is running, via a REST api,
  3. added context parameter to providerLoader's Load function,
  4. added tenantId in CSRF cookie,

You comments are highly appreciated, please have a look at the changes and let me know what you think. Since it's a large PR we can have a call to discuss to design.

Regards,

@adarshsingh2
Copy link

Tremendous Job @salmanazmat666 . Do you have any sample config file or instructions to run these changes? For eg.

  • sample tenants.yml file
  • How to pass Postgres credentials and specific configurations
  • sample alpha-config having tenantLoader and providerLoader configured
    I would appreciate it if you could provide the above details.

@salmanazmat666
Copy link
Author

Tremendous Job @salmanazmat666 . Do you have any sample config file or instructions to run these changes? For eg.

  • sample tenants.yml file
  • How to pass Postgres credentials and specific configurations
  • sample alpha-config having tenantLoader and providerLoader configured
    I would appreciate it if you could provide the above details.

Hi @adarshsingh2,
In the current state we don't need tenants.yaml, just need following alpha-config, the tenant-matcher in this config gets the tenant from a query parameter ?tid=tenant1, and the providerLoader loads the providers stored in a postgres database and uses redis as a cache, you can configure the following file according to your environment, we have implemented a REST client as well, that you can use to add/remove providers to postgres at runtime, use pkg/providerloader/postgres/NewAPIClient to get the client struct,

injectResponseHeaders:
- name: X-Forwarded-Access-Token
  values:
  - claim: access_token
- name: Authorization
  values:
  - claim: id_token
    prefix: 'Bearer '
- name: X-Auth-Request-User
  values:
  - claim: user
- name: X-Auth-Request-Email
  values:
  - claim: email
- name: X-Auth-Request-Preferred-Username
  values:
  - claim: preferred_username
- name: X-Auth-Request-Groups
  values:
  - claim: groups
- name: X-Auth-Request-Access-Token
  values:
  - claim: access_token
metricsServer:
  BindAddress: ""
  SecureBindAddress: ""
  TLS: null
providers:
  - id: tenant3
    provider: "keycloak"
    profileURL: "http://localhost:8080/auth/realms/tenant3/protocol/openid-connect/userinfo"
    validateURL: "http://localhost:8080/auth/realms/tenant3/protocol/openid-connect/userinfo"
    clientID: cmms
    clientSecret: "hf39jrh93uhd93wjd4iwj"
    scope: "openid email profile"
    loginURL: http://localhost:8080/auth/realms/tenant3/protocol/openid-connect/auth
    loginURLParameters:
    - default:
      - force
      name: approval_prompt
    oidcConfig:
      audienceClaims:
      - aud
      emailClaim: email
      groupsClaim: groups
      insecureAllowUnverifiedEmail: true
      insecureSkipIssuerVerification: true
      insecureSkipNonce: true
      issuerURL: http://localhost:8080/auth/realms/tenant3
      jwksURL: http://localhost:8080/auth/realms/tenant3/protocol/openid-connect/certs
      skipDiscovery: true
      userIDClaim: email
    redeemURL: http://localhost:8080/auth/realms/tenant3/protocol/openid-connect/token
server:
  BindAddress: 0.0.0.0:4180
  SecureBindAddress: ""
  TLS: null
tenantMatcher:
  rules:
    - source: "query"
      queryParam: "tid"
      expr: '.*'
      captureGroup: 0
providerLoader:
  type: "postgres"
  postgresLoader:
    postgres:
      host: "localhost"
      port: 5432
      database: "oauth2-proxy"
      schema: "oauth2_proxy"
      user: "admin"
      password: "admin"
      maxConnections: 50
      sslMode: "disable"
      sslRootCert: ""
      sslCrl: ""
      sslCert: ""
      sslKey: ""
    redis:
      connectionURL: "redis://localhost:6379"
      password: ""
      useSentinel: false
      sentinelPassword: ""
      sentinelMasterName: ""
      sentinelConnectionURLs:
      useCluster: false
      clusterConnectionURLs:
      cAPath: ""
      insecureSkipTLSVerify: false
      idleTimeout: 60
      expiry: 60000000000
      prefix: "oauth2-proxy-"
    aPI:
      host: "0.0.0.0"
      port: 8080
      pathPrefix: ""
      timeout: 60000000000

@salmanazmat666 salmanazmat666 force-pushed the feature/multi-tenancy branch from 70fdbbb to a9052bc Compare April 19, 2023 10:02
@zeeshanshabbir93
Copy link

Hi @JoelSpeed , it would be great if you could provide feedback on the PR so that we can merge it. It been a bit difficult for us to keep this PR updated. Lets finalize a way forward if something else needs to be done. We want to merge it by end of May latest. There has been a lot of interest by community in this feature. But unfortunately due to work pressure, it won't be possible for us after May to keep this one updated & in sync with our private fork.

@salmanazmat666 salmanazmat666 force-pushed the feature/multi-tenancy branch 3 times, most recently from d92e058 to ba59cbe Compare April 28, 2023 12:23
@imranshifa imranshifa force-pushed the feature/multi-tenancy branch 11 times, most recently from 9d8dcb2 to d053744 Compare April 29, 2023 10:27
@tuunit
Copy link
Member

tuunit commented Mar 27, 2024

@salmanazmat666 happy to see you back at it

@salmanazmat666
Copy link
Author

salmanazmat666 commented Apr 3, 2024

@tuunit I have updated the PR and following are the main things I did,

  • - Refactoring of naming schema: tenants -> providers

  • - Refactoring of package structure tenant -> providers, providerloader -> providers/loader

  • - Fixing all existing unit tests

  • - Check context handling in sessions.go > validateRedisSessionStore(o *options.Options)

  • - Remove support for legacy (toml) flags and config. (We still have necessary config in legacy config file like cookie settings etc, so cannot remove that unless we migrate all configs to alpha.)

  • Besides, I have also created files for local environment setup to demo the multi-tenancy feature, we can now use make multi-tenancy-up to bring up the necessary services that are already configured for multi-tenancy support.

  • I have also templatize the cookie domains, so now we can set different domains on cookies for different tenants/providers if we want, e.g., tenant1.example.com for provider 1 and tenant2.example.com for provider 2. We could also set the domain to be .example.com for all providers but that will cause all cookies to be sent to each logged in tenant within one browser (very low chance of happening but still possible) and we might get 413 Request Too Large if too many cookies are sent in one request.

Please have a look at the changes and let me know if you want any other change.

Regards,

Copy link
Member

@tuunit tuunit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try to do a full review again soon and do some local testing as well.

@@ -14,6 +14,14 @@ alpha-config-up:
alpha-config-%:
docker compose -f docker-compose.yaml -f docker-compose-alpha-config.yaml $*

.PHONY: multi-tenancy-up
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we rename this to:

Suggested change
.PHONY: multi-tenancy-up
.PHONY: multi-provider-up

Copy link
Member

@tuunit tuunit Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and rename all files (replace multi-tenancy with multi-provider)

@tuunit
Copy link
Member

tuunit commented Apr 3, 2024

Thanks for putting all the work in 🥳

@salmanazmat666
Copy link
Author

Thanks for putting all the work in 🥳

Hey @tuunit , did you get a chance to review the PR ?

@robertlagrant
Copy link

@salmanazmat666 it might be worth making progress by merging in the base branch?

Copy link
Contributor

github-actions bot commented Aug 3, 2024

This pull request has been inactive for 60 days. If the pull request is still relevant please comment to re-activate the pull request. If no action is taken within 7 days, the pull request will be marked closed.

@alexdutton
Copy link

Hi @salmanazmat666. I'm interested in this feature too, and thank you for all your work so far!

I've given it a go, and wanted to check my understanding.

  1. At the moment, am I right in thinking that every request (not just within the authentication flow) needs to be mapped to a provider, otherwise oauth2-proxy will return a 401?
  2. There's currently a config provider loader and a single provider loader — could this be simplified to just the config loader with an implicit default of the first provider?

Our use case is to support multiple providers for a single upstream service, all from a single public service URL. It'd be good (at least from our perspective) if the provider can be specified as a query parameter to /oauth2/start/oauth2/sign_in and then for the provider ID to be held in OIDC state and then the session. We only protect our API's login endpoint, so requests to all other paths are unauthenticated.

I've started experimenting with adapting what you've got so far to implement the suggestion above, but don't want to go off in another direction to where you're headed. If you'd like to catch up on this that'd be great!

All the best, ~ Alex

@stleon
Copy link

stleon commented Dec 22, 2024

Thank you for this amazing product and all the incredible work you've done — it's truly impressive! 🙌

Regarding multi-tenancy functionality: quite some time has passed since this idea was discussed, and it would be fantastic to see it implemented. Are there any updates on this topic?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file docs go high-priority provider tests
Projects
Development

Successfully merging this pull request may close these issues.