Featurebase integration

This commit is contained in:
Konstantin Wohlwend 2025-07-30 09:32:44 -07:00
parent 7a0a26d6fd
commit 19142f40a9
7 changed files with 59 additions and 5 deletions

View File

@ -33,6 +33,7 @@
"Emailable",
"EMESSAGE",
"Falsey",
"Featurebase",
"fkey",
"frontends",
"geoip",

View File

@ -12,5 +12,5 @@ NEXT_PUBLIC_STACK_SVIX_SERVER_URL=# For prod, leave it empty. For local developm
NEXT_PUBLIC_STACK_HEAD_TAGS='[{ "tagName": "script", "attributes": {}, "innerHTML": "// insert head tags here" }]'
STACK_DEVELOPMENT_TRANSLATION_LOCALE=# enter the locale to use for the translation provider here, for example: de-DE. Only works during development, not in production. Optional, by default don't translate
NEXT_PUBLIC_STACK_ENABLE_DEVELOPMENT_FEATURES_PROJECT_IDS='["internal"]'
NEXT_PUBLIC_STACK_DEBUGGER_ON_ASSERTION_ERROR=# set to true to open the debugger on assertion errors (set to true in .env.development)
STACK_FEATUREBASE_JWT_SECRET=# used for Featurebase SSO, you probably won't have to set this

View File

@ -8,3 +8,5 @@ NEXT_PUBLIC_STACK_SVIX_SERVER_URL=http://localhost:8113
STACK_ARTIFICIAL_DEVELOPMENT_DELAY_MS=50
NEXT_PUBLIC_STACK_DEBUGGER_ON_ASSERTION_ERROR=true
STACK_FEATUREBASE_JWT_SECRET=secret-value

View File

@ -38,6 +38,7 @@
"clsx": "^2.0.0",
"dotenv-cli": "^7.3.0",
"geist": "^1",
"jose": "^5.2.2",
"lodash": "^4.17.21",
"lucide-react": "^0.508.0",
"next": "15.4.1",

View File

@ -0,0 +1,44 @@
import { stackServerApp } from "@/stack";
import { getEnvVariable } from "@stackframe/stack-shared/dist/utils/env";
import { urlString } from "@stackframe/stack-shared/dist/utils/urls";
import * as jose from "jose";
import { redirect } from "next/navigation";
export default async function FeaturebaseSSO({
searchParams,
}: {
searchParams: Promise<{ return_to?: string }>,
}) {
const { return_to: returnTo } = await searchParams;
if (!returnTo) {
return <div>Missing return_to parameter. Please go back and try again.</div>;
}
const user = await stackServerApp.getUser();
if (!user) {
redirect(urlString`/handler/sign-in?after_auth_return_to=${urlString`/integrations/featurebase/sso?return_to=${returnTo}`}`);
}
const featurebaseSecret = getEnvVariable("STACK_FEATUREBASE_JWT_SECRET");
// Create JWT token
const secret = new TextEncoder().encode(featurebaseSecret);
const jwt = await new jose.SignJWT({
userId: user.id,
email: user.primaryEmail,
name: user.displayName || undefined,
profilePicture: user.profileImageUrl || undefined,
})
.setProtectedHeader({ alg: "HS256" })
.setIssuer("stack-auth")
.setExpirationTime("10min")
.sign(secret);
// Redirect to Featurebase with JWT and return_to
const featurebaseUrl = new URL("https://feedback.stack-auth.com/api/v1/auth/access/jwt");
featurebaseUrl.searchParams.set("jwt", jwt);
featurebaseUrl.searchParams.set("return_to", returnTo);
redirect(featurebaseUrl.toString());
}

View File

@ -11,10 +11,12 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@oslojs/otp": "^1.1.0",
"@stackframe/js": "workspace:*",
"@stackframe/stack-shared": "workspace:*",
"dotenv": "^16.4.5",
"jose": "^5.2.2",
"@oslojs/otp": "^1.1.0"
"dotenv": "^16.4.5"
},
"devDependencies": {
"jose": "^5.6.3"
}
}

View File

@ -370,6 +370,9 @@ importers:
geist:
specifier: ^1
version: 1.3.0(next@15.4.1(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))
jose:
specifier: ^5.2.2
version: 5.6.3
lodash:
specifier: ^4.17.21
version: 4.17.21
@ -491,8 +494,9 @@ importers:
dotenv:
specifier: ^16.4.5
version: 16.4.5
devDependencies:
jose:
specifier: ^5.2.2
specifier: ^5.6.3
version: 5.6.3
apps/mcp-server: