Start Here
Quick Start Guide

Quick Start

Integrate Optare authentication into your Next.js or Remix application in 5 minutes. By the end of this guide, you'll have:

  • A working login and signup flow
  • A protected dashboard page
  • Access to user and organization data

Prerequisites

Step 0: Get Your Credentials

  1. Go to id.optare.one/portal (opens in a new tab) and sign in
  2. Navigate to OAuth Clients in the sidebar
  3. Click Create OAuth Client
  4. Fill in the form:
    • Name: "My App" (or your app name)
    • Redirect URIs: http://localhost:3000/api/auth/callback
    • Client Type: Confidential
  5. Click Create and copy your Client ID and Client Secret

Keep these credentials handy - you'll need them in Step 2.

Step 1: Install the SDK

Install Better Auth, which Optare is built on:

pnpm add better-auth
# or
npm install better-auth
# or
yarn add better-auth

Step 2: Configure Environment Variables

Create a .env.local file (Next.js) or .env file (Remix) in your project root:

# Optare OAuth Client Credentials
OPTARE_CLIENT_ID="your_client_id_from_step_0"
OPTARE_CLIENT_SECRET="your_client_secret_from_step_0"
 
# Your app URL
NEXT_PUBLIC_APP_URL="http://localhost:3000"  # For Next.js
# APP_URL="http://localhost:3000"            # For Remix
 
# Generate this with: openssl rand -base64 32
BETTER_AUTH_SECRET="your_random_secret_key_min_32_chars"

Generate a secret key:

openssl rand -base64 32

Copy the output and paste it as your BETTER_AUTH_SECRET.

Step 3: Create the Auth Client

Create lib/auth-client.ts:

import { createAuthClient } from "better-auth/react";
 
export const authClient = createAuthClient({
  baseURL: process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000",
});
 
export const { useSession, signIn, signUp, signOut } = authClient;

This client provides React hooks and functions for authentication.

Step 4: Create Auth Configuration

Create lib/auth.server.ts:

import { betterAuth } from "better-auth";
 
export const auth = betterAuth({
  secret: process.env.BETTER_AUTH_SECRET!,
  baseURL: process.env.NEXT_PUBLIC_APP_URL || process.env.APP_URL || "http://localhost:3000",
  
  socialProviders: {
    optare: {
      clientId: process.env.OPTARE_CLIENT_ID!,
      clientSecret: process.env.OPTARE_CLIENT_SECRET!,
      authorizationURL: "https://id.optare.one/oauth/authorize",
      tokenURL: "https://id.optare.one/oauth/token",
      userInfoURL: "https://id.optare.one/oauth/userinfo",
    },
  },
});

This configuration connects your app to Optare's hosted identity service.

Step 5: Add Auth Routes

For Next.js App Router

Create app/api/auth/[...all]/route.ts:

import { auth } from "@/lib/auth.server";
import { toNextJsHandler } from "better-auth/next-js";
 
export const { GET, POST } = toNextJsHandler(auth);

For Remix

Create app/routes/api.auth.$.ts:

import { auth } from "~/lib/auth.server";
import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/node";
 
export async function loader({ request }: LoaderFunctionArgs) {
  return auth.handler(request);
}
 
export async function action({ request }: ActionFunctionArgs) {
  return auth.handler(request);
}

These routes handle OAuth callbacks and session management.

Step 6: Create Sign-In Page

For Next.js App Router

Create app/sign-in/page.tsx:

"use client";
 
import { authClient } from "@/lib/auth-client";
 
export default function SignInPage() {
  const handleSignIn = async () => {
    await authClient.signIn.social({
      provider: "optare",
      callbackURL: "/dashboard",
    });
  };
 
  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-50">
      <div className="max-w-md w-full space-y-8 p-8 bg-white rounded-lg shadow">
        <div>
          <h2 className="text-3xl font-bold text-center">Sign in to your account</h2>
        </div>
        <button
          onClick={handleSignIn}
          className="w-full flex justify-center py-3 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-black hover:bg-gray-800"
        >
          Sign in with Optare
        </button>
      </div>
    </div>
  );
}

For Remix

Create app/routes/sign-in.tsx:

import { authClient } from "~/lib/auth-client";
 
