<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>RLS on Kelly Dev</title><link>https://kelly-chui.github.io/tags/rls/</link><description>Recent content in RLS on Kelly Dev</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Thu, 28 Nov 2024 00:00:00 +0000</lastBuildDate><atom:link href="https://kelly-chui.github.io/tags/rls/index.xml" rel="self" type="application/rss+xml"/><item><title>SniffMEET. anon-key와 익명 사용자의 접근 권한 구분</title><link>https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-anon-access-control/</link><pubDate>Thu, 28 Nov 2024 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-anon-access-control/</guid><description>&lt;h2 id="anon-role과-anon-user의-접근-권한-차이"&gt;anon-role과 anon-user의 접근 권한 차이&lt;/h2&gt;
&lt;p&gt;Public Scheme에 있는 테이블을 &lt;code&gt;anon-key&lt;/code&gt;로는 접근할 수 있었지만, &lt;code&gt;anon-user&lt;/code&gt;의 JWT 토큰으로는 접근하지 못하는 문제가 있었다.&lt;br&gt;
처음에는 둘이 비슷한 범주라고 생각했지만, 실제로는 권한 체계와 접근 방식이 달라서 같은 문제로 볼 수 없었다.&lt;/p&gt;
&lt;h3 id="문제를-확인하기-전-생각한-내용"&gt;문제를 확인하기 전 생각한 내용&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;anon-key&lt;/code&gt;, &lt;code&gt;anon-role&lt;/code&gt;, &lt;code&gt;anon-user&lt;/code&gt;의 차이부터 다시 정리했다.&lt;br&gt;
&lt;code&gt;anon-key&lt;/code&gt;를 이용한 접근은 &lt;code&gt;anon-role&lt;/code&gt;로 보고, &lt;code&gt;anon-user&lt;/code&gt;는 이메일 주소나 전화번호를 등록하지 않은 유저를 의미한다. 이 경우 role 자체는 &lt;code&gt;authenticated-role&lt;/code&gt;이었다.&lt;/p&gt;
&lt;p&gt;익명 유저가 접근했을 때 &lt;code&gt;200 OK&lt;/code&gt;가 뜨지만 실제 값을 가져오지 못하는 경우도 있었다.&lt;br&gt;
그래서 접근 자체는 허용되지만 &lt;code&gt;RLS&lt;/code&gt;에서 막히고 있다고 추정할 수 있었다.&lt;/p&gt;</description></item><item><title>SniffMEET. Storage 버킷과 접근 제어</title><link>https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-storage-access-control/</link><pubDate>Sun, 24 Nov 2024 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-storage-access-control/</guid><description>&lt;p&gt;SniffMEET의 프로필 이미지는 사용자 정보와 성격이 다르다. 사용자 이름이나 소개처럼 구조화된 데이터는 Database에 저장하고, 이미지 파일은 Storage에 저장하는 편이 적합하다.&lt;/p&gt;
&lt;p&gt;Storage의 Postgres 테이블에는 파일 자체가 아니라 버킷과 객체의 메타데이터가 기록된다.&lt;/p&gt;
&lt;h2 id="버킷과-객체-경로"&gt;버킷과 객체 경로&lt;/h2&gt;
&lt;p&gt;버킷은 파일을 묶는 최상위 컨테이너다. 사용자마다 버킷을 만드는 것보다 용도별 버킷을 만들고 객체 경로로 사용자를 구분하는 편이 관리하기 쉽다.&lt;/p&gt;
&lt;div class="code-theme-github"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;profile-images/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── {user-id}/profile.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── {user-id}/thumbnail.jpg&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;이 구조에서는 &lt;code&gt;profile-images&lt;/code&gt; 버킷이 공통된 공개 범위와 파일 제한을 담당하고, 사용자 ID가 포함된 경로를 RLS 정책에서 검사할 수 있다.&lt;/p&gt;</description></item><item><title>SniffMEET. JWT와 Supabase RLS 정책에 연결하기</title><link>https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-jwt-rls/</link><pubDate>Wed, 20 Nov 2024 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-jwt-rls/</guid><description>&lt;p&gt;Supabase Auth로 로그인하면 클라이언트는 Access Token으로 JWT를 받는다. Supabase는 요청에 포함된 JWT를 검증하고, 그 안의 사용자 정보와 역할을 RLS 정책에 전달한다.&lt;/p&gt;
&lt;pre class="mermaid" data-mermaid-source="flowchart&amp;#43;LR%0A&amp;#43;&amp;#43;&amp;#43;&amp;#43;Client%5B%22%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8%22%5D&amp;#43;--%3E%7C%22JWT%EB%A5%BC&amp;#43;%ED%8F%AC%ED%95%A8%ED%95%9C&amp;#43;%EC%9A%94%EC%B2%AD%22%7C&amp;#43;Supabase%5B%22Supabase&amp;#43;API%22%5D%0A&amp;#43;&amp;#43;&amp;#43;&amp;#43;Supabase&amp;#43;--%3E%7C%22JWT&amp;#43;%EA%B2%80%EC%A6%9D%22%7C&amp;#43;Claims%5B%22sub%2C&amp;#43;role&amp;#43;%EB%93%B1%EC%9D%98&amp;#43;%ED%81%B4%EB%A0%88%EC%9E%84%22%5D%0A&amp;#43;&amp;#43;&amp;#43;&amp;#43;Claims&amp;#43;--%3E&amp;#43;RLS%5B%22PostgreSQL&amp;#43;RLS&amp;#43;%EC%A0%95%EC%B1%85%22%5D%0A&amp;#43;&amp;#43;&amp;#43;&amp;#43;RLS&amp;#43;--%3E&amp;#43;Database%5B%22%ED%97%88%EC%9A%A9%EB%90%9C&amp;#43;%ED%96%89%EB%A7%8C&amp;#43;%EC%A1%B0%ED%9A%8C&amp;#43;%EB%98%90%EB%8A%94&amp;#43;%EB%B3%80%EA%B2%BD%22%5D"&gt;&lt;/pre&gt;&lt;h2 id="jwt의-클레임"&gt;JWT의 클레임&lt;/h2&gt;
&lt;p&gt;JWT는 헤더, 페이로드, 서명으로 구성된다. 각 부분은 Base64URL로 인코딩되지만 암호화되지는 않으므로 페이로드에 비밀 정보를 넣으면 안 된다. 서명은 헤더와 페이로드가 변조되지 않았는지 검증하는 데 사용한다.&lt;/p&gt;
&lt;p&gt;Supabase의 JWT에는 다음과 같은 클레임이 포함된다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sub&lt;/code&gt;: 사용자의 고유 ID&lt;/li&gt;
&lt;li&gt;&lt;code&gt;role&lt;/code&gt;: RLS를 적용할 PostgreSQL 역할&lt;/li&gt;
&lt;li&gt;&lt;code&gt;iat&lt;/code&gt;: 토큰이 발급된 시각&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exp&lt;/code&gt;: 토큰이 만료되는 시각&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="supabase에서-jwt-사용하기"&gt;Supabase에서 JWT 사용하기&lt;/h2&gt;
&lt;p&gt;RLS는 PostgreSQL이 행 단위로 접근을 제한하는 기능이다. Supabase에서는 &lt;code&gt;auth.uid()&lt;/code&gt;로 현재 JWT의 &lt;code&gt;sub&lt;/code&gt; 값을 가져와 행의 사용자 ID와 비교할 수 있다.&lt;/p&gt;</description></item><item><title>SniffMEET. RLS와 테이블 권한</title><link>https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-rls-empty-response/</link><pubDate>Wed, 20 Nov 2024 00:00:00 +0000</pubDate><guid>https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-rls-empty-response/</guid><description>&lt;h2 id="문제"&gt;문제&lt;/h2&gt;
&lt;p&gt;Supabase에서 anon key로 Public 테이블에 접근하면 데이터가 보이는데, 익명 로그인 후 받은 JWT access token으로 접근하면 &lt;code&gt;200 OK&lt;/code&gt;만 오고 데이터가 비어 있었다.&lt;/p&gt;
&lt;img loading="lazy" src="https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-rls-empty-response/image-003-optimized-image.webp"&gt;&lt;p&gt;처음에는 authenticated role이 anon role보다 상위 권한이라고 생각해서 더 헷갈렸다. 하지만 Supabase에서는 role의 상하관계보다 RLS 정책을 어떻게 작성했는지가 더 중요했다.&lt;/p&gt;
&lt;h2 id="확인"&gt;확인&lt;/h2&gt;
&lt;img loading="lazy" src="https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-rls-empty-response/image-002-optimized-image.webp"&gt;&lt;p&gt;JWT가 만료된 경우에는 &lt;code&gt;401 Unauthorized&lt;/code&gt;가 발생했다. 반면 RLS 조건에 맞지 않는 경우에는 요청 자체는 성공해서 &lt;code&gt;200 OK&lt;/code&gt;가 오지만, row가 필터링되어 데이터가 내려오지 않았다.&lt;/p&gt;
&lt;img loading="lazy" src="https://kelly-chui.github.io/posts/sniffmeet-dev-log-supabase-rls-empty-response/image-004-optimized-image.webp"&gt;&lt;p&gt;authenticated 유저가 읽을 수 있도록 다음 policy를 추가하자 데이터를 읽을 수 있었다.&lt;/p&gt;</description></item></channel></rss>