Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ef313b9
test(wallet): add comprehensive tests for default auth
rodrigo-fournier-immutable Feb 10, 2026
103c7e7
feat(auth-next-client,auth-next-server): add default auth with auto-d…
rodrigo-fournier-immutable Feb 11, 2026
e173a4e
chore(examples): add auth-next default auth test app
rodrigo-fournier-immutable Feb 11, 2026
6b67639
feat(examples): add default auth example to wallets-connect-with-nextjs
rodrigo-fournier-immutable Feb 11, 2026
e77a2cc
Merge branch 'main' into ID-4319
rodrigo-fournier-immutable Feb 11, 2026
b55cd2c
chore: update pnpm-lock.yaml after merge
rodrigo-fournier-immutable Feb 11, 2026
46a72c9
fix(examples,auth-next): fix build issues and add missing config
rodrigo-fournier-immutable Feb 11, 2026
106e791
fix(auth-next,examples): address PR feedback and deduplicate code
rodrigo-fournier-immutable Feb 12, 2026
81855db
chore(auth-next,examples): upgrade to Next.js 15.2.6 (patches both CVEs)
rodrigo-fournier-immutable Feb 12, 2026
5b23657
refactor(auth-next-client): remove Partial from LoginConfig and Logou…
rodrigo-fournier-immutable Feb 12, 2026
e48702f
fix(auth-next-server): add NODE_ENV fallback for server-side sandbox …
rodrigo-fournier-immutable Feb 12, 2026
badcef1
feat(sdk-sample-app): add default auth option for zero-config testing
rodrigo-fournier-immutable Feb 13, 2026
6366c33
refactor(auth-next-server): enforce zero-config=sandbox, full-config=…
rodrigo-fournier-immutable Feb 13, 2026
866ebd8
refactor(auth-next): simplify config to no config or full config policy
rodrigo-fournier-immutable Feb 16, 2026
cfa0370
refactor(auth-next): remove unused code and enforce valid redirect URIs
rodrigo-fournier-immutable Feb 16, 2026
051f252
Remove defaultConfig.ts
rodrigo-fournier-immutable Feb 16, 2026
5f95c49
fix(auth-next-server): use __NEXT_PRIVATE_ORIGIN for redirectUri in z…
rodrigo-fournier-immutable Feb 16, 2026
576c756
Change code owners from the examples folder
rodrigo-fournier-immutable Feb 17, 2026
ddc1a33
chore: merge main and resolve pnpm-lock.yaml conflict
rodrigo-fournier-immutable Feb 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .syncpackrc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@
"dependencyTypes": ["peer"],
"packages": ["@imtbl/auth-next-server", "@imtbl/auth-next-client", "@imtbl/sdk"],
"isIgnored": true
},
{
"label": "Allow Next.js 14 in auth-next devDependencies for compatibility",
"dependencies": ["next"],
"dependencyTypes": ["dev"],
"packages": ["@imtbl/auth-next-server", "@imtbl/auth-next-client"],
"isIgnored": true
}
]
}
5 changes: 5 additions & 0 deletions examples/passport/auth-next-default-test/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Required for NextAuth.js
AUTH_SECRET=your-secret-key-min-32-characters-long-change-this-in-production

# Optional: Override default clientId (will use sandbox/production defaults if not set)
# NEXT_PUBLIC_IMMUTABLE_CLIENT_ID=your-client-id-here
Comment thread
rodrigo-fournier-immutable marked this conversation as resolved.
Outdated
4 changes: 4 additions & 0 deletions examples/passport/auth-next-default-test/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"root": true,
"extends": ["next/core-web-vitals", "next"]
}
32 changes: 32 additions & 0 deletions examples/passport/auth-next-default-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Default Auth Test App

Test application for `@imtbl/auth-next-client` and `@imtbl/auth-next-server` with default auth configuration.

## Setup

```bash
# Install dependencies
pnpm install

# Create .env.local
cp .env.example .env.local

# Run dev server
pnpm dev
```

## Testing

This app tests:
- ✅ Zero-config setup (no clientId or redirectUri required)
- ✅ Auto-detection of clientId (sandbox vs production)
- ✅ Auto-derivation of redirectUri
- ✅ useLogin hook with optional config
- ✅ useLogout hook with optional config
- ✅ Custom config overrides

## URLs

