ktseo41 / quotes-sharer

문장 공유
https://ktseo41.github.io/quotes-sharer/
2 stars 0 forks source link

개인 기록 저장 #10

Open ktseo41 opened 2 years ago

ktseo41 commented 1 year ago

인증 과정 진행

image

ktseo41 commented 1 year ago

참고

https://developers.naver.com/docs/login/devguide/devguide.md#1--%EA%B0%9C%EC%9A%94

https://datatracker.ietf.org/doc/html/rfc6749#section-1.2

ktseo41 commented 1 year ago

ChatGPT

OAuth is an open standard for authorization that allows a user to grant a third-party application access to their resources on another service without sharing their credentials. There are several different ways to implement OAuth, depending on the specific requirements of your application and the service you are using.

Here is a general overview of how you might implement OAuth with a client, server, and database:

The client initiates the OAuth flow by redirecting the user to the service's authorization endpoint.

The user interacts with the service to grant permission to the application to access their resources.

The service redirects the user back to the client with an authorization code.

The client sends the authorization code to the server, which sends it to the service's token endpoint.

The service returns an access token and a refresh token to the server.

The server stores the tokens in the database, associating them with the user's account. => 인증 이후, 발급받은 token을 이용해 유저 정보 api를 요구한다. 추가로 네이버에 요청할 건 없어서 이 과정은 필요 없어보인다.

The client uses the access token to make requests to the service on behalf of the user.

The server uses the refresh token to obtain a new access token when the original one expires. => 따라서 위 두 과정도 필요 없다. 대신,

인증이 완료되면 네이버에서 발급해준 unique_id 를 DB 유저 테이블에 저장한다. 동시에 access_token, refresh_token을 발급한다. refresh_token은 DB에 저장한다.

access_token이 만료되면 refresh_token이 DB에 있는지 확인한 후 둘 다 재발급한다.

This is just one way to implement OAuth, and different services might have different implementations or requirements, for example some services may use different grant types or have different endpoints.

It's important to consider the security measures, such as securing tokens and tokens on client side, handling token expiration and refresh, ensure there are no cross-site request forgery (CSRF) attacks,etc. Also, it's important to comply with the regulation and security standards, such as the GDPR, HIPAA.

ktseo41 commented 1 year ago

4-2-3-네이버-로그인을-통한-로그인--로그아웃의-구현

사용자에 대한 인증은 네이버 로그인을 통한 인증으로 대체가 되었기 때문에 비밀번호에 대한 검증이 추가로 필요하지 않습니다. 사용자에 대한 조회가 완료되었을 경우 세션에 로그인 정보를 발행하거나 쿠키로 로그인 정보를 발행하여 로그인 상태로 만들 수 있습니다.

ktseo41 commented 1 year ago

geekstogeeks: how-to-implement-jwt-authentication-in-express-js-app

간단한 구조의 jwt 사용 예시 코드, 설명

로그인

// Handling post request
app.post("/login", async (req, res, next) => {
  let { email, password } = req.body;

  let existingUser;
  try {
    existingUser = await User.findOne({ email: email });
  } catch {
    const error = new Error("Error! Something went wrong.");
    return next(error);
  }
  if (!existingUser || existingUser.password != password) {
    const error = Error("Wrong details please check at once");
    return next(error);
  }
  let token;
  try {
    //Creating jwt token
    token = jwt.sign(
      { userId: existingUser.id, email: existingUser.email },
      "secretkeyappearshere",
      { expiresIn: "1h" }
    );
  } catch (err) {
    console.log(err);
    const error = new Error("Error! Something went wrong.");
    return next(error);
  }

  res
    .status(200)
    .json({
      success: true,
      data: {
        userId: existingUser.id,
        email: existingUser.email,
        token: token,
      },
    });
});

토큰 사용 예시

