본문 바로가기

Study/Web Dev Basic

Authentication

why we use Authentication? 

  • 모든 방문자에게 보여지는 게 아닌 컨텐츠가 있을 때, 즉 보호해야할 컨텐츠(protected contents)가 있을 때 authentication이 필요하다. 
    1. 이 때 컨텐츠란 페이지의 특정 요소라기 보다는, 특정 api의 end point(= 특정 request를 보내고, 그 response를 보여주는 페이지)를 의미하는 경우가 많다. 
    2. ex) 로그인하지 않은 사용자는 비밀번호를 바꾸는 페이지에 접근할 수 없다. 

how Authentication works? 

  1. Authentication Steps
    • get permission
      • 사용자가 로그인을 시도하면, 관련 데이터가 서버에 제출된다. 
      • 서버는 DB에 접근해서 id/pw 조합을 확인하고, 그 결과를 response로 보낸다.
      •  이 때 yes/no의 형태의 response는 위조하기 쉽기 때문에, 더 정교한 response가 필요하다. => server side session이나 auth token을 사용한다. 
    • send requests to protected resources
      • 로그인(인증)에 성공하면, 사용자는 보호된 리소스에 접근할 수 있다.
      • 보호된 페이지에 접근하는 것 뿐만 아니라, 인증 권한을 이용해 다른 후속 요청(subsequent request)을 보낼 수도 있다.
  2. Server side session | 가장 오랫동안(traditional) 사용됨
    • 서버는 사용자에게 접근 권한을 주면서, 그 사용자에 대한 unique id를 저장한다. 즉, 모든 인증된 사용자에 대한 id가 서버에 저장된다.
    • 이 때 id는 서버에서 클라이언트 측으로 전달된다. => id는 yes/no 형태의 단순한 응답이 아니기 때문에, 위조하기 어렵다. 
    • 서버와 클라이언트 측 모두 id를 알고있다. => 클라이언트는 protected request에 해당 id를 접목(attach)하고, 서버는 그 id가 유효한지, 아닌지를 확인할 수 있다. 
    • SPA와 같이 백엔드와 프론드엔드의 coupling이 느슨한 경우에는 사용하기 어렵다. 
      • ex) 리액트로 만든 프론트엔드 어플리케이션은 서버 A에서, 이 어플리케이션에서 사용하는 백엔드 APIs는 서버 B에서 제공하고 있는 경우 coupling이 느슨하다 => 이런 경우에는 서버에 id를 저장하지 않는다. 
  3. Authentication tokens | SPA에서 주로 사용  
    • server side session과 공통점 | 사용자가 로그인을 시도하면, 서버 측에서 관련 데이터를 DB에서 확인한 뒤 허가를 내어줄 것인인지, 말 것인지를 결정한다.
    • server side session과 차이점
      • 인증된 사용자의 "permisson token"을 생성한다. 
      • permission token은 특정 알고리즘을 이용해서 사용자의 데이터를 암호화(encode)한 string을 말한다. encoded string은 다시 복호화(decode)할 수 있다. 
      • token을 생성할 때 사용하는 key는 서버 측에서만 알고 있다(서버 측에만 저장되고, 클라이언트에게 전송되지 않는다).
      • 반면 token 그 자체는 서버에 의해 생성되기만 하고, 저장되지는 않는다. 대신, token을 클라이언트 측에 response 형태로 전송한다. 
      • 클라이언트가 protected resourece를 요청할 때 그 token을 사용하면, 서버는 해당 token이 자신(key)에 의해 생성된 값인지를 확인한다. 
      • 즉 token을 서버에 직접 저장하지 않더라도, key를 이용해 해당 token이 유효한 값인지 아닌지를 확인할 수 있다. 

+) how to protect front end page? | navigation gaurd

로그인 상태를 이용해서 route를 설정함으로써 프론트엔드 페이지를 보호할 수 있다. 아래 리액트 어플리케이션의 예제를 통해 살펴보자.

 

function App() {
  const { isLoggedIn } = useContext(AuthContext);
  
  return (
    <Layout>
      <Switch>
        <Route path='/auth'>
          <AuthPage />
        </Route>
        {isLoggedIn && (
          <Route path='/profile'>
            <UserProfile />
          </Route>
        )}
        <Route path='*'>
          <p>page not found!</p>
        </Route>
      </Switch>
    </Layout>
  );
}

+) keeping login state & auto logout 

// AuthContextProvider
const initialToken = localStorage.getItem('token');
const [token, setToken] = useState(initialToken);
const isLoggedIn = !!token; 

/* or
useEffect(() => {
  setToken(localStorage.getItem('token'));
}, []);
*/

const loginHandler = (token) => {
  setToken(token);
  localStorage.setItem('token', token); 
};

const logoutHandler = () => {
  setToken(null);
  localStorage.removeItem('token'); 
};

//

 

  1. 브라우저를 새로고침할 때 마다 js 파일이 재실행되므로, 로그인과 관련된 state를 잃게 된다.
  2. cookies/ localStorage와 같은 브라우저 저장소를 이용하면 state를 유지할 수 있다. 
  3. idToken이 만료되는 시간(기본값 1시간)이 지나면, 사용자를 logout 상태로 변경해 주어야 한다. 
  4. ??? expiresIn은 토큰 만료되기 까지 얼마 남았는지 알려주는 건가?? 아님 항상 기본 값을 알려주는 건가
    •  => 항상 기본값을 알려주기 때문에, 로그인 handler에 남은 시간을 계산하고 업데이트하는 로직이 필요

 

// AuthContext
let logoutTimer; 

const remainigAuthTime = (expireTime) => {
  const loginTime = new Date().getTime(); // 현재 시각을 ms 단위로 반환
  const autoLogoutTime = new Date(expireTime).getTime();
  const remainigTime = autoLogoutTime - loginTime; 
  return remainigTime; 
}

// AuthContextProvider
const logoutHandler = () => {
  setToken(null);
  localStorage.removeItem('token'); 
  logoutTimer && clearTimeout(logoutTimer); 
};

const loginHandler = (token, expireTime) => {
  setToken(token);
  localStorage.setItem('token', token); 
  const remaining = remainigAuthTime(expireTime); 
  
  logoutTimer = setTimeout(logoutHandler, remaining); 
  // (+data.expiresIn) * 1000을 이용
};

 

// expireTime
const transformData = (data) => {
  //  +data.expiresIn
  const expireTime = 
  
  authCtx.loginHandler(data.idToken, 3000);
  history.replace('/');
};

const signInConfiguration = signInConfig(email, pw); 

sendReq(signInConfiguration, transformData);

 

'Study > Web Dev Basic' 카테고리의 다른 글

what is MVC pattern?  (0) 2022.09.07
MPA vs. SPA  (0) 2022.07.01
What is LocalStorage?  (0) 2022.06.19
What is Database?  (0) 2022.06.04
What is REST?  (0) 2022.06.04