Nicholai 0d38f81e2c feat(auth): implement custom Nextcloud OAuth with auto-provisioning
Replaced NextAuth's built-in OAuth provider (incompatible with Cloudflare
Workers) with custom OAuth implementation using native fetch API.

Features:
- Custom OAuth flow compatible with Cloudflare Workers edge runtime
- Auto-provisions users from Nextcloud based on group membership
- Group-based role assignment (artists, shop_admins, admins)
- Auto-creates artist profiles for users in 'artists' group
- Seamless integration with existing NextAuth session management

Technical changes:
- Added custom OAuth routes: /api/auth/nextcloud/authorize & callback
- Created Nextcloud API client for user provisioning (lib/nextcloud-client.ts)
- Extended credentials provider to accept Nextcloud one-time tokens
- Added user management functions to database layer
- Updated signin UI to use custom OAuth flow
- Added environment variables for OAuth configuration

Documentation:
- Comprehensive setup guide in docs/NEXTCLOUD-OAUTH-SETUP.md
- Updated CLAUDE.md with new authentication architecture

Fixes: NextAuth OAuth incompatibility with Cloudflare Workers (unenv https.request error)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 02:06:14 +00:00

59 lines
1.7 KiB
TypeScript

"use client"
import { useEffect } from "react"
import { signIn } from "next-auth/react"
import { useSearchParams } from "next/navigation"
import { Loader2 } from "lucide-react"
/**
* Nextcloud OAuth Completion Page
*
* This page automatically completes the NextAuth sign-in after successful OAuth.
* It receives a one-time token and submits it to the credentials provider.
*/
export default function NextcloudCompletePage() {
const searchParams = useSearchParams()
const token = searchParams.get("token")
const callbackUrl = searchParams.get("callbackUrl") || "/admin"
useEffect(() => {
if (!token) {
// No token, redirect to sign-in
window.location.href = "/auth/signin?error=SessionError"
return
}
// Auto-submit to NextAuth credentials provider
const completeSignIn = async () => {
try {
const result = await signIn("credentials", {
nextcloud_token: token,
redirect: false,
})
if (result?.error) {
console.error("Sign-in error:", result.error)
window.location.href = "/auth/signin?error=SessionError"
} else if (result?.ok) {
// Success! Redirect to callback URL
window.location.href = callbackUrl
}
} catch (error) {
console.error("Unexpected error during sign-in:", error)
window.location.href = "/auth/signin?error=SessionError"
}
}
completeSignIn()
}, [token, callbackUrl])
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="text-center">
<Loader2 className="h-12 w-12 animate-spin mx-auto text-gray-600" />
<p className="mt-4 text-gray-600">Completing sign-in...</p>
</div>
</div>
)
}