Base IdP

Base IdP

CLI

create

Scaffold a wired Base IdP starter for your stack — the right minimal env, the right SDK glue, the right redirect setup.

base-idp create is the command that turns "I want to integrate Base IdP into my app" into "here is a folder with the code already wired." It picks the right SDK for your stack, generates the env block your role actually needs, and writes a BASE_IDP_SETUP.md with the remaining manual steps.

There are nine supported stacks. Each one is a minimal, idiomatic starter that you can drop into your project and adapt.

The basics

Run it interactively for the full prompt experience:

npx base-idp create

Or skip the prompts by passing everything as flags:

npx base-idp create \
  --stack flutter \
  --client-id sq_live_yourapp \
  --redirect-uri "myapp://auth/callback"

For a confidential server you add the secret. Type it into the command — do not save it in a script that lives in version control.

npx base-idp create \
  --stack nextjs-fullstack \
  --client-id sq_live_web \
  --client-secret sqk_your_secret

Listing stacks

npx base-idp create --list
Available stacks:
  flutter            Flutter (mobile, public)
  react-native       React Native / Expo (mobile, public)
  swiftui            SwiftUI (iOS, public)
  nextjs-fullstack   Next.js fullstack (server login, confidential)
  nextjs             Next.js frontend (talks to a separate API, public)
  nestjs             NestJS (confidential server)
  express            Express / Node API (verify-only)
  go                 Go API (verify-only)
  rust               Rust API (verify-only)

What each stack generates

The output is small on purpose. The CLI is not trying to be your project template. It is trying to remove the moment where you sit and wonder which SDK function to call and which env variable to read.

Flutter

base-idp-flutter/
├── BASE_IDP_SETUP.md
├── lib/
│   └── auth/
│       └── base_idp_auth.dart
└── run.sh

base_idp_auth.dart reads the client id from String.fromEnvironment, so the value can be passed at build time:

flutter run --dart-define=BASE_IDP_CLIENT_ID=sq_live_yourapp

This is exactly how a public client should pass a client id — it is in the binary at build time but never in the source repo.

React Native / Expo

base-idp-react-native/
├── BASE_IDP_SETUP.md
├── .env
└── auth/
    └── baseIdp.ts
.env
BASE_IDP_CLIENT_ID=sq_live_yourapp

The .env here is the Expo convention — EXPO_PUBLIC_* variables are exposed to the bundle, which is fine for a client id.

SwiftUI

base-idp-swiftui/
├── BASE_IDP_SETUP.md
└── BaseIdPAuth.swift

The Swift file uses ASWebAuthenticationSession and CryptoKit for PKCE. The starter expects you to add a URL Type in your target's Info to register the callback scheme.

Next.js (fullstack)

base-idp-nextjs-fullstack/
├── BASE_IDP_SETUP.md
├── .env.local
└── app/
    └── api/
        └── auth/
            ├── login/route.ts
            └── callback/route.ts

Two route handlers: /api/auth/login builds the authorize URL and redirects, /api/auth/callback exchanges the code and hands you a verified principal.

Next.js (frontend only)

base-idp-nextjs/
├── BASE_IDP_SETUP.md
├── .env.local
└── lib/
    └── auth.ts

Browser-only setup — for a Next.js app that talks to a separate backend API. The env variable is named NEXT_PUBLIC_BASE_IDP_CLIENT_ID because the client id is exposed to the bundle.

NestJS

base-idp-nestjs/
├── BASE_IDP_SETUP.md
├── .env
└── src/
    └── auth/
        └── base-idp.guard.ts

A BaseIdpGuard that you apply with @UseGuards(BaseIdpGuard). The verified principal lands on request.principal.

Express, Go, Rust (verify-only)

base-idp-express/
├── BASE_IDP_SETUP.md
├── .env       # placeholder, nothing required
└── middleware/
    └── baseIdp.ts

The .env is a commented placeholder. Verify-only services need nothing in env to function, only optionally the client id to pin the audience.

Every option

FlagDescription
--stack <id>The stack to scaffold. See the list above.
--listPrint every stack and exit.
--client-id <id>Your OAuth client id. Public. Safe to ship in frontend bundles.
--client-secret <secret>Server secret. Only used for confidential / fullstack stacks.
--dir <path>Output directory. Default is base-idp-<stack>.
--redirect-uri <uri>Redirect URI, e.g. myapp://auth/callback.
--scheme <scheme>Mobile URL scheme. Derived from --redirect-uri if omitted.
--forceWrite into a non-empty directory.

Role rules the CLI enforces

The CLI does not let you make a mistake about which env goes where. The rules that scaffolded code follows:

  • Public stacks never get a BASE_IDP_CLIENT_SECRET in their generated files, even if you pass --client-secret on the command line. The flag is ignored for these stacks.
  • Verify-only stacks generate a .env that has the Base IdP env commented out. Nothing is required to run.
  • Confidential / fullstack stacks get BASE_IDP_CLIENT_ID plus BASE_IDP_CLIENT_SECRET in a server-side file (.env, not .env.local unless the framework expects it).

This means you cannot accidentally ship a secret to a mobile bundle through the scaffolder. The role rules are not a convention; they are mechanically enforced.

After the CLI is done

The last thing create prints is a one-line "next" pointing at the BASE_IDP_SETUP.md it generated. Open it. The setup steps differ by stack but always come down to:

  1. Install the SDK.
  2. Set the env variable to your real client id.
  3. Register the redirect URI in the platform that needs it.

The first time you do this for a new framework, it takes about ten minutes. The second time it takes about two.

Where to go next

On this page