Provider Setup
Configuring Privy and the Rising Tide auth layer for a new app.
Setting up authentication for a new Rising Tide app requires configuring Privy and wrapping your app with the SDK providers.
Privy configuration
- Create a Privy app at privy.io
- Enable Google and X as login methods. Those two are the only methods the shared sign-in offers, because an invite is the only way in and the claim works with them
- Copy your Privy App ID
The .risingtide.app cookie domain is set by RisingTideProviders itself, and only when the page is served from a risingtide.app host. A client app on its own domain gets a plain per-origin session.
Environment variables
NEXT_PUBLIC_PRIVY_APP_ID=your-privy-app-id
NEXT_PUBLIC_APP_ID=42
# optional - set to "false" to never offer the bring-your-own-wallet path in this build
NEXT_PUBLIC_ENABLE_BYOW=trueNEXT_PUBLIC_APP_ID is the app's numeric protocol id. The provider reads it for the signup posture, the server-set app mode and the entry curtain; see Providers.
The bring-your-own-wallet option is hidden by default. It shows only when this env var is not "false" AND the server flag App.byowEnabled is on for the app (it defaults off), or the page was opened with ?byow=1 for the session.
Provider setup in Next.js
The canonical path is the consolidated RisingTideProviders from @rising-tide/ui/providers. It encodes the load-bearing provider order once - the real @privy-io/react-auth PrivyProvider mounts above the protocol's conditional providers, which is required so the Privy access token is available on the first POST /api/users at signup. Wire a thin client AppProviders that builds the config and renders it. Every field below is required by the config type except signupRedirect:
"use client";
import { RisingTideProviders } from "@rising-tide/ui/providers";
import { RisingTideSDK } from "@rising-tide/sdk";
const sdk = new RisingTideSDK({
baseUrl: process.env.NEXT_PUBLIC_RT_API_URL!,
apiKey: process.env.NEXT_PUBLIC_API_KEY,
});
export function AppProviders({ children }: { children: React.ReactNode }) {
return (
<RisingTideProviders
config={{
sdk,
storagePrefix: "myapp",
network: process.env.NEXT_PUBLIC_STACKS_NETWORK,
privy: {
appId: process.env.NEXT_PUBLIC_PRIVY_APP_ID!,
appearance: { theme: "light" },
},
auth: {
appName: "My App",
signupRedirect: "/home",
},
}}
>
{children}
</RisingTideProviders>
);
}Then render <AppProviders> inside your root layout body. Copy the config from any of rt-profile / grammata / OTR / bracket-blitz when scaffolding a new app. The full field list, including the entry curtain every app gets, is on Providers.
Lower-level composition
For finer control, compose the stack yourself with RisingTideAuthProvider, mounted inside your app's own PrivyProvider from @privy-io/react-auth (not from the SDK - the SDK never re-exports it, because each app owns its Privy theming). userConfig is the inner ProtocolUserProvider's config, and two of its members are required:
import { PrivyProvider } from "@privy-io/react-auth";
import { RisingTideAuthProvider } from "@rising-tide/ui/auth";
<PrivyProvider appId={process.env.NEXT_PUBLIC_PRIVY_APP_ID!} config={{ loginMethods: ["google", "twitter"] }}>
<RisingTideAuthProvider
sdk={sdk}
userConfig={{
storagePrefix: "myapp",
linkInviteCodes: true,
onSignupComplete: () => {
window.location.assign("/home");
},
}}
>
{children}
</RisingTideAuthProvider>
</PrivyProvider>RisingTideAuthProvider requires a configured sdk and userConfig, and must sit inside PrivyProvider. Mounting it outside throws "You need to wrap your application with the <PrivyProvider>" on the first auth interaction. This path does not mount the sign-in dialog, the welcome dialog or the entry curtain - those are RisingTideProviders features.
AuthGate
Use AuthGate to protect routes that require authentication. It redirects unauthenticated visitors to loginPath (default /):
import { AuthGate } from "@rising-tide/ui/auth";
export default function ProtectedPage() {
return (
<AuthGate>
<PageContent />
</AuthGate>
);
}Pass an allowlist for the public routes that should skip the gate (each entry matches by exact or prefix), and an optional requiredEntitlement to gate on RBAC as well as auth:
<AuthGate
allowlist={[{ exact: "/" }, { prefix: "/claim/" }]}
requiredEntitlement="CREATE_PAGES"
>
<PageContent />
</AuthGate>See AuthGate for the matcher shape and the full prop list.