app.get('/accessResource', (req, res)=>{
  const token = req.headers.authorization.split(' ')[1];
  //Authorization: 'Bearer TOKEN'
  if(!token) {
    res.status(200).json({success:false, message: "Error! Token was not provided."});
  }
  //Decoding the token
  const decodedToken = jwt.verify(token,"secretkeyappearshere" );
  res.status(200).json({success:true, data:{userId:decodedToken.userId,
  email:decodedToken.email});
})
ktseo41 commented 1 year ago

JSON Web Token 소개 및 구조

ktseo41 commented 1 year ago

should-we-store-tokens-in-db

The answer to this question is refresh token rotation, refresh token reuse detection and deleting all old refresh tokens when a new one is generated. Let me try to explain my answer — when a new access token is generated (at the time of sign in/signup or using a refresh token) — a new refresh token should also be generated (this is called refresh token rotation), and all the previous refresh tokens must be deleted.

ktseo41 commented 1 year ago

stackoverflow: Is it secure to store a refresh token in the database?


This is how I would go about it:

5 minute access token as JWT (self-contained, don't need to store it anywhere).

7 day refresh token for one-time usage: generate random secret (don't need to sign it/encrypt it), store it in Redis with a 7 day TTL (or MySQL with a valid_until timestamp). On /refresh_token validate the provided token (check if it's in Redis/MySQL) and delete it. Generate a new access and refresh token pair. (I like to rotate refresh tokens as well, it makes it a bit more secure: it's probably already rotated=invalid if stolen)

ktseo41 commented 1 year ago

bezodker: jwt-refresh-token-node-js-mongodb

Node.js Express Rest API for JWT Refresh Token

// ...
  try {
    let refreshToken = await RefreshToken.findOne({ token: requestToken });

    if (!refreshToken) {
      res.status(403).json({ message: "Refresh token is not in database!" });
      return;
    }

    if (RefreshToken.verifyExpiration(refreshToken)) {
      RefreshToken.findByIdAndRemove(refreshToken._id, { useFindAndModify: false }).exec();

      res.status(403).json({
        message: "Refresh token was expired. Please make a new signin request",
      });
      return;
    }

    let newAccessToken = jwt.sign({ id: refreshToken.user._id }, config.secret, {
      expiresIn: config.jwtExpiration,
    });

    return res.status(200).json({
      accessToken: newAccessToken,
      refreshToken: refreshToken.token,
    });
// ...
ktseo41 commented 1 year ago

OAuth와 JWT 도입하기

image

ktseo41 commented 1 year ago

인증 과정 결론

--- naver access_token, refresh_token은 이제 필요 없음

ktseo41 commented 1 year ago

Velog refresh token jwt

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiNzQzMmM2YzAtOGFiMS0xMWU5LWE3MGUtMTk2NDkxNWI4NjdiIiwidG9rZW5faWQiOiI1NjRhMjcwYS04NTgwLTRhMzAtODExMS0zN2FjYTY3NWM3Y2MiLCJpYXQiOjE2NzM2NzgwMjYsImV4cCI6MTY3NjI3MDAyNiwiaXNzIjoidmVsb2cuaW8iLCJzdWIiOiJyZWZyZXNoX3Rva2VuIn0.d23fALYMp2ASm3q1gpW9duNxKDSb8H-3hcNZGhKQfHY

image

코드 참조

https://github.com/velopert/velog-server

ktseo41 commented 1 year ago

image

ktseo41 commented 1 year ago

localhost에서 쿠키 할당하기 (https://stackoverflow.com/a/1188145/9302758)

정확한 setCookie domain 옵션 필요 (https://stackoverflow.com/a/1188145/9302758)

By design, domain names must have at least two dots; otherwise the browser will consider them invalid. (See reference on http://curl.haxx.se/rfc/cookie_spec.html)

When working on localhost, the cookie domain must be omitted entirely. You should not set it to "" or NULL or FALSE instead of "localhost". It is not enough.

credentials (https://stackoverflow.com/a/70292828/9302758)

결론적인 cookie 설정, request response credential 설정

setCookie

reply
  .setCookie("accessToken", accessToken, {
    path: "/",
    httpOnly: true,
    maxAge: 60 * 60 * 1000,
    domain: "localhost"
  })

credentials

// server
server.register(cors, {
  origin: ["http://localhost:5173"],
  credentials: true,
});
await fetch(`http://localhost:8080/auth?code=${code}`, {
  credentials: "include",
});
ktseo41 commented 1 year ago

cookie signature?

server.register(cookie, {
  secret: "cookie-secret",
  hook: "onRequest",
});

The cookie will still be visible, but it has a signature, so it can detect if the client modified the cookie.

https://stackoverflow.com/a/11898142/9302758

ktseo41 commented 1 year ago

slide 효과

https://codepen.io/aija/pen/xvXWoK