Hey πŸ‘‹ I am trying to implement multi tenancy (f...
# support-questions
n
Hey πŸ‘‹ I am trying to implement multi tenancy (for our cloud implementation; single instance for all customers). This is the implementation I have in mind: 1. An organisation entity is linked to an OpenID Connect provider 2. A link is generated for logging in via a specific OpenID Connect provider (
https://example.com/auth/org?id=oidc_provider_id
) 3. People visiting this link start the OAuth2 OIDC login flow with the organization specific OIDC provider 4. Users that newly register/log in are automatically added to the specific organization I am now trying to figure out how to handle this on the SuperTokens Next.js side. Within the
getServerSideProps
function of the
auth
route I can load the specific integration (id, secret and issuer url) from the database. However, it is unclear to me on how I would then utilize this information with the SuperTokens SDK (since it is a singleton that is initialized once and cannot be initialised per request πŸ€” . Do you have any pointers here?
r
hey @n1ru4l
so essentially, on the frontend, you want to set the provider list in the thirdparty recipe based on the
id
in the query params.
On the backend, you can initialise all of the providers and dynamically load their config during API calls. You want to override the signinup and getauthorisationurl API on the backend to fetch the config of the tenantID from your db. You can get the teantId from the request object somehow. Then set the config in the the userContext before calling the original implementation. This userContext with the config will be available in your custom provider's definition (in the
get
function)
n
> On the backend, you can initialise all of the providers and dynamically load their config during API calls. Since providers are added/removed dynamically and we can potentially have hundreds that does not seem like a nice solution πŸ€”
oh wait i misunderstood
okay let me try this
r
Ok
I could show it to you on a call sometime tomorrow or on Thursday
n
let me try how far I get today - if not lets schedule a call πŸ™‚
r
Sounds good
n
thank you ❀️
Okay it seems like my
authorisationUrlGET
override is not used at all πŸ€” I added a simple override with a console.log, and it never shows up The HTTP call (initiated by supertokens react) to
http://localhost:3000/api/auth/authorisationurl?thirdPartyId=org%7Ckekeke
simply gives me
{"message":"The third party provider org|kekeke seems to be missing from the backend configs."}
Copy code
const getOIDCOverrides = () => {
  console.log('it is applied for sure');
  const override: ThirdPartEmailPasswordTypeInput['override'] = {
    apis(originalImplementation) {
      return {
        ...originalImplementation,
        async authorisationUrlGET(input) {
          console.log(input);

          return originalImplementation.authorisationUrlGET!(input);
        },
      };
    },
  };

  return override;
};
I added a console.log for the
apis
function - it seems like that one is not invoked
r
Oh yea. You need to add a provider with that id in the provider list in the backend for it to work
n
πŸ€”
Any workaround if we don't know the list of oidc providers upfront? πŸ˜„
r
Hmm. On the backend you would need to add all the ones that your app can integrate with
n
Uhmm, but we generate the id on the fly as an user adds an integration to the org
r
Hmm I see. But how will the SDK know how to integrate with that org without you specifying info for it - like the auth exchange url, or how to get the profile info from that org
n
it will load it from the db
we store client id, secret (encrypted) and issuer url in the db
r
Yea that’s fine. But the actual integration with that info is on a per provider basis. For example, the way you get the user profile from Microsoft AD is different compared to something like LinkedIn
n
The userinfo is received via the OpenID Connect UserInfo endpoint
r
You would also need to store the mapping of id token to user info then. Like which field in the id token corresponds to the email (for example)
n
Yeah, but that is not really related to this issue, no? It is something that must also be solved for sure - but I cannot get to this point yet
r
Right yea. Ok so I have an idea
Essentially, you create one generic open id client, which some is like β€œgeneric”
On the frontend and backend
And then, on the backend, you not only load the client id, client secret etc config, but also the auth urls for that provider from the db
And then dynamically inject all of those in the generic provider on the backend
Including info about how to get profile info from the id token
n
Oh so the organization specifc auth id would be an additional query parameter? πŸ€”
r
Yea. A custom param in the request
n
Copy code
export const startAuthFlowForOIDCProvider = async (oidcId: string) => {
  let authUrl = await getAuthorisationURLWithQueryParamsAndSetState({
    providerId: 'org',
    authorisationURL: `${env.appBaseUrl}/auth/callback/org`,
  });

  const url = new URL(authUrl);
  url.searchParams.set('oidc_id', oidcId);

  authUrl = url.toString();

  window.location.assign(authUrl);
};
Is it possible to add a query parameter to the callback url?
or will the oidc provider complain (as it also adds query parameters)
r
You want to add info to the state in this case.
What’s the use case of adding extra info?
n
oh so org is the generic provider - but also we need to know which org it is, right? So that is why we also need to somehow pass the open id connect provider id
r
I see. When handling the callback. Makes sense!
Oh and you may also want to modify the value of thirdpartyid in the signinup function to add the actual provider id to the input value of thirdpartyid so that in the db, users are not all shared for the same thirdpartyid
n
@rp Okay so the biggest question really is how I could provide/inject the organization specific id into all steps in a "secure" way that would not allow an attacker to inject a random one
Into
thirdPartySignInUpPOST
and
authorisationUrlGET
to be precise
for
authorisationUrlGET
just relying on the
referer
headers seems to be safe enough...
but for
thirdPartySignInUpPOST
I need a safe way πŸ€”
r
you can use the state. Also, even if an attacker injects a different org specific ID in
thirdPartySignInUpPOST
, it won't work cause the auth code exchange will fail
n
Potentially stupid question... Where do i access the state πŸ˜“
r
Well yea. You will need to add that as a custom prop in the request body from the frontend and access it on the backend
n
Copy code
authorisationRedirect: {
  // this contains info about forming the authorisation redirect URL without the state params and without the redirect_uri param
  url: `${oidcConfig.domain}/authorize`,
  params: {
    client_id: oidcConfig.clientId,
    scope: 'openid email',
    response_type: 'code',
    redirect_uri: `${env.appBaseUrl}/auth/callback/oidc`,
    state: oidcConfig.id,
  },
},
so here i pass the state to the oicd provider (within a custom TypeProvider) on the backend
how does okta/any other oidc provider give me back that state?
r
Query param in the callback URL
n
Does SuperTokens by default add a state query parameter?
Seems like I would need a way of overriding
generateStateToSendToOAuthProvider
figured it out
I think there is a bug with the
getAuthorisationURLFromBackend
function override
options
.
Copy code
getAuthorisationURLFromBackend(input) {
  const maybeId: unknown = input.userContext['oidcId'];

  if (typeof maybeId === 'string') {
    return originalImplementation.getAuthorisationURLFromBackend({
      ...input,
      options: {
        preAPIHook: async options => {
          alert('NANI');

          const url = new URL(options.url);
          url.searchParams.append('oidc_id', maybeId);

          return {
            ...options,
            url: url.toString(),
          };
        },
      },
    });
  }
  return originalImplementation.getAuthorisationURLFromBackend(input);
The
preAPIHook
seems to never be invoked
r
@nkshah2 can have a look at this sometime tomorrow
n
worked around this by instead having a global
preAPIHook
override
r
Thanks. @nkshah2 can have a look at this to see if there is a bug in the SDK
@rp Thank you so much for all the help today! 😊
r
Happy to help :)! Thank you as well for giving us insight into what needs improving
n
Hi @n1ru4l can I see how you are using
getAuthorisationURLFromBackend
. I tried setting up a sample project and the pre api hook works correctly for me
n
@nkshah2 Did you test it on Next.js?
could this be related to a dependency of supertokens being resolved to the wrong version?
n
Ill setup a sample app to test with NestJS, in the meantime what version of supertokens-web-js are you using?
I have a minimal reproduction
I call
getAuthorisationURLWithQueryParamsAndSetState
n
Ah thanks for that ill have a look
n
It is not next.js related it seems
n
Right I can reproduce the bug and ill be working on a fix for this, if you like you can open an issue about this in
supertokens-auth-react
to keep track of progress on this
And yeah its not NextJS specific
n