Package Exports
- @boxyhq/remix-auth-sso
- @boxyhq/remix-auth-sso/build/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@boxyhq/remix-auth-sso) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
BoxyHQSSOStrategy
Attention ⚠️: We have deprecated the earlier strategy BoxyHQSAMLStrategy (npm package: @boxyhq/remix-auth-saml). In case you are using that one please consider changing over to this strategy.
The BoxyHQSSOStrategy can be used to enable Single Sign-On (SSO) in your remix app. It extends the OAuth2Strategy.
Demo
Checkout the demo at https://github.com/boxyhq/jackson-remix-auth
Supported runtimes
| Runtime | Has Support |
|---|---|
| Node.js | ✅ |
| Cloudflare | ✅ |
SAML Jackson Service
SAML Jackson implements SSO as an OAuth 2.0 flow, abstracting away all the complexities of the underlying SAML/OIDC protocol.
You can deploy SAML Jackson as a separate service. Check out the documentation for more details
Configuration
SSO login requires a connection for every tenant/product of yours. One common method is to use the domain for an email address to figure out which tenant they belong to. You can also use a unique tenant ID (string) from your backend for this, typically some kind of account or organization ID.
Check out the documentation for more details.
Usage
Install the strategy
npm install @boxyhq/remix-auth-ssoCreate the strategy instance
// app/utils/auth.server.ts
import { Authenticator } from "remix-auth";
import {
BoxyHQSSOStrategy,
type BoxyHQSSOProfile,
} from "@boxyhq/remix-auth-sso";
// Create an instance of the authenticator, pass a generic with what your
// strategies will return and will be stored in the session
export const authenticator = new Authenticator<BoxyHQSSOProfile>(
sessionStorage
);
auth.use(
new BoxyHQSSOStrategy(
{
issuer: "http://localhost:5225", // point this to the hosted jackson service
clientID: "dummy", // The dummy here is necessary if the tenant and product are set dynamically from the client side
clientSecret: "dummy", // The dummy here is necessary if the tenant and product are set dynamically from the client side
callbackURL: new URL(
"/auth/sso/callback",
process.env.BASE_URL
).toString(), // BASE_URL should point to the application URL
},
async ({ profile }) => {
return profile;
}
)
);Setup your routes
// app/routes/login.tsx
export default function Login() {
return (
<Form method="post" action="/auth/sso">
{/* We will be using user email to identify the tenant*/}
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
name="email"
placeholder="johndoe@example.com"
required
/>
{/* Product can also be set dynamically, set to `demo` here */}
<input type="text" name="product" hidden defaultValue="demo" />
<button type="submit">Sign In with SSO</button>
</Form>
);
}// app/routes/auth/sso.tsx
import { ActionFunction, json } from "remix";
import { auth } from "~/auth.server";
import invariant from "tiny-invariant";
type PostError = {
email?: boolean;
product?: boolean;
};
export const action: ActionFunction = async ({ request }) => {
const formData = await request.formData();
const email = formData.get("email");
const product = await formData.get("product");
const errors: PostError = {};
if (!email) errors.email = true;
if (!product) errors.product = true;
if (Object.keys(errors).length) {
return json(errors);
}
invariant(typeof email === "string");
// Get the tenant from the domain
const tenant = email.split("@")[1];
return await auth.authenticate("boxyhq-sso", request, {
successRedirect: "/private",
failureRedirect: "/",
context: {
clientID: `tenant=${tenant}&product=${product}`,
clientSecret: "dummy",
},
});
};// app/routes/auth/sso/callback.tsx
import type { LoaderFunction } from "remix";
import { auth } from "~/auth.server";
export const loader: LoaderFunction = async ({ request, params }) => {
return auth.authenticate("boxyhq-sso", request, {
successRedirect: "/private",
failureRedirect: "/",
});
};