react native- user authentication flow

·2020년 11월 5일
2

좌충우돌

목록 보기
22/26

이 글은 https://medium.com/@rossbulat/react-native-user-authentication-flow-explained-d988905ba106 를 한국어로 해석&요약한 글입니다.

React Native Authentication Flow

Authentication Tokens

App authentication은 random한 식별자(identifier)로 built되는데, 이때 이 identifier를 authToken이라고 부른다.
authToken만 가지게 된다면 app은 연속적인 authenticated API requests를 만들 수 있다.
이때 authToken은 server side에서 random hash의 형태로 만들어질 수 있는데, 만드는 방법 중 가장 각광받는 방법은 바로 아래와 같이 built-in NodeJS crypto package를 쓰는 것이다.

이렇게 만들어진 authToken은 암호화된 response body에 넣어져 디바이스에 전달이 된다.

만약 authToken이 이미 디바이스에 저장된 상태라면, valid한 authToken인 지를 확인하면 된다(아래 캡쳐에서의 프로세스 참고).

splash page(앱 로고 스크린)은 initial authentication check를 위한 아주 좋은 page다. 여기서 initial authentication check에 fail하면 SignIn 스크린으로 가면 되고, success하면 Home 스크린으로 navigate하면 되기 때문이다.

하지만 위 캡쳐에서의 authentication flow에는 문제가 있는데, 디바이스가 인터넷에 연결되어 있지 않으면 authToken을 validate할 수 없기 때문이다. 오프라인의 경우에 대해 고려를 해 보자.

Authentication Metadata for offline usage

Authentication token은 다른 추가적인 기능을 가지는 metadata와 쉽게 combined된다. 다음은 metadata의 유용한 pieces들이다.

  • timestamp 기능: user가 마지막으로 authenticated된 게 언제인 지(+ authToken이 언제 만들어졌는 지)를 알려줌
  • 다른 random한 암호화되고 데미지가 없는 identifier: 디바이스가 authenticating하는 지를 결정하기 위해.

Authentication TimeStamp

로그인으로 return되는 authToken과 함께 주어지는 timestamp는 앱이 authToken이 어느 정도 기간이 지났는 지를 알려준다. 여기서 만약 30일을 possible offline usage period로 잡는다면, 30일이 아직 지나지 않은 시점에서는 authToken을 validate하려는 것을 막거나 30일이 되는 시점에서는 자동으로 renew를 해준다.

가장 이상적인 건, authtoken의 validity(유효성)을 기간이 만료되기 전(직전 예시에서와 같이 기간=30일이라면, 약 24~30일즈음)에 authToken의 validity를 check하는 것이다. 물론, 디바이스가 인터넷에 연결되었을 때 authtoken의 validity를 검증해야 한다. 이런 방법은 어느 정도 보안성을 지켜주고, 앱 사용 만족도를 높여준다(UX 경험 ⬆️).

Device ID to uniquely identify a sign-in device

앞서 말한 다른 random한 암호화되고 데미지가 없는 identifier는 바로 디바이스의 uuid다.
'디바이스의 uuid'라는 정보는 로그인 시도에서 그 시도가 서버를 해킹하려는(아무튼 불순한) 해커의 시도인 지, 아니면 어떤 디바이스의 단순 비밀번호를 까먹어서 행하는 여러번의 시도인 지를 알아챌 수 있게 한다.

ip address data를 물론 가져올 수는 있겠지만, 이는 사용자가 vpn이나 wifi access points마다 달라질 수 있기 때문에 uuid로 이 역할을 하게 하면서 유저의 아이디로 다른 디바이스에서 로그인 시도가 있었는 지를 알린다.

deviceId는 React Native의 uuid package로 another secure random ID를 만드는 데 사용된다. 다름은 deviceId를 생성하는 코드다.

deviceId는 app install 직후(app initialisation)에 만들어지고 서버에 저장되어 authentication request를 할 때마다 서버에 전달된다. 만약 authentication이 성공한다면:

  • deviceId는 백엔드 데이터베이스에 저장되어 추후의 authToken validations에서 해당 기기가 맞는 기기인 지 실험할 수 있다.
  • deviceId는 또한 앱 내에(in-app) 저장되어 추가적인 authToken validations에서 referenced될 수 있다.

그래서 authentication flow를 다음과 같이 수정할 수 있다.

authToken, timestamp, 그리고 deviceId는 모두 디바이스 내에 저장되어야 한다. 가장 단순한 방법은 React Native의 AsyncStorage API를 사용하는 것이다.

Persisting Authentication Data with AsyncStorage

위 캡쳐와 같이 react native에는 async storage API이 있는데, 본 글에서 다룬 주제인 authentication needs를 위한 basic functions를 저렇게 짤 수 있다.
(async storage 관련해서는 작성자가 공부를 했었기 때문에 요약의 요약만 달아둠)

Retrieving authentication data to populate user settings

앱의 splash screen이 보일 때, authToken이 storage에 존재하는 지부터 확인한다.
데이터가 존재한다면, 앱의 Home Screen으로 바로 naviagate하거나 더 많은 정보를 위해 server에 request를 던진다.

(예시 코드가 작성되어 있었지만, 해당 코드는 arrow function이나 hook을 쓰지 않았기 때문에 코드에 대한 설명과 함께 생략하겠다)

Signing in and persisting authentication state

(마찬가지로 생략, 해당 코드에 대한 해석은 추후에 덧붙이던가 하겠다)

A note on server side authentication

server side에서는 bcrypt package를 이용하여 민감한 credentials를 해싱하는 게 가장 널리 쓰이는 방법이다.

User가 새로운 계정을 만들었을 때(회원가입), bcrypt는 credentials를 아래와 같이 hash 처리해서 db에 저장해둘 수 있다.

// 코드 출처: https://medium.com/@rossbulat/react-native-user-authentication-flow-explained-d988905ba106
// hashing a password with bcrypt
const bcrypt = require('bcrypt');
const saltRounds = 10;

const hashedPw = await bcrypt.hash(password, saltRounds);

따라서 연속적인 sign-in request에 대해 password를 validate할 때는 stored hash password와 비교한다.

// 코드 출처: https://medium.com/@rossbulat/react-native-user-authentication-flow-explained-d988905ba106
// validating a password
const passwordValid = await bcrypt.compare(req.body.password, hashedPassword);

추가적인 bcypt에 대한 정보는 Github page를 참조.

Other considerations

authToken과 timestamp는 영원히 지속되면 안된다. 앱의 보안을 위해 다음 두가지를 참고해라.

  • 어떤 deviceid가 authToken을 validate하는 데 fail할 경우, authToken과 timestamp에 관한 정보를 서버가 삭제하도록 조치한다. 이 행동은 inactive user일 가능성이 높은데, 앱 보안을 위해서는 그들이 다시 로그인을 하도록 하는 것이 좋다.

  • 한 사용자가 로그인할 수 있는 디바이스의 개수에 한도를 둔다.

  • 사용자가 비밀번호를 재설정했을 때 authToken을 refresh하고 해당 이벤트에 대해 기록을 한다. 해당 유저의 모든 authenticated된 deviceId에 이를 적용한다.

결론적으로, 이런 보안을 지키기 위한 노력들은 UX에 아주 작은 영향을 미치지만 보안성은 올라간다(좋다는 말인 듯).

profile
이것저것 개발하는 것 좋아하지만 서버 개발이 제일 좋더라구요..

0개의 댓글