- Home: http://localhost:3000
- Callback: http://localhost:3000/callback
- Auth API: http://localhost:3000/api/auth
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { handlers } from "@/lib/auth";

export const { GET, POST } = handlers;
26 changes: 26 additions & 0 deletions examples/passport/auth-next-default-test/app/callback/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client";

import { CallbackPage } from "@imtbl/auth-next-client";
import {
DEFAULT_PRODUCTION_CLIENT_ID,
DEFAULT_SANDBOX_CLIENT_ID,
} from "@imtbl/auth-next-client";

export default function Callback() {
// Auto-detect environment and derive config
const isSandbox = typeof window !== 'undefined' &&
(window.location.hostname.includes('sandbox') || window.location.hostname.includes('localhost'));

const config = {
clientId: isSandbox ? DEFAULT_SANDBOX_CLIENT_ID : DEFAULT_PRODUCTION_CLIENT_ID,
redirectUri: typeof window !== 'undefined' ? `${window.location.origin}/callback` : '/callback',
};

return (
<div>
<h1>Processing authentication...</h1>
<CallbackPage config={config} redirectTo="/" />
</div>
);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"use client";

import { useEffect, useState } from "react";
import {
DEFAULT_PRODUCTION_CLIENT_ID,
DEFAULT_SANDBOX_CLIENT_ID,
} from "@imtbl/auth-next-client";

export function ConfigInfo() {
const [info, setInfo] = useState<{
hostname: string;
isSandbox: boolean;
expectedClientId: string;
redirectUri: string;
} | null>(null);

useEffect(() => {
const hostname = window.location.hostname;
const isSandbox = hostname.includes('sandbox') || hostname.includes('localhost');
const expectedClientId = isSandbox ? DEFAULT_SANDBOX_CLIENT_ID : DEFAULT_PRODUCTION_CLIENT_ID;
const redirectUri = `${window.location.origin}/callback`;

setInfo({
hostname,
isSandbox,
expectedClientId,
redirectUri,
});
}, []);

if (!info) {
return <div>Loading config info...</div>;
}

return (
<div className="info">
<h3>📋 Auto-Detected Configuration</h3>
<ul>
<li><strong>Hostname:</strong> <span className="code">{info.hostname}</span></li>
<li><strong>Environment:</strong> <span className="code">{info.isSandbox ? 'Sandbox' : 'Production'}</span></li>
<li><strong>ClientId:</strong> <span className="code">{info.expectedClientId}</span></li>
<li><strong>RedirectUri:</strong> <span className="code">{info.redirectUri}</span></li>
</ul>
<p><small>These values are automatically detected and don&apos;t need to be configured!</small></p>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"use client";

import { useLogin, useImmutableSession } from "@imtbl/auth-next-client";

export function LoginButton() {
const { isAuthenticated, session } = useImmutableSession();
const { loginWithPopup, isLoggingIn, error } = useLogin();

if (isAuthenticated && session) {
return (
<div className="success">
<h3>✅ Logged In</h3>
<p>Email: <span className="code">{session.user?.email || 'N/A'}</span></p>
<p>User ID: <span className="code">{session.user?.sub || 'N/A'}</span></p>
</div>
);
}

return (
<div>
<h3>Test Login (Zero Config)</h3>
<button
onClick={() => loginWithPopup()}
disabled={isLoggingIn}
>
{isLoggingIn ? "Signing in..." : "🔐 Sign In with Popup"}
</button>
{error && <p className="error">Error: {error}</p>}
<p className="info">
This uses <span className="code">loginWithPopup()</span> with no config!<br/>
ClientId and redirectUri are auto-detected.
</p>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"use client";

import { useLogin, useImmutableSession } from "@imtbl/auth-next-client";
import { useState } from "react";

export function LoginWithOverride() {
const { isAuthenticated, session } = useImmutableSession();
const { loginWithPopup, isLoggingIn, error } = useLogin();
const [customClientId, setCustomClientId] = useState("");

if (isAuthenticated && session) {
return (
<div className="success">
<h3>✅ Logged In with Override</h3>
<p>Email: <span className="code">{session.user?.email || 'N/A'}</span></p>
</div>
);
}

return (
<div>
<h3>Test Login with Custom ClientId Override</h3>
<div style={{ marginBottom: '1rem' }}>
<input
type="text"
placeholder="Enter custom clientId (optional)"
value={customClientId}
onChange={(e) => setCustomClientId(e.target.value)}
style={{
width: '100%',
padding: '0.5rem',
fontSize: '0.9rem',
borderRadius: '0.25rem',
border: '1px solid #ccc',
}}
/>
<small style={{ display: 'block', marginTop: '0.5rem', color: '#666' }}>
Leave empty to use default sandbox clientId
</small>
</div>
<button
onClick={() => loginWithPopup(customClientId ? {
clientId: customClientId,
// Other fields will use defaults
} : undefined)}
disabled={isLoggingIn}
>
{isLoggingIn ? "Signing in..." : "🔐 Sign In with Override"}
</button>
{error && <p className="error">Error: {error}</p>}
<p className="info">
This tests <span className="code">loginWithPopup(&#123; clientId: &apos;...&apos; &#125;)</span><br/>
Custom clientId overrides the default, but redirectUri is still auto-detected.
</p>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"use client";

import { useLogout, useImmutableSession } from "@imtbl/auth-next-client";

export function LogoutButton() {
const { isAuthenticated } = useImmutableSession();
const { logout, isLoggingOut, error } = useLogout();

if (!isAuthenticated) {
return null;
}

return (
<div>
<h3>Test Logout (Zero Config)</h3>
<button
onClick={() => logout()}
disabled={isLoggingOut}
>
{isLoggingOut ? "Signing out..." : "🚪 Sign Out"}
</button>
{error && <p className="error">Error: {error}</p>}
<p className="info">
This uses <span className="code">logout()</span> with no config!<br/>
Performs federated logout to clear both local and upstream sessions.
</p>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"use client";

import { useLogout, useImmutableSession } from "@imtbl/auth-next-client";
import { useState } from "react";

export function LogoutWithOverride() {
const { isAuthenticated } = useImmutableSession();
const { logout, isLoggingOut, error } = useLogout();
const [customRedirectUri, setCustomRedirectUri] = useState("");

if (!isAuthenticated) {
return null;
}

return (
<div>
<h3>Test Logout with Custom Redirect Override</h3>
<div style={{ marginBottom: '1rem' }}>
<input
type="text"
placeholder="Enter custom logoutRedirectUri (optional)"
value={customRedirectUri}
onChange={(e) => setCustomRedirectUri(e.target.value)}
style={{
width: '100%',
padding: '0.5rem',
fontSize: '0.9rem',
borderRadius: '0.25rem',
border: '1px solid #ccc',
}}
/>
<small style={{ display: 'block', marginTop: '0.5rem', color: '#666' }}>
Leave empty to use default (http://localhost:3000)
</small>
</div>
<button
onClick={() => logout(customRedirectUri ? {
logoutRedirectUri: customRedirectUri,
// ClientId will use default
} : undefined)}
disabled={isLoggingOut}
>
{isLoggingOut ? "Signing out..." : "🚪 Sign Out with Override"}
</button>
{error && <p className="error">Error: {error}</p>}
<p className="info">
This tests <span className="code">logout(&#123; logoutRedirectUri: &apos;...&apos; &#125;)</span><br/>
Custom redirect after logout, but clientId is still auto-detected.
</p>
</div>
);
}
63 changes: 63 additions & 0 deletions examples/passport/auth-next-default-test/app/globals.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
body {
font-family: system-ui, -apple-system, sans-serif;
margin: 0;
padding: 2rem;
max-width: 800px;
margin: 0 auto;
}

h1 {
color: #333;
}

button {
background: #0070f3;
color: white;
border: none;
padding: 0.75rem 1.5rem;
font-size: 1rem;
border-radius: 0.5rem;
cursor: pointer;
margin: 0.5rem 0.5rem 0.5rem 0;
}

button:hover {
background: #0051cc;
}

button:disabled {
background: #ccc;
cursor: not-allowed;
}

.error {
color: #ff0000;
margin-top: 1rem;
}

.success {
color: #00aa00;
margin-top: 1rem;
}

.info {
background: #f0f0f0;
padding: 1rem;
border-radius: 0.5rem;
margin: 1rem 0;
}

.code {
background: #f5f5f5;
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-family: monospace;
font-size: 0.9rem;
}

pre {
background: #f5f5f5;
padding: 1rem;
border-radius: 0.5rem;
overflow-x: auto;
}
Loading