반응형

 

Supabase를 사용하다 보면 Row Level Security(RLS)를 켜놓은 상태에서 UPDATE 쿼리를 실행했는데도 다음과 같은 에러를 만날 수 있습니다:

 

ERROR:  permission denied for table user_profiles

 

“분명히 Policy도 설정했는데 왜 안 되지?” 싶은 분들을 위해, 이 글에서는 이 에러의 근본 원인과 해결 방법을 예제로 함께 정리합니다.

 

문제 상황

 

시도한 쿼리

update public.user_profiles
set name = '테스트'
where id = 'e0804eda-xxxxx-xxxxx-xxxxxx-xxxx';

 

 

발생한 에러 로그

ERROR:  permission denied for table user_profiles

 

이 에러는 단순히 RLS 정책이 없어서가 아닙니다. 오히려 RLS는 잘 설정되어 있었지만, PostgreSQL 권한(GRANT) 자체가 누락되어 있어서 발생하는 것입니다.

 


원인 분석: Supabase의 보안 구조

 

Supabase는 다음 두 단계를 거쳐 요청을 허용합니다:

 

1. PostgreSQL 권한 (GRANT)

역할(Role)이 테이블에 접근할 수 있는지

예: SELECT, INSERT, UPDATE, DELETE 권한을 가졌는가?

 

2. Row Level Security (RLS) 정책

해당 row에 접근 가능한 조건을 만족하는가?

예: 로그인한 사용자의 auth.uid()와 row의 id가 일치하는가?

 

즉, GRANT + RLS 둘 다 통과해야 UPDATE가 작동합니다.

 

 

 


 해결 방법

 

테이블 권한 부여

GRANT UPDATE ON public.user_profiles TO authenticated;

 

필요에 따라 SELECT도 함께 부여합니다:

GRANT SELECT ON public.user_profiles TO authenticated;

 

 

 


RLS 활성화

ALTER TABLE public.user_profiles ENABLE ROW LEVEL SECURITY;

 

RLS 정책 작성

 

로그인한 사용자가 자신의 프로필만 수정 가능하게 만들기:

CREATE POLICY "Users can update their own profile"
ON public.user_profiles
FOR UPDATE
TO authenticated
USING (auth.uid() = id)
WITH CHECK (auth.uid() = id);

 

USING: 해당 row를 조회할 수 있는 조건

WITH CHECK: 해당 row를 수정할 수 있는 조건

 

 


최종 정리

 

설정항목 설명 예시
GRANT 테이블에 대한 접근 권한 부여 GRANT UPDATE ON ... TO authenticated
RLS ENABLE Row-Level Security 사용 설정 ALTER TABLE ... ENABLE ROW LEVEL SECURITY
POLICY 행(row) 단위 조건 설정 USING, WITH CHECK 조건 작성

 

 


결과: 정상 동작

 

위 설정이 완료되면, Supabase Auth를 통해 로그인한 사용자가 자신의 id를 가진 row에 대해 다음과 같은 쿼리를 정상적으로 실행할 수 있습니다.

update public.user_profiles
set name = '테스트'
where id = '로그인한_사용자의_id';

 

 

 

마무리

 

처음엔 "RLS 정책만 설정하면 된다"고 생각하기 쉽지만, Supabase는 PostgreSQL 위에서 작동하는 만큼 기본 권한 설정도 꼭 필요합니다. 앞으로는 permission denied 에러가 나면 다음 두 가지를 먼저 체크해보세요:

1. GRANT 권한이 있는가?

2. RLS Policy가 올바른가?

 

이 구조만 이해하면, Supabase의 보안 모델을 더 자신 있게 다룰 수 있을 거예요.

 

 

# 해피코딩!

반응형

 

Supabase Authentication을 hooking 해서 사용해야 하는 경우가 있습니다.

이때 sql query 함수를 개발하고 테스트를 보통은 cli를 통해서 진행할텐데, cli에 extension이 활성화 안되어있는 경우 검토가 힘들어지겠죠.

 

 

저도 역시 plv8이 활성화 안되어있어서 , 문제였습니다.

 

해결책은 다음 과 같이 찾아서 정리해봤습니다.

 

1. supabase에 기본적으로 extension으로 plv8이 있는 거 같음.

2. 그래서 이를 활성화 시키는 방법을 찾아서 실행.

3. 활성화 된것 확인

 

extension 활성화

CREATE EXTENSION plv8;

 

 

설치 확인

SELECT * FROM pg_available_extensions WHERE name = 'plv8';
 

 

코드 작성...

create or replace function custom_jwt_token_hook(event jsonb)
returns jsonb
language plv8
as $$

  var org_id, role, org_depart_id;

  -- Fetch the current user's level from the profiles table
  var result = plv8.execute("select org_id, role, org_depart_id from public.user_profiles where user_id = $1", [event.user_id]);
  if (result.length > 0) {
    org_id = result[0].org_id;
    org_depart_id = result[0].org_depart_id;
    role = result[0].role;

  } else {
    org_id = 0;
    org_depart_id = 0;
    role = '';
  }

  -- Check if 'claims' exists in the event object; if not, initialize it
  if (!event.claims) {
    event.claims = {};
  }

  -- Update the level in the claims
  event.claims.org_id = org_id;
  event.claims.org_depart_id = org_depart_id;
  event.claims.role = role;

  return event;
$$;

grant all
  on table public.user_profiles
  to supabase_auth_admin;

revoke all
  on table public.user_profiles
  from authenticated, anon, public;

+ Recent posts