diff --git a/.github/ISSUE_TEMPLATE/2_bug_provider.yml b/.github/ISSUE_TEMPLATE/2_bug_provider.yml index 1ee4409a23..6006433f81 100644 --- a/.github/ISSUE_TEMPLATE/2_bug_provider.yml +++ b/.github/ISSUE_TEMPLATE/2_bug_provider.yml @@ -87,6 +87,7 @@ body: - "Reddit" - "Roblox" - "Salesforce" + - "Scalekit" - "SimpleLogin" - "Slack" - "Spotify" diff --git a/docs/pages/getting-started/providers/scalekit.mdx b/docs/pages/getting-started/providers/scalekit.mdx new file mode 100644 index 0000000000..50d14ac9f9 --- /dev/null +++ b/docs/pages/getting-started/providers/scalekit.mdx @@ -0,0 +1,203 @@ +import { Callout } from "nextra/components" +import { Code } from "@/components/Code" + + + +# Scalekit Provider + +## Resources + +- [Add modular SSO with Scalekit](https://docs.scalekit.com/authenticate/sso/add-modular-sso/) +- [Next.js + Auth.js example app](https://github.com/scalekit-developers/scalekit-authjs-example) +- [Onboard enterprise customers](https://docs.scalekit.com/sso/guides/onboard-enterprise-customers/) +- [SSO integrations guide](https://docs.scalekit.com/guides/integrations/sso-integrations/) + +## Setup + +### Callback URL + + + + +```bash +https://example.com/api/auth/callback/scalekit +``` + + + + +```bash +https://example.com/auth/callback/scalekit +``` + + + + +```bash +https://example.com/auth/callback/scalekit +``` + + + + +```bash +https://example.com/auth/callback/scalekit +``` + + + + +### Environment Variables + +``` +AUTH_SCALEKIT_ID +AUTH_SCALEKIT_SECRET +AUTH_SCALEKIT_ISSUER +``` + +`AUTH_SCALEKIT_ISSUER` is your Scalekit environment URL (e.g. `https://yourenv.scalekit.dev`). + +### Configuration + + + + +```ts filename="/auth.ts" +import NextAuth from "next-auth" +import Scalekit from "next-auth/providers/scalekit" + +export const { handlers, auth, signIn, signOut } = NextAuth({ + providers: [Scalekit], +}) +``` + + + + +```ts filename="/src/routes/plugin@auth.ts" +import { QwikAuth$ } from "@auth/qwik" +import Scalekit from "@auth/qwik/providers/scalekit" + +export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$( + () => ({ + providers: [Scalekit], + }) +) +``` + + + + +```ts filename="/src/auth.ts" +import { SvelteKitAuth } from "@auth/sveltekit" +import Scalekit from "@auth/sveltekit/providers/scalekit" + +export const { handle, signIn, signOut } = SvelteKitAuth({ + providers: [Scalekit], +}) +``` + + + + +```ts filename="/src/app.ts" +import { ExpressAuth } from "@auth/express" +import Scalekit from "@auth/express/providers/scalekit" + +app.use("/auth/*", ExpressAuth({ providers: [Scalekit] })) +``` + + + + +### SSO Routing + +Scalekit supports multiple ways to route users to their SSO connection. You can pass optional routing parameters to target a specific connection: + + + + +```ts filename="/auth.ts" +import NextAuth from "next-auth" +import Scalekit from "next-auth/providers/scalekit" + +export const { handlers, auth, signIn, signOut } = NextAuth({ + providers: [ + Scalekit({ + // pick one of the following routing strategies: + connectionId: "conn_...", // exact connection (highest precedence) + organizationId: "org_...", // org's active SSO connection + domain: "acme.com", // resolve org from email domain + }), + ], +}) +``` + + + + +```ts filename="/src/routes/plugin@auth.ts" +import { QwikAuth$ } from "@auth/qwik" +import Scalekit from "@auth/qwik/providers/scalekit" + +export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$( + () => ({ + providers: [ + Scalekit({ + connectionId: "conn_...", + organizationId: "org_...", + domain: "acme.com", + }), + ], + }) +) +``` + + + + +```ts filename="/src/auth.ts" +import { SvelteKitAuth } from "@auth/sveltekit" +import Scalekit from "@auth/sveltekit/providers/scalekit" + +export const { handle, signIn, signOut } = SvelteKitAuth({ + providers: [ + Scalekit({ + connectionId: "conn_...", + organizationId: "org_...", + domain: "acme.com", + }), + ], +}) +``` + + + + +```ts filename="/src/app.ts" +import { ExpressAuth } from "@auth/express" +import Scalekit from "@auth/express/providers/scalekit" + +app.use( + "/auth/*", + ExpressAuth({ + providers: [ + Scalekit({ + connectionId: "conn_...", + organizationId: "org_...", + domain: "acme.com", + }), + ], + }) +) +``` + + + + + + Scalekit is an OIDC-compliant platform — Auth.js automatically discovers all + endpoints from your `AUTH_SCALEKIT_ISSUER` URL via + `/.well-known/openid-configuration`. No manual endpoint configuration is + required. + diff --git a/docs/public/img/providers/scalekit.png b/docs/public/img/providers/scalekit.png new file mode 100644 index 0000000000..e5723608aa Binary files /dev/null and b/docs/public/img/providers/scalekit.png differ diff --git a/packages/core/src/providers/scalekit.ts b/packages/core/src/providers/scalekit.ts new file mode 100644 index 0000000000..50607e3f57 --- /dev/null +++ b/packages/core/src/providers/scalekit.ts @@ -0,0 +1,161 @@ +/** + *
+ * Built-in Scalekit integration. + * + * + * + *
+ * + * @module providers/scalekit + */ +import type { OIDCConfig, OAuthUserConfig } from "./index.js" + +/** + * The profile returned by Scalekit's userinfo endpoint. + * + * - {@link https://docs.scalekit.com/apis/userinfo | Scalekit UserInfo endpoint} + */ +export interface ScalekitProfile extends Record { + /** Unique user ID (`usr_...`) */ + sub: string + email: string + email_verified: boolean + name: string + given_name: string + family_name: string + picture: string + /** Organization ID (`org_...`) */ + oid: string +} + +/** + * Add Scalekit SSO login to your page. + * + * ### Setup + * + * #### Callback URL + * ``` + * https://example.com/api/auth/callback/scalekit + * ``` + * + * #### Configuration + * ```ts + * import { Auth } from "@auth/core" + * import Scalekit from "@auth/core/providers/scalekit" + * + * const request = new Request(origin) + * const response = await Auth(request, { + * providers: [ + * Scalekit({ + * clientId: AUTH_SCALEKIT_ID, + * clientSecret: AUTH_SCALEKIT_SECRET, + * issuer: AUTH_SCALEKIT_ISSUER, + * }), + * ], + * }) + * ``` + * + * ### Resources + * + * - [Add modular SSO with Scalekit](https://docs.scalekit.com/authenticate/sso/add-modular-sso/) + * - [Scalekit SSO code samples](https://docs.scalekit.com/authenticate/sso/code-samples/) + * - [Onboard enterprise customers](https://docs.scalekit.com/sso/guides/onboard-enterprise-customers/) + * - [SSO integrations guide](https://docs.scalekit.com/guides/integrations/sso-integrations/) + * + * ### SSO Routing + * + * Scalekit supports multiple ways to route a user to their SSO connection. + * Pass one of the following optional parameters to target a specific connection: + * + * ```ts + * Scalekit({ + * clientId: AUTH_SCALEKIT_ID, + * clientSecret: AUTH_SCALEKIT_SECRET, + * issuer: AUTH_SCALEKIT_ISSUER, + * // pick one of the following routing strategies: + * connectionId: "conn_...", // exact connection (highest precedence) + * organizationId: "org_...", // org's active SSO connection + * domain: "acme.com", // resolve org from email domain + * }) + * ``` + * + * ### Notes + * + * By default, Auth.js assumes that the Scalekit provider is + * based on the [Open ID Connect](https://openid.net/specs/openid-connect-core-1_0.html) specification. + * + * Scalekit is an enterprise SSO platform that supports SAML, OIDC, and social login + * (Google, Microsoft, GitHub, etc.) connections. It exposes a single OIDC-compliant + * interface, so Auth.js auto-discovers all endpoints from the issuer URL via + * `/.well-known/openid-configuration` — no manual endpoint configuration required. + * + * Each Scalekit environment has a unique issuer URL of the form + * `https://.scalekit.dev` (dev) or `https://.scalekit.com` (prod). + * Set `AUTH_SCALEKIT_ISSUER` to this URL. + * + * :::tip + * + * The Scalekit provider comes with a [default configuration](https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/providers/scalekit.ts). + * To override the defaults for your use case, check out [customizing a built-in OAuth provider](https://authjs.dev/guides/configuring-oauth-providers). + * + * ::: + * + * :::info **Disclaimer** + * + * If you think you found a bug in the default configuration, you can [open an issue](https://authjs.dev/new/provider-issue). + * + * Auth.js strictly adheres to the specification and it cannot take responsibility for any deviation from + * the spec by the provider. You can open an issue, but if the problem is non-compliance with the spec, + * we might not pursue a resolution. You can ask for more help in [Discussions](https://authjs.dev/new/github-discussions). + * + * ::: + */ +export default function Scalekit

( + options: OAuthUserConfig

& { + /** + * Your Scalekit environment URL (e.g. `https://yourenv.scalekit.dev`). + * Used for OIDC discovery and as the token issuer. + */ + issuer: string + /** + * Route the user to a specific SSO connection by its ID (`conn_...`). + * Takes precedence over `organizationId` and `domain`. + */ + connectionId?: string + /** + * Route the user to a specific organization's active SSO connection (`org_...`). + */ + organizationId?: string + /** + * Route the user to the SSO connection associated with this email domain. + */ + domain?: string + } +): OIDCConfig

{ + const { issuer, connectionId, organizationId, domain } = options + + return { + id: "scalekit", + name: "Scalekit", + type: "oidc", + issuer, + authorization: { + params: { + scope: "openid email profile", + ...(connectionId && { connection_id: connectionId }), + ...(organizationId && { organization_id: organizationId }), + ...(domain && { domain }), + }, + }, + profile(profile) { + return { + id: profile.sub, + name: profile.name ?? `${profile.given_name} ${profile.family_name}`, + email: profile.email, + image: profile.picture ?? null, + } + }, + style: { bg: "#1c1c1e", text: "#fff" }, + options, + } +}