File size: 3,350 Bytes
656ac31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
03c0bdb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
656ac31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
03c0bdb
656ac31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
03c0bdb
 
656ac31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { createServerClient, type CookieOptions } from '@supabase/ssr'
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'

export interface AuthResult {
  authenticated: true
  userId: string
  email: string | undefined
}

interface AuthError {
  authenticated: false
  response: NextResponse
}

async function getUserFromBearerToken(
  supabaseUrl: string,
  supabaseAnonKey: string,
  token: string,
) {
  const response = await fetch(`${supabaseUrl}/auth/v1/user`, {
    headers: {
      apikey: supabaseAnonKey,
      Authorization: `Bearer ${token}`,
    },
    cache: 'no-store',
  })

  if (!response.ok) {
    return { data: { user: null }, error: new Error('Invalid bearer token') }
  }

  const user = await response.json()
  return { data: { user }, error: null }
}

/**
 * Verify that the incoming request belongs to an authenticated Supabase user.
 *
 * Usage in any API route:
 * ```ts
 * const auth = await requireAuth()
 * if (!auth.authenticated) return auth.response
 * // auth.userId is available
 * ```
 *
 * Works with both cookie-based sessions (browser) and
 * Authorization: Bearer <token> header (API clients).
 */
export async function requireAuth(request?: Request): Promise<AuthResult | AuthError> {
  try {
    const cookieStore = await cookies()
    const authHeader = request?.headers.get('authorization') ?? null

    const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
    const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
    if (!supabaseUrl || !supabaseAnonKey) {
      return {
        authenticated: false as const,
        response: NextResponse.json(
          { error: 'Sunucu yapılandırma hatası' },
          { status: 500 }
        ),
      }
    }

    const supabase = createServerClient(
      supabaseUrl,
      supabaseAnonKey,
      {
        cookies: {
          getAll() {
            return cookieStore.getAll()
          },
          setAll(cookiesToSet: { name: string; value: string; options: CookieOptions }[]) {
            try {
              cookiesToSet.forEach(({ name, value, options }) =>
                cookieStore.set(name, value, options)
              )
            } catch {
              // Cannot set cookies in read-only context
            }
          },
        },
      }
    )

    // Try cookie-based auth first
    const { data: { user }, error } = await supabase.auth.getUser()

    if (user && !error) {
      return { authenticated: true, userId: user.id, email: user.email }
    }

    // Fallback: try Authorization header (for API clients)
    if (request) {
      if (authHeader?.startsWith('Bearer ')) {
        const token = authHeader.slice(7)
        const { data: { user: tokenUser }, error: tokenError } =
          await getUserFromBearerToken(supabaseUrl, supabaseAnonKey, token)

        if (tokenUser && !tokenError) {
          return { authenticated: true, userId: tokenUser.id, email: tokenUser.email }
        }
      }
    }

    return {
      authenticated: false,
      response: NextResponse.json(
        { error: 'Kimlik doğrulama gerekli. Lütfen giriş yapın.' },
        { status: 401 }
      ),
    }
  } catch {
    return {
      authenticated: false,
      response: NextResponse.json(
        { error: 'Kimlik doğrulama hatası' },
        { status: 401 }
      ),
    }
  }
}