export default function SignInPage() {
  const handleSignIn = async () => {
    await authClient.signIn.social({
      provider: "optare",
      callbackURL: "/dashboard",
    });
  };
 
  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-50">
      <div className="max-w-md w-full space-y-8 p-8 bg-white rounded-lg shadow">
        <div>
          <h2 className="text-3xl font-bold text-center">Sign in to your account</h2>
        </div>
        <button
          onClick={handleSignIn}
          className="w-full flex justify-center py-3 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-black hover:bg-gray-800"
        >
          Sign in with Optare
        </button>
      </div>
    </div>
  );
}

Step 7: Create a Protected Dashboard

For Next.js App Router

Create app/dashboard/page.tsx:

"use client";
 
import { useSession, signOut } from "@/lib/auth-client";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
 
export default function DashboardPage() {
  const { data: session, isPending } = useSession();
  const router = useRouter();
 
  useEffect(() => {
    if (!isPending && !session) {
      router.push("/sign-in");
    }
  }, [session, isPending, router]);
 
  if (isPending) {
    return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
  }
 
  if (!session) {
    return null;
  }
 
  return (
    <div className="min-h-screen p-8">
      <div className="max-w-4xl mx-auto">
        <div className="bg-white rounded-lg shadow p-6">
          <h1 className="text-3xl font-bold mb-4">Welcome, {session.user.name}!</h1>
          <div className="space-y-2 text-gray-600">
            <p><strong>Email:</strong> {session.user.email}</p>
            <p><strong>Organization:</strong> {session.organization?.name || "No organization"}</p>
            <p><strong>Role:</strong> {session.role || "member"}</p>
          </div>
          <button
            onClick={() => signOut()}
            className="mt-6 px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700"
          >
            Sign Out
          </button>
        </div>
      </div>
    </div>
  );
}

For Remix

Create app/routes/dashboard.tsx:

import { useSession, signOut } from "~/lib/auth-client";
import { Navigate } from "@remix-run/react";
 
export default function DashboardPage() {
  const { data: session, isPending } = useSession();
 
  if (isPending) {
    return <div className="min-h-screen flex items-center justify-center">Loading...</div>;
  }
 
  if (!session) {
    return <Navigate to="/sign-in" />;
  }
 
  return (
    <div className="min-h-screen p-8">
      <div className="max-w-4xl mx-auto">
        <div className="bg-white rounded-lg shadow p-6">
          <h1 className="text-3xl font-bold mb-4">Welcome, {session.user.name}!</h1>
          <div className="space-y-2 text-gray-600">
            <p><strong>Email:</strong> {session.user.email}</p>
            <p><strong>Organization:</strong> {session.organization?.name || "No organization"}</p>
            <p><strong>Role:</strong> {session.role || "member"}</p>
          </div>
          <button
            onClick={() => signOut()}
            className="mt-6 px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700"
          >
            Sign Out
          </button>
        </div>
      </div>
    </div>
  );
}

Step 8: Test Your Integration

  1. Start your development server:
pnpm dev
# or npm run dev
  1. Navigate to http://localhost:3000/sign-in

  2. Click "Sign in with Optare" - you'll be redirected to id.optare.one

  3. Sign in or create an account at id.optare.one

  4. You'll be redirected back to /dashboard with your session active

You should now see your name, email, organization, and role displayed on the dashboard.

What You Built

Congratulations! You've successfully integrated Optare authentication. Your app now has:

  • OAuth 2.0 Authentication - Users sign in via Optare's hosted identity service
  • Session Management - Secure JWT-based sessions
  • Protected Routes - The dashboard requires authentication
  • Organization Awareness - Every user belongs to an organization
  • Role-Based Access - You can check user roles (owner/admin/member)

Next Steps

Now that you have basic authentication working, learn how to:

  1. Understand Organizations - Learn about multi-tenancy and how to manage organization members
  2. Use React Hooks - Deep dive into useSession, useUser, and useOrganization
  3. Protect API Routes - Verify tokens on your backend
  4. Explore the API - Build custom integrations

Troubleshooting

"Redirect URI mismatch" error

Make sure the redirect URI in your OAuth client settings exactly matches your app's callback URL:

  • Development: http://localhost:3000/api/auth/callback
  • Production: https://yourapp.com/api/auth/callback

"Invalid client credentials"

Double-check that your OPTARE_CLIENT_ID and OPTARE_CLIENT_SECRET environment variables match the credentials from your OAuth client in the portal.

Session not persisting

Ensure your BETTER_AUTH_SECRET is set and your app's domain matches your baseURL configuration.