why we use Authentication?
- 모든 방문자에게 보여지는 게 아닌 컨텐츠가 있을 때, 즉 보호해야할 컨텐츠(protected contents)가 있을 때 authentication이 필요하다.
- 이 때 컨텐츠란 페이지의 특정 요소라기 보다는, 특정 api의 end point(= 특정 request를 보내고, 그 response를 보여주는 페이지)를 의미하는 경우가 많다.
- ex) 로그인하지 않은 사용자는 비밀번호를 바꾸는 페이지에 접근할 수 없다.
how Authentication works?
- Authentication Steps
- get permission
- 사용자가 로그인을 시도하면, 관련 데이터가 서버에 제출된다.
- 서버는 DB에 접근해서 id/pw 조합을 확인하고, 그 결과를 response로 보낸다.
- 이 때 yes/no의 형태의 response는 위조하기 쉽기 때문에, 더 정교한 response가 필요하다. => server side session이나 auth token을 사용한다.
- send requests to protected resources
- 로그인(인증)에 성공하면, 사용자는 보호된 리소스에 접근할 수 있다.
- 보호된 페이지에 접근하는 것 뿐만 아니라, 인증 권한을 이용해 다른 후속 요청(subsequent request)을 보낼 수도 있다.
- get permission
- 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를 저장하지 않는다.
- 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');
};
//
- 브라우저를 새로고침할 때 마다 js 파일이 재실행되므로, 로그인과 관련된 state를 잃게 된다.
- cookies/ localStorage와 같은 브라우저 저장소를 이용하면 state를 유지할 수 있다.
- idToken이 만료되는 시간(기본값 1시간)이 지나면, 사용자를 logout 상태로 변경해 주어야 한다.
- ??? 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 |