Supabase 트러블 슈팅 - Team-HGD/SniffMEET GitHub Wiki

익명 로그인 생성, 복원, 갱신 로직 구현

  • JWT, Refresh 토큰과 세션의 개념이 잘 정립되지 않은 상태
  • 특히 익명 계정을 생성은 했는데, 이 생성한 계정을 어떻게 쓰는가? 에 대한 해결 과정이 필요했다.

문제 해결 전 생각한 내용

  • SDK를 뜯어보니 토큰과 현재 유저 정보를 저장하고 있는 것을 SupabaseSession이라고 하는 것 같다.
  • 생성, 복원, 갱신 로직이 굉장히 밀접하게 연관되어 있고 특히 복원과 갱신 로직은 파고들수록 비슷해진다.
  • 앱을 다운받고 처음 실행했을 때와, 앱을 다시 실행했을 때의 동작 차이를 확실하게 구분할 수 있어야 한다.
    • 익명 로그인을 했으니, 키체인에 저장된 세션의 유무로 파악할 수 있지 않을까?

실제 문제 해결 과정

  1. 일단 시점에 따른 Supabase Session의 상태를 표로 시각화했다.
  최초로 앱 실행 앱 다시 실행 실행 중
라이브 세션 X X O
유저 정보 X X O
키체인 저장 토큰 X O O
  1. 키체인에 토큰이 저장되어 있는지 여부로 앱이 실행될 때, 세션 복원을 해야하는지 아니면 익명 로그인을 해야하는지 알 수 있다.
  2. 처음에 복원을 시도하고 실패하면 익명 로그인, 복원이 성공하면 그대로 복원하고 세션 객체를 생성하면 된다.

갱신 로직 생성

  1. 현재 세션에서 Refresh Token을 가져온다.
  2. Refresh Token으로 네트워크에 JWT 토큰갱신을 요청한다.
  3. 새로 받아온 토큰으로 현재 토큰을 업데이트 한다.

복원 로직 생성

  1. 키체인에서 토큰을 가져와서 세션을 업데이트 한다. (만료 여부랑 상관없이)
  2. 토큰이 만료되었는지 확인한다. 토큰 만료 시점을 가지고 있으므로 서버와 연결 없이 바로 확인할 수 있다.
    1. 만료된 토큰이라면 갱신을 요청한다.
    2. 만료되지 않은 토큰이라면 서버에 유저 정보를 요청한다. (키체인에는 유저 정보가 없으므로)
  3. 새로 받아온 토큰, 유저 정보로 세션 객체를 생성한다.

해결 과정 중 추가적으로 알게 된 부분.

이미지1

유저 정보만 따로 서버에 요청할 수는 있지만, 실제로 갱신을 수행했을 때 토큰 뿐만 아니라 세션 전체 내용이 응답으로 오게 되는 것을 알게 됨.

수정된 복원 로직

  1. 키체인에서 토큰을 가져와서 세션을 업데이트한다. (만료 여부랑 상관없이)
  2. (만료 여부랑 상관없이) 바로 세션 갱신을 요청하여 서버에서 세션을 받아와서 세션 객체를 생성한다.

anon-role과 anon-user의 접근 권한 차이

  • Public Scheme에 있는 테이블을 anon-key로는 접근 가능하나, anon-user(익명 유저)의 JWT 토큰으로는 접근하지 못하는 문제 발생
  • anon-key는 공개키라서 인터넷에 공개해도 되는 키고, anon-user의 JWT 토큰은 사용자 인증 정보가 담겨있어서 보안이 필요한 키임

문제 해결 전 생각한 내용

  • anon-key, anon-role, anon-user의 차이
    1. anon-key를 이용한 접근을 anon-role이라고 하고,
    2. anon-user는 이메일 주소, 전화번호 등을 등록하지 않은 유저를 말함. role 자체는 authenticated-role이다.
  • 익명 유저가 접근했을 때 200 OK가 뜨지만, 실제론 아무런 값을 가져오지 못함 → 접근 권한 자체는 존재하지만 RLS에서 막힌 것을 유추할 수 있음
  • anon-roleauthenticated-role은 상하 관계가 아니고 서로 독립된 롤이다.

실제 문제 해결 과정

  1. SQL 쿼리를 통해서 RLS 설정 → authenticated-role인 유저만 접근할 수 있는 테이블로
CREATE policy "Authenticated users can read"
ON "public"."hgdtest"
TO public
USING (
  (auth.role() = 'authenticated'::text)
);

이미지1

RLS를 설정한 이후에는 테이블의 데이터를 잘 받아올 수 있었다.

authenticated-roleanon-role을 포함하지 않는 사실을 알 수 있었다.

→ RLS에 걸린 것은 응답에 실패한것으로 처리되지 않는다.

추가적으로,

아무런 RLS 정책이 설정되어 있지 않으면 기본적으로 모든 접근에 배타적으로 된다. 즉 Public 테이블이더라도 RLS가 켜져있는 상태에서 아무런 정책이 없다면, 사실상 Private 테이블이 된다는 것을 알게 됨.

해결 과정중 추가적으로 알게 된 부분

  • 다른 작업을 하고 다시 확인을 해 보니 JWT 토큰의 수명이 매우 짧다는 것을 알게 됨. 현재 서버에서 지정된 시간은 3600초(1시간)임을 확인가능

    JWT만료.png

  • 그래도 테이블 자체의 접근 권한을 설정하는 것은 RLS보다는 DCL을 쓰는게 낫다.

    • role 단위 지정이 가능함.
    • RLS에 걸리는 것은 응답 코드가 200 OK가 되고 데이터를 안 불러오는 방식이 되지만, DCL로 지정한 접근 권한에 걸리는 것은 401 Unauthorized에러가 뜸.
GRANT SELECT, INSERT, UPDATE, DELETE ON public.mate_list TO "Authenticated-role";

추가적으로 넣을만한 내용?

  • 저희도 서버 분리했어요… SniffMEET-Test랑 SniffMEET으로…
  • (저희 프로젝트 세션엔 없지만 실제 Supabase)세션에 포함되어있는 user_metadata도 처음에 많이 고려했었던 친구인데요. 쓰지 않게된 이유는 아래와 같습니다!
    • 일단 장점으로는 테이블이 아닌 바로 json 파일로 저장되어서 매우 불러오기 편함
    • 가장 큰 단점으로는 다른 유저가 접근을 못함. 내 정보는 내가 이미 로컬에도 가지고 있는데…
  • 그리고 혜민님이 Storage 관련 한 것도 넣을만한거 있을텐데 이미 하셨겠죠? 버켓 구조같은거 고민했던거…

⚠️ **GitHub.com Fallback** ⚠️