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

How can I configure the issuer, clientId, and clientSecret at request time while still abstracting? #44

Open
devbytyler opened this issue Dec 1, 2022 · 4 comments

Comments

@devbytyler
Copy link

devbytyler commented Dec 1, 2022

Help! Been stuck on this for hours.... 😩

I have a requirement where each customer has unique credentials for issuer, clientID, and clientSecret. (Okta)

I don't know which credentials to configure on the strategy until the user tells me which tenant they are trying to log in to. At that point I can look up the tenant and grab the credentials. Therefore I can't create the strategy until after that point.

This is the problem I'm having (and it might be due to me not understanding typescript):
I import my instance of the authenticator:

export const authenticator = new Authenticator<string | undefined>(storage, {
  sessionKey: 'userEmail',
});

Then in my action function I create the strategy and register it on the authenticator:

import {authenticator} from 'auth.server.tsx';

export async function action({ request }: ActionArgs) {
  const user: User = getUserFromDB();
  if (user.company.oktaClientId && user.company.oktaClientSecret && user.company.oktaDomain) {
    let oktaStrategy = new OktaStrategy(
      {
        issuer: `https://${user.company.oktaDomain}/oauth2/default`,
        clientID: user.company.oktaClientId,
        clientSecret: user.company.oktaClientSecret,
        callbackURL: `${process.env.FRONT_URL}/auth/okta/authorization-code/callback`,
      },
      async ({ accessToken, refreshToken, extraParams, profile }) => {
        // Do stuff with profile
      }
    );
    authenticator.use(oktaStrategy);
    try {
      return authenticator.authenticate('okta', request);
    } catch (error) {
      //....
    }
  }
}

As soon as I try to call authenticator, i get the error:

strategy okta not found

which leads me to think the registration isn't happening.

I HAVE gotten this all to work by defining everything inline in the action function. (i.e. creating the authenticator, creating the strategy, registering the strategy, and calling authenticate.) Seems like something about importing it from another module breaks it. While it worked, I would much rather be able to remove all the authenticator logic from my login route, including configuring the strategy.

Do I need to have some kind of singleton for my Authenticator?

Any help would be very appreciated.

@devbytyler
Copy link
Author

devbytyler commented Dec 2, 2022

closing this as I found my problem was something else. Still haven't found a clean way to dynamically set the secrets at request time

@sergiodxa
Copy link
Owner

What I would do is to create the Authenticator and Strategy on the server and identify the tenant there, then use getLoadContext to pass it to Remix, so in your loaders you do

export async function loader({ request, context }: LoaderArgs) {
  context.auth.authenticate("okta", request, options)
  // or
  context.auth.isAuthenticated(request, options)
  // more code here
}

@devbytyler
Copy link
Author

devbytyler commented Dec 2, 2022

Thanks for the response @sergiodxa!

Could you clarify what "create...on the server" means? Seems like I'm a bit outside the norm here so having a hard time finding examples.

Can getLoadContext() be used with the RemixAppServer? I could only find examples with an express server, and the documentation is pretty sparse.

@sergiodxa
Copy link
Owner

Could you clarify what "create...on the server" means? Seems like I'm a bit outside the norm here so having a hard time finding examples.

Remix is designed to work as a request handler inside an HTTP server, so you can run code inside the HTTP server itself which runs before Remix, there you have access to the request the HTTP server uses (e.g. the Express request object) so you can identify from that object the tenant.

Can getLoadContext() be used with the RemixAppServer? I could only find examples with an express server, and the documentation is pretty sparse

It can't be used with Remix App Server, the idea of RAS is to be used when you don't need to customize the HTTP server, if you want to you will have to switch to Express, you can use rmx-cli package to eject from RAS to Express, see https://github.com/kiliman/rmx-cli#-eject-ras

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

No branches or pull requests

2 participants